From patchwork Thu Sep 19 13:52:30 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: 11152453 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BBC6316B1 for ; Thu, 19 Sep 2019 13:54:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 725C021D6C for ; Thu, 19 Sep 2019 13:54:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="qdeyaUp8" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2403789AbfISNwm (ORCPT ); Thu, 19 Sep 2019 09:52:42 -0400 Received: from mail-eopbgr700060.outbound.protection.outlook.com ([40.107.70.60]:34912 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387924AbfISNwl (ORCPT ); Thu, 19 Sep 2019 09:52:41 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=cr3knVlfdLEd4oVq9VaV3b3dlvtO110Q8OxKRzdGTuNCg72OlgoLZEX2+J/xn0R/f72+6FnR/nDKdN3pv+T9jIzSUZznNdB2LN9gGQ1fMBC7tyyHMjH5c+Y0bVjpZpi7fvjERPTQiuZWqtAicAeGjpjbO29qMaBVX5zxOTKHLo/dKSexqCC87kzo/oU9xXf8HqXfZxoam1uDAo5w/RgImw+t6YFtSvQPpciRqWVu55jYv2V+lH0VTtXRqb1ivQBsCCQlTq0DnO0+1fnyquySFWmJVvpRr7djvRNOAL/HHsVSThRQnBpvTxOrbLOwlKbKsvriR4Lz5FDVaeq3tMlS1A== 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=JJmLYKGBNP1yEW+U7Bq9VBXjKPNOhoQ6Z0Z3d3DgoVXtLowEmL7H+Q3IGpsZOUXirIvbAYH4QAb+eTDGUNoEFOJJXOvXN7+soo6FSaT9cXFQiwp0ZZDiJqRxIOTCVHtz9Z+rbQ3OdFCglBWQAOmZlE8t2XZZi81gg+nx3Oaq/nZi+C4ybU8b3v+jq3GwrSh9w5JEtYtot2VcdUEDdBR/7uHC2jW5JeVu/Ig7fe8E4mKP2aBNltrqMQa14/uKJkBHpHyU3wgDo1+YmLjvW/e+DiAwqtdrqlS1sD3I6aW2Mz7tWkZR60YlEBv5IIQH0C26ljPm6YV0UHF+CYOv9JHouw== 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=qdeyaUp8GD8qCumdyhsGHyWrUwyQBtPxju8jTzeuTgz5Uao5Ejs2hIFizNz7Vq3oixuN1YKnh5+vz1QU/RJTzAcjJa/4ISg9usgbAP9P4Ay9rNPZg7BoaWXSJtyc6kt1aI8dV+VPNvaTuSBoTZFtpSvYoI5/qKswfrIjmi8OBf8= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4400.namprd11.prod.outlook.com (52.135.37.204) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.20; Thu, 19 Sep 2019 13:52:31 +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 13:52:31 +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 v2 02/20] staging: wfx: add support for I/O access Thread-Topic: [PATCH v2 02/20] staging: wfx: add support for I/O access Thread-Index: AQHVbvF6faUoM0BeiUKFyqEHsn0RWg== Date: Thu, 19 Sep 2019 13:52:30 +0000 Message-ID: <20190919135220.30663-3-Jerome.Pouiller@silabs.com> References: <20190919135220.30663-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919135220.30663-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: b81b54c7-bf11-4ecf-4720-08d73d089db4 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4400; x-ms-traffictypediagnostic: MN2PR11MB4400: 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)(376002)(39850400004)(136003)(366004)(396003)(346002)(189003)(199004)(3846002)(76176011)(8676002)(76116006)(476003)(25786009)(2616005)(6116002)(54906003)(71190400001)(6436002)(102836004)(6486002)(36756003)(186003)(71200400001)(26005)(305945005)(6512007)(99286004)(6506007)(91956017)(7736002)(446003)(8936002)(256004)(81156014)(14444005)(2906002)(66066001)(30864003)(107886003)(4326008)(86362001)(14454004)(478600001)(64756008)(81166006)(2501003)(486006)(66574012)(316002)(66946007)(66446008)(66556008)(5660300002)(11346002)(66476007)(1076003)(110136005);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4400;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A: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: UfsD2wX74x49lSnqUZoOzzrHVXIPdLEZ+BNd+lrpBukQe+shFckr0fGibQ9o7Zid4GIgWBLB8YL+fCK3BV+bO0FeEq2WgpIXIgvYqVii2nqFgMNMOzBWRCM+76CqRU9u022QxchlYLLTVzrvo1Y0T/w9GX5wLwKIhZuld+tqeulwpyeKBW/EumjFkNC9SvRK9uUxWvWDVKuZrMXnKWxaw3b3R7t1dZiraTWU3Hoc2CviYf9qsrYWsg5HLTkjDKu5yfFarQZk/ylq8C7MAMtfP7J+r0Uvjqi4i5hvkI1MTQw6QdwECDepwisF5TFjScAgwiB9zWPA9/9B4ebbyutaS69mNQzSk7t2pfIvhFLVT8kAAZ74KFSCTPMkilyWiZBrMA7eHGkpmgeD26QtG4ZjlvJ37fWqD5tbj5b6hoUkDGw= Content-ID: <9A31C8C1B88F0140BD001FE6A239E4A8@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: b81b54c7-bf11-4ecf-4720-08d73d089db4 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 13:52:30.4228 (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: b7aSphENCu497JKVZixohdkFLIVFUkLi9Z4JUifTInTsM4moaxsgfEBjSKvu/rDCZy1l1BGxR+Nu2uhljNVVUA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4400 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@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 */