From patchwork Tue Jan 11 17:14:08 2022 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: 12710081 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6CB43C43217 for ; Tue, 11 Jan 2022 17:16:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344289AbiAKRQ1 (ORCPT ); Tue, 11 Jan 2022 12:16:27 -0500 Received: from mail-mw2nam12on2061.outbound.protection.outlook.com ([40.107.244.61]:39649 "EHLO NAM12-MW2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S243073AbiAKRPm (ORCPT ); Tue, 11 Jan 2022 12:15:42 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AUY9xrpez9GoiURrNAGwXpX101BbI7suMwZ99em1BEsFFa77Py8d66Mv+cp2Nh+KAgph69j/SPQBTfJraBM3cUC2QeW6DPiNA2DVXZGqDPAQ4TatxCV75+HLOHDRPdUoOA+OdJyYhiN1Fwm1jJiz6zVhL6FtthU1GC0ucbRq1chS0taHE2UtSrjxfEPc7CwvchmsC2sggp1Uku+lkpmz1i0cUJ2nwRb3eSnQJD8geP/AwzCllt737FzQ4gfesC53Q8k/LnAYj84ZIkUVj/aVfYOBTU4nt6dQJtEkSly8eE1AJmQxcFlPS5BwoetjxPvjTFiv8hGFFhuIgfG6iYt+hA== 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-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=FKR/RqIimPfbxVdFm9QPX0JtwSB1m5A+/5JMbv21Loo=; b=VklzYCANVlkMvH73yLVqg2rDbT1LFEaf38TXjWbg9l9VlgXe617TkExT0QDYaoGerkuMdWkp0IY427X5cJIhAHi/FPYuYYnARP1OPYTbYqm9dYF1G1p4qqBlO8mfoXs79twNh0IR5gsQ4aCanJakO8u0T8WMezXwoV6RpPV+uD5QTQsiu0ALiB+k27/duVGPJwGsP+gDNs/vtELqGS2wA2f/o+/IAztKBsosDwlym1WZaGtNGsfOpwkLfSqTEe+G+0skYkZt+2THK+fB/jm2h7aRoT6s2qjLNv1Sb6N9zPRyBHdPChegOHqV6ffmjZc2vqiTbupWIHZ9PBl9O2vHGA== 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=FKR/RqIimPfbxVdFm9QPX0JtwSB1m5A+/5JMbv21Loo=; b=DBCXl8G48uIPRPXN0vE03BdB+PQqKySttV6nOoyAf0i/V3pm1CVRpdwuMBUwzm6eA5eFTYD//3z240KLGyf8K0labdIWGrImlokfz2aZ2jAnTZf8V6TqlaYFGTc4mLHozpuFpZ1RWwyVGg0Mf8r3M00FdzSCQ4wxqulL2SlQSoI= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=silabs.com; Received: from PH0PR11MB5657.namprd11.prod.outlook.com (2603:10b6:510:ee::19) by PH0PR11MB5595.namprd11.prod.outlook.com (2603:10b6:510:e5::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4867.7; Tue, 11 Jan 2022 17:15:07 +0000 Received: from PH0PR11MB5657.namprd11.prod.outlook.com ([fe80::d031:da9e:71a:73e4]) by PH0PR11MB5657.namprd11.prod.outlook.com ([fe80::d031:da9e:71a:73e4%6]) with mapi id 15.20.4867.012; Tue, 11 Jan 2022 17:15:07 +0000 From: Jerome Pouiller To: linux-wireless@vger.kernel.org, netdev@vger.kernel.org, Kalle Valo Cc: devel@driverdev.osuosl.org, linux-kernel@vger.kernel.org, Greg Kroah-Hartman , "David S . Miller" , devicetree@vger.kernel.org, Rob Herring , linux-mmc@vger.kernel.org, =?utf-8?q?Pali?= =?utf-8?q?_Roh=C3=A1r?= , Ulf Hansson , =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= Subject: [PATCH v9 08/24] wfx: add bus_sdio.c Date: Tue, 11 Jan 2022 18:14:08 +0100 Message-Id: <20220111171424.862764-9-Jerome.Pouiller@silabs.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220111171424.862764-1-Jerome.Pouiller@silabs.com> References: <20220111171424.862764-1-Jerome.Pouiller@silabs.com> X-ClientProxiedBy: SN1PR12CA0099.namprd12.prod.outlook.com (2603:10b6:802:21::34) To PH0PR11MB5657.namprd11.prod.outlook.com (2603:10b6:510:ee::19) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 36914231-a93f-42f4-55a8-08d9d525ea1e X-MS-TrafficTypeDiagnostic: PH0PR11MB5595:EE_ X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2512; X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: WPs0kDfrgTZ9NsOp7KGNnEsXzwWb0DQSMWplmVeidHXCQ8uYl7NixdvgLbCE0QuNTDnKHggdlVGfGf85fYb+NvC2NM7HT8iGGY/0wkz4culmyPURcyivL8gjih+IYJgL9fUmXjw0Bumb43nGfZHyB943MLfZ4uZwwmtGt5Zb5odx7HiHoS8H7bOINiEh475Y+tMuwa6xOCaLCXSc8AJmrK/EnKapE8MT65FfCuukANKNCDFxtV5cFp9E6MPPGZcpKCdmkl2Po64g/7fqRTEVG6tc00rKomxb//0POd+H71OOZZ2QgP6dvQTXR2CM9x+p0zP1OmkMl84VjHFeW5PqM+gGeIiQ8hjt0AJ1wvS141Kl7+Ts02lysWI9ScIdO27a0knshY6QrfCKs8rwqiIX6oS2NGBVtlXzyJ6UVx/gFuzkUkfJ0OzjUmKq1q7Xj2oORe0nMWmQwn4W5gGvw/malRUD+XSBF6UmgZsIjZRV9K9Mnsmyu/s1wCVWJpwPK/BPqY3278xiV9KLeBXa7VmTNUOCJHsUABlGFXyp7YUO3slMDwIqgxye7gO022ADsFRIevcF7+gfXK3v+TL5TMoi6OywOyx7a6ipy642d980JR5B0ZZIYelpL17LPXgOk+65VBVmGKl88iFXc0Ka6AsbDA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PH0PR11MB5657.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(366004)(36756003)(66946007)(6512007)(2906002)(107886003)(66574015)(8936002)(2616005)(7416002)(38100700002)(6506007)(4326008)(66476007)(8676002)(54906003)(6916009)(316002)(66556008)(83380400001)(52116002)(86362001)(186003)(5660300002)(6486002)(508600001)(1076003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?Lpda3IsB5js5i8XvuGwLtg+L+JAU?= =?utf-8?q?8g80wwMBMZhTF7quCip6eM8kUBUzCCjYgCWsJiOiXgD/wtqBlpP6BvVSVeegMlzw6?= =?utf-8?q?WSjQw/ggb4hPFRRb++pwzCAeEGs6X6IX+vqFTbWed0+UT/BkHlso6FQHJj5dl1s4I?= =?utf-8?q?2hcL8iHImsNIuVoJ5TPAPbNEjX/sCpKFo9643k+T6QJ4YU/WdncYpQGQFsJNZml/m?= =?utf-8?q?ScUwRreYiwVJI0/b26MstudsiQLpicbxO8Drt23XtvlvwOEG/Zij7vzVhvWyTFCb+?= =?utf-8?q?vMNJoilD4R6PSdC6m0OP6yRCqELHKmetNAXgv7wYyPuFuOI2jh6zfZwNXNYU1svYO?= =?utf-8?q?QifSp6DSHistTp5G4hs5ki2H3ibFxbvXEcnQvnCRV/p3bfBnqn9KerMs7kZxvVsuL?= =?utf-8?q?fh1HZwqwkbuk0UjDGkbi5MM6jAU+VXypJGED1aUkmGku8LfTr7rtbW1S2tcK/Yijn?= =?utf-8?q?2hBOo1/KLWJ+nMNqQnTF3TL9kkv6D7Axxd688qtC2FJty99rEudF2bgrD7IXrdRhh?= =?utf-8?q?2p3v5ghZ9IEH29EzUHJ7pkexChc+dxlWiHEjJpvqvNbgP13d5LataX3A2SYjsWULW?= =?utf-8?q?HWyJ34AH/UQt92Bzo395okFkn5bxd43w+wh6YU/j0z1eJn3woAPGlNu5wbCPx7dr+?= =?utf-8?q?Yyv72CSK1T+fgCNZsYnwI3z0iOtZolD4vGZM1rYyYVBNDOKJ14uEv1J2xKIwNBrCf?= =?utf-8?q?8qQuIJUKRlZ5ORlPka+FYSETaCOQq3Af6jWV9y0570Dv2KU7J1tyHS4H1KUtYafRk?= =?utf-8?q?r5nizrJRuVxVHzldiYr7rt6AiGF2HV5FExIeGhGFdQf/9Ww/6Kmfw9q7dknEv33zZ?= =?utf-8?q?IttpYxCU6XTAO8zigkfCxa6Y8T/QVwYcejK2F1XLrIii+ZBs38VaEQbUxv3JrRmN+?= =?utf-8?q?xOA3TnpIx3VDphCGgjU+YLpocaLc4dedgZ4CF+GrjyvrxO6gFt21ekr809emCFScd?= =?utf-8?q?+iSxjxYe1X5cHjfP9397YSUdED3lrr+8cAfjiHHQXfZ4Nxo2H0YY9FqawaZ8Hyi4J?= =?utf-8?q?2+9LlPAn6FB2lwsu05viTlYgn1bynQd8FqLkBELVcRN+9kCaxw05gJYKNIOw1+FFm?= =?utf-8?q?RcGnhvQEOu6Ys2gKvmhUxnhhWHPit6jfP8U/aVBAaZQcLOKcZPZbDXr8qTPxs7oAv?= =?utf-8?q?oXLhe1vSCTMDKFix1azN7kfmv3iyRoh6f4QUtixqoOmY8ZKRxbwMEW6tc50NLqRZv?= =?utf-8?q?rGyP+34zMPS+h9s23IYR4MgXXYzWLgoyujh7bItgf8YV3PScv61SP4/3jVNxp7A2R?= =?utf-8?q?v0zqi6ajnEo8quL88bxKFsF0NVgLIaT70dtQQeUyBScUkq44UpucHGYkb+I32pIcB?= =?utf-8?q?xCylU5n/M4+r20z38jFWA+bTfcxpcp/DOsBFw1qmF9LqrxYOxu1yiKObPKKu1CNCp?= =?utf-8?q?Js4F3XuDBVFifP4yBZV0/emHoHB4aioZZ+f0uiV7VosrGoXlE6PjbiFw/cLaSPC6Y?= =?utf-8?q?v5IrwhmgDXONAf6IdX3a9iNdxT/9YYeSMAJX8wwv5vvm76fCL5aSGOixkPoNcDwTB?= =?utf-8?q?KQs+HZFXdOQti16QCgtEKZqMLbRzCWNzAinpI7X1RChbLQFgWgasORJ0dS3Z5xG69?= =?utf-8?q?wXmLwo07AO1BI+aK+oZS44zRqRloaXX+in0SL+/K4Xy1HvXv0rBdUk=3D?= X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 36914231-a93f-42f4-55a8-08d9d525ea1e X-MS-Exchange-CrossTenant-AuthSource: PH0PR11MB5657.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Jan 2022 17:15:07.0019 (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: he71OrWnXT8CX8vtILnX1ZPJJztt8+WvEpyMverHSt6fQqr7QCNkCkCewjypc+YSZEzNsQKGx8skAPp3Bmjz6A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR11MB5595 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Jérôme Pouiller Signed-off-by: Jérôme Pouiller --- drivers/net/wireless/silabs/wfx/bus_sdio.c | 283 +++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 drivers/net/wireless/silabs/wfx/bus_sdio.c diff --git a/drivers/net/wireless/silabs/wfx/bus_sdio.c b/drivers/net/wireless/silabs/wfx/bus_sdio.c new file mode 100644 index 000000000000..ce873abe2740 --- /dev/null +++ b/drivers/net/wireless/silabs/wfx/bus_sdio.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SDIO interface. + * + * Copyright (c) 2017-2020, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" +#include "bh.h" + +static const struct wfx_platform_data pdata_wf200 = { + .file_fw = "wfx/wfm_wf200", + .file_pds = "wfx/wf200.pds", +}; + +static const struct wfx_platform_data pdata_brd4001a = { + .file_fw = "wfx/wfm_wf200", + .file_pds = "wfx/brd4001a.pds", +}; + +static const struct wfx_platform_data pdata_brd8022a = { + .file_fw = "wfx/wfm_wf200", + .file_pds = "wfx/brd8022a.pds", +}; + +static const struct wfx_platform_data pdata_brd8023a = { + .file_fw = "wfx/wfm_wf200", + .file_pds = "wfx/brd8023a.pds", +}; + +/* Legacy DT don't use it */ +static const struct wfx_platform_data pdata_wfx_sdio = { + .file_fw = "wfm_wf200", + .file_pds = "wf200.pds", +}; + +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; + + WARN(reg_id > 7, "chip only has 7 registers"); + WARN(!IS_ALIGNED((uintptr_t)dst, 4), "unaligned buffer address"); + WARN(!IS_ALIGNED(count, 4), "unaligned buffer size"); + + /* 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; + + WARN(reg_id > 7, "chip only has 7 registers"); + WARN(!IS_ALIGNED((uintptr_t)src, 4), "unaligned buffer address"); + WARN(!IS_ALIGNED(count, 4), "unaligned buffer size"); + + /* 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); + + wfx_bh_request_rx(bus->core); +} + +static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_claim_host(bus->func); + wfx_bh_request_rx(bus->core); + sdio_release_host(bus->func); + return IRQ_HANDLED; +} + +static int wfx_sdio_irq_subscribe(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + u32 flags; + int ret; + u8 cccr; + + if (!bus->of_irq) { + sdio_claim_host(bus->func); + ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler); + sdio_release_host(bus->func); + return ret; + } + + flags = irq_get_trigger_type(bus->of_irq); + if (!flags) + flags = IRQF_TRIGGER_HIGH; + flags |= IRQF_ONESHOT; + ret = devm_request_threaded_irq(&bus->func->dev, bus->of_irq, NULL, + wfx_sdio_irq_handler_ext, flags, "wfx", bus); + if (ret) + return ret; + sdio_claim_host(bus->func); + cccr = sdio_f0_readb(bus->func, SDIO_CCCR_IENx, NULL); + cccr |= BIT(0); + cccr |= BIT(bus->func->num); + sdio_f0_writeb(bus->func, cccr, SDIO_CCCR_IENx, NULL); + sdio_release_host(bus->func); + return 0; +} + +static int wfx_sdio_irq_unsubscribe(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + int ret; + + if (bus->of_irq) + devm_free_irq(&bus->func->dev, bus->of_irq, bus); + 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 wfx_hwbus_ops wfx_sdio_hwbus_ops = { + .copy_from_io = wfx_sdio_copy_from_io, + .copy_to_io = wfx_sdio_copy_to_io, + .irq_subscribe = wfx_sdio_irq_subscribe, + .irq_unsubscribe = wfx_sdio_irq_unsubscribe, + .lock = wfx_sdio_lock, + .unlock = wfx_sdio_unlock, + .align_size = wfx_sdio_align_size, +}; + +static const struct of_device_id wfx_sdio_of_match[] = { + { .compatible = "silabs,wf200", .data = &pdata_wf200 }, + { .compatible = "silabs,brd4001a", .data = &pdata_brd4001a }, + { .compatible = "silabs,brd8022a", .data = &pdata_brd8022a }, + { .compatible = "silabs,brd8023a", .data = &pdata_brd8023a }, + { .compatible = "silabs,wfx-sdio", .data = &pdata_wfx_sdio }, + { }, +}; +MODULE_DEVICE_TABLE(of, wfx_sdio_of_match); + +static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + const struct wfx_platform_data *pdata = of_device_get_match_data(&func->dev); + 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; + } + + if (!pdata) { + dev_warn(&func->dev, "no compatible device found in DT\n"); + return -ENODEV; + } + + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + + bus->func = func; + bus->of_irq = irq_of_parse_and_map(np, 0); + 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) + return ret; + + bus->core = wfx_init_common(&func->dev, pdata, &wfx_sdio_hwbus_ops, bus); + if (!bus->core) { + ret = -EIO; + goto sdio_release; + } + + ret = wfx_probe(bus->core); + if (ret) + goto sdio_release; + + return 0; + +sdio_release: + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + return ret; +} + +static void wfx_sdio_remove(struct sdio_func *func) +{ + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + wfx_release(bus->core); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +} + +static const struct sdio_device_id wfx_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_SILABS, SDIO_DEVICE_ID_SILABS_WF200) }, + { }, +}; +MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids); + +struct sdio_driver wfx_sdio_driver = { + .name = "wfx-sdio", + .id_table = wfx_sdio_ids, + .probe = wfx_sdio_probe, + .remove = wfx_sdio_remove, + .drv = { + .owner = THIS_MODULE, + .of_match_table = wfx_sdio_of_match, + } +};