From patchwork Thu Sep 19 14:25:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 1164705 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=silabs.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="imosFAod"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 46Yzhw6d3Gz9sQx for ; Fri, 20 Sep 2019 00:28:00 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389476AbfISO2A (ORCPT ); Thu, 19 Sep 2019 10:28:00 -0400 Received: from mail-eopbgr780084.outbound.protection.outlook.com ([40.107.78.84]:48384 "EHLO NAM03-BY2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1732402AbfISOZr (ORCPT ); Thu, 19 Sep 2019 10:25:47 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=m75nGJwvkupXrRk457jAkcNEs0pc2EYGKyZNBz4ieNuJ/Uq/5w9d2KEaXORXiiPlWzy8jVmfyVNoCTROlzbLT94Z+SllvqnmhySejZCrsJSVOM1OWdUsjTYwkMhoFPvzC87cXc3nOib1FVs+PluWIwjeCsWyVkJ2nWeAICeM49cw3TgxF3qlxUDikp1kGbOh9t7hY5eXheRBzAzqcNl//A39lwBlLIlQIPELVT7o6Kdp0Txc9GmBdr7u/v1wpvJPBeE0J69zFighdROTwAUU55zQFTiFA1VntVSRdmv92g/2aa9LQuNYIhu1PlhLRotnCeum3aBbl0n3VS5bfVTQ+Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=uAlOZLlwivXTdC1BS0GmtLZFhoGma3ZuLCQ+0oU0lz4=; b=Yyyze6EP4S8MsLiGMAO7xsop1ZlqyQgtoSCUocDRHtIEfGOlMkeBASBUbYUptJrKXzuPaP1ZkailpfMPtUi4Djz/2SS7jkOizzFiznV5fjWbI6qjjinYP8KcJubBjB3F1dKxLHNV+/DQiALqVh9OhIxvntTtCIh2ZaqMMe881cP6lJWfGInk1qzmfQ+U1Lwhczt2NecuIxXoON53BceHD/qO7BDP1aHne1iBdP72qVqBUzII7nY2VWYhqdemIi8gsONS92ggCd807NJnlympUuUpLZbdiR/f4HD9ZUbzUR3oV04GsYIyCjlYnNa6tIZFiJDL87LygN6Hg8ZRwOVSAA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=uAlOZLlwivXTdC1BS0GmtLZFhoGma3ZuLCQ+0oU0lz4=; b=imosFAodn67w0G/nx+l+grxmgJyzGH6pX1Su3C3IijcFHCarFStnnYGj33ksoXYJe4pxnymwWy9+8xZMz2S1+lNKNO1dT0Mov8jELHWjei1vFqz+1ibyDPE/Yztzumhs5e7K8csNIiCz3BKBzFE/UTa/tE3Ak9fT+gMb+sAffwA= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4207.namprd11.prod.outlook.com (52.135.37.97) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.24; Thu, 19 Sep 2019 14:25:37 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 14:25:37 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH v3 02/20] staging: wfx: add support for I/O access Thread-Topic: [PATCH v3 02/20] staging: wfx: add support for I/O access Thread-Index: AQHVbvYb16/QcWKXu0SSSrbujYAaNA== Date: Thu, 19 Sep 2019 14:25:37 +0000 Message-ID: <20190919142527.31797-3-Jerome.Pouiller@silabs.com> References: <20190919142527.31797-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919142527.31797-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 8f443c30-6795-45f5-293b-08d73d0d3dcc x-microsoft-antispam: BCL:0; PCL:0; RULEID:(2390118)(7020095)(4652040)(8989299)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(5600167)(711020)(4605104)(1401327)(2017052603328)(7193020); SRVR:MN2PR11MB4207; x-ms-traffictypediagnostic: MN2PR11MB4207: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:580; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM; SFS:(10009020)(346002)(376002)(39850400004)(366004)(396003)(136003)(199004)(189003)(2501003)(71200400001)(71190400001)(99286004)(4326008)(478600001)(25786009)(6512007)(107886003)(3846002)(6116002)(14444005)(14454004)(256004)(30864003)(6486002)(2906002)(1076003)(86362001)(6436002)(66574012)(2616005)(476003)(26005)(316002)(446003)(36756003)(8676002)(81166006)(81156014)(11346002)(54906003)(110136005)(8936002)(102836004)(186003)(5660300002)(7736002)(305945005)(91956017)(76176011)(66066001)(66946007)(6506007)(486006)(76116006)(66476007)(66446008)(64756008)(66556008); DIR:OUT; SFP:1101; SCL:1; SRVR:MN2PR11MB4207; H:MN2PR11MB4063.namprd11.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: vVhtgO1LK9ra2pyt5znAOiDcsHEbZSGrgOwuOCD3Er5Zl13sYaul6xvaW5vgFbnBVwUcgL/CaadywACcRZS4b8IKMO+JiHhw+f/s+RKAMkvHEQ6JMLNgu/wQq+HnL90QyXYOZ5KOze50ApCXfInhIzw6U033L66Phync7Pe85Rr2cphaCXPS3WwCCCdviJPgosvxm03iokRfPrbV9XsydOEclII6qCSSXhHzJHS1Esl0QCrLr2TTGm+7l5FSRKNeidV6roqeatz6H1OpIfipC0K7JYn4gMsQLvx9/vIWFbOpicY26ehtyvYk5DI4So0MYAxLfpGeJH7c0jCKdz7FharPZN/rfIviMnST/FMT9z0CAlIcwuJhekvGyIxeUuoaGSCdQvXrZ02nrfq4xemD8BMtOfJJP9HlRT4LQ7KgwfA= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8f443c30-6795-45f5-293b-08d73d0d3dcc X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 14:25:37.2836 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: WqVEh5XLB3APLh3Sd3EbrndYSQY0DkaOGB5277DLYEV8DeeLKuNQCq+kG8P7gQCdR4bFATwVk6gtHphyJRYxQQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4207 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Jérôme Pouiller Introduce bus level communication layer. At this level, 7 registers can be addressed. Notice that SPI driver is able to manage chip reset. SDIO mode relies on an external driver (`mmc-pwrseq`) to reset chip. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/bus.h | 17 +++ drivers/staging/wfx/bus_sdio.c | 189 ++++++++++++++++++++++++++++++- drivers/staging/wfx/bus_spi.c | 200 ++++++++++++++++++++++++++++++++- drivers/staging/wfx/hwio.h | 48 ++++++++ drivers/staging/wfx/main.c | 53 +++++++++ drivers/staging/wfx/main.h | 32 ++++++ drivers/staging/wfx/wfx.h | 24 ++++ 7 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/wfx/hwio.h create mode 100644 drivers/staging/wfx/main.h create mode 100644 drivers/staging/wfx/wfx.h diff --git a/drivers/staging/wfx/bus.h b/drivers/staging/wfx/bus.h index 8ce871a8a9ff..eb77abc09ec2 100644 --- a/drivers/staging/wfx/bus.h +++ b/drivers/staging/wfx/bus.h @@ -11,6 +11,23 @@ #include #include +#define WFX_REG_CONFIG 0x0 +#define WFX_REG_CONTROL 0x1 +#define WFX_REG_IN_OUT_QUEUE 0x2 +#define WFX_REG_AHB_DPORT 0x3 +#define WFX_REG_BASE_ADDR 0x4 +#define WFX_REG_SRAM_DPORT 0x5 +#define WFX_REG_SET_GEN_R_W 0x6 +#define WFX_REG_FRAME_OUT 0x7 + +struct hwbus_ops { + int (*copy_from_io)(void *bus_priv, unsigned int addr, void *dst, size_t count); + int (*copy_to_io)(void *bus_priv, unsigned int addr, const void *src, size_t count); + void (*lock)(void *bus_priv); + void (*unlock)(void *bus_priv); + size_t (*align_size)(void *bus_priv, size_t size); +}; + extern struct sdio_driver wfx_sdio_driver; extern struct spi_driver wfx_spi_driver; diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index 4b26c994f43c..35bcca7ec5dc 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -8,36 +8,223 @@ #include #include #include +#include #include #include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" + +static const struct wfx_platform_data wfx_sdio_pdata = { +}; + +struct wfx_sdio_priv { + struct sdio_func *func; + struct wfx_dev *core; + u8 buf_id_tx; + u8 buf_id_rx; + int of_irq; +}; + +static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id, + void *dst, size_t count) +{ + struct wfx_sdio_priv *bus = priv; + unsigned int sdio_addr = reg_id << 2; + int ret; + + BUG_ON(reg_id > 7); + WARN(((uintptr_t) dst) & 3, "unaligned buffer size"); + WARN(count & 3, "unaligned buffer address"); + + /* Use queue mode buffers */ + if (reg_id == WFX_REG_IN_OUT_QUEUE) + sdio_addr |= (bus->buf_id_rx + 1) << 7; + ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count); + if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE) + bus->buf_id_rx = (bus->buf_id_rx + 1) % 4; + + return ret; +} + +static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id, + const void *src, size_t count) +{ + struct wfx_sdio_priv *bus = priv; + unsigned int sdio_addr = reg_id << 2; + int ret; + + BUG_ON(reg_id > 7); + WARN(((uintptr_t) src) & 3, "unaligned buffer size"); + WARN(count & 3, "unaligned buffer address"); + + /* Use queue mode buffers */ + if (reg_id == WFX_REG_IN_OUT_QUEUE) + sdio_addr |= bus->buf_id_tx << 7; + // FIXME: discards 'const' qualifier for src + ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *) src, count); + if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE) + bus->buf_id_tx = (bus->buf_id_tx + 1) % 32; + + return ret; +} + +static void wfx_sdio_lock(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_claim_host(bus->func); +} + +static void wfx_sdio_unlock(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_release_host(bus->func); +} + +static void wfx_sdio_irq_handler(struct sdio_func *func) +{ + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + if (bus->core) + /* empty */; + else + WARN(!bus->core, "race condition in driver init/deinit"); +} + +static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + if (!bus->core) { + WARN(!bus->core, "race condition in driver init/deinit"); + return IRQ_NONE; + } + sdio_claim_host(bus->func); + sdio_release_host(bus->func); + return IRQ_HANDLED; +} + +static int wfx_sdio_irq_subscribe(struct wfx_sdio_priv *bus) +{ + int ret; + + if (bus->of_irq) { + ret = request_irq(bus->of_irq, wfx_sdio_irq_handler_ext, + IRQF_TRIGGER_RISING, "wfx", bus); + } else { + sdio_claim_host(bus->func); + ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler); + sdio_release_host(bus->func); + } + return ret; +} + +static int wfx_sdio_irq_unsubscribe(struct wfx_sdio_priv *bus) +{ + int ret; + + if (bus->of_irq) { + free_irq(bus->of_irq, bus); + ret = 0; + } else { + sdio_claim_host(bus->func); + ret = sdio_release_irq(bus->func); + sdio_release_host(bus->func); + } + return ret; +} + +static size_t wfx_sdio_align_size(void *priv, size_t size) +{ + struct wfx_sdio_priv *bus = priv; + + return sdio_align_size(bus->func, size); +} + +static const struct hwbus_ops wfx_sdio_hwbus_ops = { + .copy_from_io = wfx_sdio_copy_from_io, + .copy_to_io = wfx_sdio_copy_to_io, + .lock = wfx_sdio_lock, + .unlock = wfx_sdio_unlock, + .align_size = wfx_sdio_align_size, +}; static const struct of_device_id wfx_sdio_of_match[]; static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct device_node *np = func->dev.of_node; + struct wfx_sdio_priv *bus; + int ret; if (func->num != 1) { dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n", func->num); return -ENODEV; } + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + if (np) { if (!of_match_node(wfx_sdio_of_match, np)) { dev_warn(&func->dev, "no compatible device found in DT\n"); return -ENODEV; } + bus->of_irq = irq_of_parse_and_map(np, 0); } else { dev_warn(&func->dev, "device is not declared in DT, features will be limited\n"); // FIXME: ignore VID/PID and only rely on device tree // return -ENODEV; } - return -EIO; // FIXME: not yet supported + + bus->func = func; + sdio_set_drvdata(func, bus); + func->card->quirks |= MMC_QUIRK_LENIENT_FN0 | MMC_QUIRK_BLKSZ_FOR_BYTE_MODE | MMC_QUIRK_BROKEN_BYTE_MODE_512; + + sdio_claim_host(func); + ret = sdio_enable_func(func); + // Block of 64 bytes is more efficient than 512B for frame sizes < 4k + sdio_set_block_size(func, 64); + sdio_release_host(func); + if (ret) + goto err0; + + ret = wfx_sdio_irq_subscribe(bus); + if (ret) + goto err1; + + bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata, + &wfx_sdio_hwbus_ops, bus); + if (!bus->core) { + ret = -EIO; + goto err2; + } + + return 0; + +err2: + wfx_sdio_irq_unsubscribe(bus); +err1: + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +err0: + return ret; } static void wfx_sdio_remove(struct sdio_func *func) { + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + wfx_free_common(bus->core); + wfx_sdio_irq_unsubscribe(bus); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); } #define SDIO_VENDOR_ID_SILABS 0x0000 diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index 574b60f513e9..5e8f84baf2ca 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -7,19 +7,217 @@ * Copyright (c) 2010, ST-Ericsson */ #include +#include +#include +#include #include +#include #include #include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" + +static int gpio_reset = -2; +module_param(gpio_reset, int, 0644); +MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none."); + +#define SET_WRITE 0x7FFF /* usage: and operation */ +#define SET_READ 0x8000 /* usage: or operation */ + +static const struct wfx_platform_data wfx_spi_pdata = { +}; + +struct wfx_spi_priv { + struct spi_device *func; + struct wfx_dev *core; + struct gpio_desc *gpio_reset; + struct work_struct request_rx; + bool need_swab; +}; + +/* + * WFx chip read data 16bits at time and place them directly into (little + * endian) CPU register. So, chip expect byte order like "B1 B0 B3 B2" (while + * LE is "B0 B1 B2 B3" and BE is "B3 B2 B1 B0") + * + * A little endian host with bits_per_word == 16 should do the right job + * natively. The code below to support big endian host and commonly used SPI + * 8bits. + */ +static int wfx_spi_copy_from_io(void *priv, unsigned int addr, + void *dst, size_t count) +{ + struct wfx_spi_priv *bus = priv; + u16 regaddr = (addr << 12) | (count / 2) | SET_READ; + struct spi_message m; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .rx_buf = dst, + .len = count, + }; + u16 *dst16 = dst; + int ret, i; + + WARN(count % 2, "buffer size must be a multiple of 2"); + + cpu_to_le16s(®addr); + if (bus->need_swab) + swab16s(®addr); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(bus->func, &m); + + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&dst16[i]); + return ret; +} + +static int wfx_spi_copy_to_io(void *priv, unsigned int addr, + const void *src, size_t count) +{ + struct wfx_spi_priv *bus = priv; + u16 regaddr = (addr << 12) | (count / 2); + // FIXME: use a bounce buffer + u16 *src16 = (void *) src; + int ret, i; + struct spi_message m; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .tx_buf = src, + .len = count, + }; + + WARN(count % 2, "buffer size must be a multiple of 2"); + WARN(regaddr & SET_READ, "bad addr or size overflow"); + + cpu_to_le16s(®addr); + + if (bus->need_swab) + swab16s(®addr); + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&src16[i]); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(bus->func, &m); + + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&src16[i]); + return ret; +} + +static void wfx_spi_lock(void *priv) +{ +} + +static void wfx_spi_unlock(void *priv) +{ +} + +static irqreturn_t wfx_spi_irq_handler(int irq, void *priv) +{ + struct wfx_spi_priv *bus = priv; + + if (!bus->core) { + WARN(!bus->core, "race condition in driver init/deinit"); + return IRQ_NONE; + } + queue_work(system_highpri_wq, &bus->request_rx); + return IRQ_HANDLED; +} + +static void wfx_spi_request_rx(struct work_struct *work) +{ +} + +static size_t wfx_spi_align_size(void *priv, size_t size) +{ + // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned + return ALIGN(size, 4); +} + +static const struct hwbus_ops wfx_spi_hwbus_ops = { + .copy_from_io = wfx_spi_copy_from_io, + .copy_to_io = wfx_spi_copy_to_io, + .lock = wfx_spi_lock, + .unlock = wfx_spi_unlock, + .align_size = wfx_spi_align_size, +}; static int wfx_spi_probe(struct spi_device *func) { - return -EIO; + struct wfx_spi_priv *bus; + int ret; + + if (!func->bits_per_word) + func->bits_per_word = 16; + ret = spi_setup(func); + if (ret) + return ret; + // Trace below is also displayed by spi_setup() if compiled with DEBUG + dev_dbg(&func->dev, "SPI params: CS=%d, mode=%d bits/word=%d speed=%d\n", + func->chip_select, func->mode, func->bits_per_word, func->max_speed_hz); + if (func->bits_per_word != 16 && func->bits_per_word != 8) + dev_warn(&func->dev, "unusual bits/word value: %d\n", func->bits_per_word); + if (func->max_speed_hz > 49000000) + dev_warn(&func->dev, "%dHz is a very high speed\n", func->max_speed_hz); + + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + bus->func = func; + if (func->bits_per_word == 8 || IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + bus->need_swab = true; + spi_set_drvdata(func, bus); + + bus->gpio_reset = wfx_get_gpio(&func->dev, gpio_reset, "reset"); + if (!bus->gpio_reset) { + dev_warn(&func->dev, "try to load firmware anyway\n"); + } else { + gpiod_set_value(bus->gpio_reset, 0); + udelay(100); + gpiod_set_value(bus->gpio_reset, 1); + udelay(2000); + } + + ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, + IRQF_TRIGGER_RISING, "wfx", bus); + if (ret) + return ret; + + INIT_WORK(&bus->request_rx, wfx_spi_request_rx); + bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata, + &wfx_spi_hwbus_ops, bus); + if (!bus->core) + return -EIO; + + return ret; } /* Disconnect Function to be called by SPI stack when device is disconnected */ static int wfx_spi_disconnect(struct spi_device *func) { + struct wfx_spi_priv *bus = spi_get_drvdata(func); + + wfx_free_common(bus->core); + // A few IRQ will be sent during device release. Hopefully, no IRQ + // should happen after wdev/wvif are released. + devm_free_irq(&func->dev, func->irq, bus); + flush_work(&bus->request_rx); return 0; } diff --git a/drivers/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h new file mode 100644 index 000000000000..c490014c1df8 --- /dev/null +++ b/drivers/staging/wfx/hwio.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Low-level API. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_HWIO_H +#define WFX_HWIO_H + +#define CFG_ERR_SPI_FRAME 0x00000001 // only with SPI +#define CFG_ERR_SDIO_BUF_MISMATCH 0x00000001 // only with SDIO +#define CFG_ERR_BUF_UNDERRUN 0x00000002 +#define CFG_ERR_DATA_IN_TOO_LARGE 0x00000004 +#define CFG_ERR_HOST_NO_OUT_QUEUE 0x00000008 +#define CFG_ERR_BUF_OVERRUN 0x00000010 +#define CFG_ERR_DATA_OUT_TOO_LARGE 0x00000020 +#define CFG_ERR_HOST_NO_IN_QUEUE 0x00000040 +#define CFG_ERR_HOST_CRC_MISS 0x00000080 // only with SDIO +#define CFG_SPI_IGNORE_CS 0x00000080 // only with SPI +#define CFG_WORD_MODE_MASK 0x00000300 // Bytes ordering (only writable in SPI): +#define CFG_WORD_MODE0 0x00000000 // B1,B0,B3,B2 (In SPI, register address and CONFIG data always use this mode) +#define CFG_WORD_MODE1 0x00000100 // B3,B2,B1,B0 +#define CFG_WORD_MODE2 0x00000200 // B0,B1,B2,B3 (SDIO) +#define CFG_DIRECT_ACCESS_MODE 0x00000400 // Direct or queue access mode +#define CFG_PREFETCH_AHB 0x00000800 +#define CFG_DISABLE_CPU_CLK 0x00001000 +#define CFG_PREFETCH_SRAM 0x00002000 +#define CFG_CPU_RESET 0x00004000 +#define CFG_SDIO_DISABLE_IRQ 0x00008000 // only with SDIO +#define CFG_IRQ_ENABLE_DATA 0x00010000 +#define CFG_IRQ_ENABLE_WRDY 0x00020000 +#define CFG_CLK_RISE_EDGE 0x00040000 +#define CFG_SDIO_DISABLE_CRC_CHK 0x00080000 // only with SDIO +#define CFG_RESERVED 0x00F00000 +#define CFG_DEVICE_ID_MAJOR 0x07000000 +#define CFG_DEVICE_ID_RESERVED 0x78000000 +#define CFG_DEVICE_ID_TYPE 0x80000000 + +#define CTRL_NEXT_LEN_MASK 0x00000FFF +#define CTRL_WLAN_WAKEUP 0x00001000 +#define CTRL_WLAN_READY 0x00002000 + +#define IGPR_RW 0x80000000 +#define IGPR_INDEX 0x7F000000 +#define IGPR_VALUE 0x00FFFFFF + +#endif /* WFX_HWIO_H */ diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index cd69f955f531..744445ef597c 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -11,10 +11,15 @@ * Copyright (c) 2004-2006 Jean-Baptiste Note , et al. */ #include +#include +#include +#include #include #include #include +#include "main.h" +#include "wfx.h" #include "bus.h" #include "wfx_version.h" @@ -23,6 +28,54 @@ MODULE_AUTHOR("Jérôme Pouiller "); MODULE_LICENSE("GPL"); MODULE_VERSION(WFX_LABEL); +struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label) +{ + struct gpio_desc *ret; + char label_buf[256]; + + if (override >= 0) { + snprintf(label_buf, sizeof(label_buf), "wfx_%s", label); + ret = ERR_PTR(devm_gpio_request_one(dev, override, GPIOF_OUT_INIT_LOW, label_buf)); + if (!ret) + ret = gpio_to_desc(override); + } else if (override == -1) { + ret = NULL; + } else { + ret = devm_gpiod_get(dev, label, GPIOD_OUT_LOW); + } + if (IS_ERR(ret) || !ret) { + if (!ret || PTR_ERR(ret) == -ENOENT) + dev_warn(dev, "gpio %s is not defined\n", label); + else + dev_warn(dev, "error while requesting gpio %s\n", label); + ret = NULL; + } else { + dev_dbg(dev, "using gpio %d for %s\n", desc_to_gpio(ret), label); + } + return ret; +} + +struct wfx_dev *wfx_init_common(struct device *dev, + const struct wfx_platform_data *pdata, + const struct hwbus_ops *hwbus_ops, + void *hwbus_priv) +{ + struct wfx_dev *wdev; + + wdev = devm_kmalloc(dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return NULL; + wdev->dev = dev; + wdev->hwbus_ops = hwbus_ops; + wdev->hwbus_priv = hwbus_priv; + memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + return wdev; +} + +void wfx_free_common(struct wfx_dev *wdev) +{ +} + static int __init wfx_core_init(void) { int ret = 0; diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h new file mode 100644 index 000000000000..82222edf998b --- /dev/null +++ b/drivers/staging/wfx/main.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Device probe and register. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2006, Michael Wu + * Copyright 2004-2006 Jean-Baptiste Note , et al. + */ +#ifndef WFX_MAIN_H +#define WFX_MAIN_H + +#include +#include + +#include "bus.h" + +struct wfx_dev; + +struct wfx_platform_data { +}; + +struct wfx_dev *wfx_init_common(struct device *dev, + const struct wfx_platform_data *pdata, + const struct hwbus_ops *hwbus_ops, + void *hwbus_priv); +void wfx_free_common(struct wfx_dev *wdev); + +struct gpio_desc *wfx_get_gpio(struct device *dev, int override, + const char *label); + +#endif diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h new file mode 100644 index 000000000000..9716acc981df --- /dev/null +++ b/drivers/staging/wfx/wfx.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common private data for Silicon Labs WFx chips. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2006, Michael Wu + * Copyright 2004-2006 Jean-Baptiste Note , et al. + */ +#ifndef WFX_H +#define WFX_H + +#include "main.h" + +struct hwbus_ops; + +struct wfx_dev { + struct wfx_platform_data pdata; + struct device *dev; + const struct hwbus_ops *hwbus_ops; + void *hwbus_priv; +}; + +#endif /* WFX_H */