From patchwork Tue Jan 11 17:14:05 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: 12710123 X-Patchwork-Delegate: kvalo@adurom.com 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 3F395C433EF for ; Tue, 11 Jan 2022 17:15:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1343898AbiAKRPO (ORCPT ); Tue, 11 Jan 2022 12:15:14 -0500 Received: from mail-dm3nam07on2065.outbound.protection.outlook.com ([40.107.95.65]:38465 "EHLO NAM02-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1343955AbiAKRPC (ORCPT ); Tue, 11 Jan 2022 12:15:02 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=NX4SyPaTueeXnU1UCW4kFM7ZWEAn79tHot7xC8L/KU4BkhYlYLub2FfoDSEzQIzvKAhir4x58DQtUnL29P1QDloKfWLjysHOjqGcXcBmWsNKhLtWoVXt36djA9IfTPjL8jQTqGJuMvkzew8TUZ+ZgBCuMZ2SR/GnDXbJQ3LpAWfw9v02yxEXQMPFAhPKtxtmJb9aDFZC4kn7zGF0p1r6JcIORggxq/ARaempydWIHkPch4nze4ODWkYKV2Nz15b0i/zcoxuqFrKQkT3m7hJwGx34ZKWocPyXESyyI+EHNbYCpiUjdheHsRT21q3daBaXW+dEiblb5FuTwmC/GMKxrA== 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=MiTeny0u/cbsH682FeE+dHMx44CGaIV/DCmCDqEFrpo=; b=laceNOlVLhjJff3OKbf57atUkFO4jo9YBOPakUeLkAJ/T9TxdNv1Wdqr6yMSQ1+8o1oDQ/RbB4NoO+XbdErdqVxm4E+E0ID85qmnjDb77ck0rXJ/Kr6WvjFOC0OzuQToVsLT1KBQTH9NprjQnYyKE6OBt1AV2nl7VXftaBEDtFENmvsm/m6PszaMp7YzE0mXc417X3BL1RSpSIu0fWGLZtJA9WKRByU/FLsBMe9nv5y5jMmCKghUPLfhXh1eJVKXwrNXswFJFEaVQnZjDGDZOVK4+AE8g/o7rju3ZrBdqEdSNUlBfLpWQX7StMTIQihghSNMTom7W73HPmKt96yg5g== 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=MiTeny0u/cbsH682FeE+dHMx44CGaIV/DCmCDqEFrpo=; b=UFTWhZpQqk+zHBEqMf6OjWUwP0MYW7FqQwKFBxfX8NryzaneI/gltSHTknXrIh2hw9GtVPaMQVLxiSKqZihb0yc0FiKOipWRJXhbBiDKHDScTyt8o/z5SGpveSip0snBPilXE06NfKaKSmX52UdjBsc3cncHQVRAmzn4vEaM7Fo= 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 PH0PR11MB5674.namprd11.prod.outlook.com (2603:10b6:510:ec::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4867.11; Tue, 11 Jan 2022 17:14:59 +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:14:59 +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 05/24] wfx: add main.c/main.h Date: Tue, 11 Jan 2022 18:14:05 +0100 Message-Id: <20220111171424.862764-6-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: a272a7d3-96e2-4f89-f1de-08d9d525e558 X-MS-TrafficTypeDiagnostic: PH0PR11MB5674:EE_ X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:189; X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: /xZvEDbT8Dw5oyta6oIwOGxWX6ajH21cCQORF7cyByoLoeoTXAw894Ma8grvnBGtca3RjuCQ6yzQH9GMCNJfY/mITAn0l1lY/UNdHoqD4fOlJWxVfKj0f3Q1u9mJJEoIpfr7l1nPIch5TMI9r7raTFrnDVlmxAkJGX2IKMrCytjpaT/LEqqzHM4HPJozb5Chc8Fp0eJG5dJuGxfk9UdPolHE+MdfYfYgbbztvmaCxzqQXW7qxVFn2YAlaBS8oDuqPZFBVCIBIdFJGti55P0hujjBVWeTPv2q6OhfHKXDkbM6clCj6biuEgzLZ95LBKZTGb8LR595syrGl5jO5r+OOJegiqOju8TF6py/JsnyCtTjr/IFve96RNQYXt+ura1eQMH0WIHBqGW4Xd8FLncXgoK9L0pD5CGkxeQ3bzZd3b/LmGB8XEtWBCZ8IeEb/vhAq5Fhb3uZYGu+reQPcenaVrQpitqPjsObmU/c3SvkqnL1pgWtH9ppi4Q6/lS2zjkhjsWauqwrnZ764Re9z/mMJkkaFVhblKYz0EjvG4hMAX/GH2gPjMYZh74XwWuoAWtVRmt0hs3KujUnXDokawLKHoJXR7HNJ1TbbMp7iX4lumNw0AnRndmBcWl8o/DlQ4JQKSVZTiWx8ZU0rOg7nwfEZz8seZX+2YerxbcdhBp1LTcT023F+ytCZEMp1UXZxYGMgaDR48xtpc8q2J5O8nDk5iUFCd7zJ3vgEKaeavkXui0= 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)(6506007)(66946007)(66574015)(8936002)(107886003)(4326008)(66476007)(508600001)(36756003)(84970400001)(7416002)(8676002)(86362001)(6916009)(83380400001)(52116002)(316002)(6512007)(2906002)(6666004)(30864003)(6486002)(1076003)(186003)(66556008)(54906003)(38100700002)(2616005)(5660300002);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?Cq8gwV2/JY7KDuTo9LLDkmz5R8wR?= =?utf-8?q?DrRrZyHLJ9ojGjJlrc7PlAUvnw2HkXZ6G6MbVirblwz9c/NMZu4V3hkyhbvhHW2Ed?= =?utf-8?q?zC79P/1zLCV43TUdS1NWznJ+pEZL2ZieyMoojIu+MTw0e+G0G6VPhDAH0DZgp3Oip?= =?utf-8?q?WDAiDoLBSyf4g+upXd2e1oy6FGpVpLFBpWLcVN9lA9je6dFr3etUcKfkAfQEb66C/?= =?utf-8?q?Dj/Nb7XC5/Eug8Plq23I2DUnUYiAp4qt08A49C1hvi+NkG5nBuIBW7RC1/aInkZt/?= =?utf-8?q?2mBGm09gGLdKzsqklKzZVS8QKPZucms9BV/dBUZGJ9KXFLdxvxyatAIoqbtSrIEOw?= =?utf-8?q?QV/ixztiuadQhQeeBtL8vgc5i8/AjzmLiclpMGzaVehYdCR4rc3Mchlyf9iLWLNqD?= =?utf-8?q?LhBvRUBM63SNfTv9O5XW8Asu3DVudSwhHpKBAfXuIKlzbna4kzznZSnuhIW4xpk2L?= =?utf-8?q?Gb8gn5XF7wP4jJ5o9HNJOupRPikRe56G6RR6mT3htvPwcl/hhQBR1u94USuYbYavW?= =?utf-8?q?9lk8wLItaSsJawXId37QkzGTItSQzKxKXW5kFZ5loW4ebGRCHb07nc7XSQB4qJkm6?= =?utf-8?q?0/7IhQyTAJ9CjzdJGDiIts2RHCMYKqM7h+GypzdUWrL0Dfwx5CzPCtk0wwN7QBLib?= =?utf-8?q?LIs8JqRtaDBK9T/sTBBM3JZwFbjRsCyLXf/vcMpouhHfHfBcwDL7WMEr/uaihvgAa?= =?utf-8?q?CXhUClAftAk8FjinY/lbTHW84UO0CnO8EJ5mS2yeKRgGie04SLZ9CNMtv1/0XkCJk?= =?utf-8?q?6ye9GNMSqE9+/egzaaVyOlLSd0GsBnGQQwXTQg0iXlqBOCISK0UJHNhwS8Ks5zOwF?= =?utf-8?q?/t9UY4o226zAWt3ZfRB1tjFUu5w5clJgl2ZQR5uHwLfMbCirXkzNyPVIRzWN2A59c?= =?utf-8?q?LcNt3O5CGTro9/M+6g4m0J7zyHSBEHBOW1YcShAT76X7Ti3tC2jbj30wIY/FZv+zy?= =?utf-8?q?KtmhKpiyvbN3OjsA5/diOtdPigS4zogrNLd4O5rLqvMTJ7jgCh6+klq5Dr7Nbnbny?= =?utf-8?q?egS2QwTCtziobppBPnkUsBE1CJlR4S++zZW4VBvaaDFtVYxP/Yynr7SwtJEg6RYT6?= =?utf-8?q?G0zhLO1V3rLA30rbl35hd+Em1ircZRk6hLfmtXmz9Kw3cbvUz1JAaVZBWSDLoMqb+?= =?utf-8?q?Zk6UcOcxzSQOlza5ynFnn3iM3YaUKQzEI7YPxx8cCmVRTt1k4pNOlVGl7MlyyPPwr?= =?utf-8?q?O447/LL3kJ1Ff/5Y14QR4hjcPJQGqZFUL2RwOEFMNh24FM82YT36cV9Kw35bdCB0c?= =?utf-8?q?WWJBi9rWCkrHvPpTP5+mn1rB7026RnFiCBXM2bIy7tjvHZ+1ZkmUzZJ1jwm2wRRtS?= =?utf-8?q?7RkE6C0PsK/tccWRrlmUtsOlHGfPsdDaK33+kK49rUDfTJD+dnVJ5zogtRtO4M096?= =?utf-8?q?eR+yVuLTMuu9mT55I2HBT3QbFeEIgVDhofLVErEXUpxpPs18hSNlz0vmI1+y/zD+A?= =?utf-8?q?M2xS85+oynNyPDtgZsim76Qd9aylnPoVJzynwpYckfWHR7z4/5cuuEFRmqHR6mekD?= =?utf-8?q?mNStdGV76jcmYojNq0tnU0g7STse7jiLKEQwWYRAnO5zBSQ0QjbyeSmDBm4XtpCT0?= =?utf-8?q?JIbaCFh5N864fP3AOA6m11z1lYBu3Hn0h3MoEnW5mhcPSm7wZp4hmw=3D?= X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: a272a7d3-96e2-4f89-f1de-08d9d525e558 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:14:59.1234 (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: bzo4GycsnADactlG7Dng8z0zDbc3pGhiCOdc332dyi4hEieTk0Z1D6FXECIAqvMtX3H4VI3p9cG76EPiWgunEA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR11MB5674 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Signed-off-by: Jérôme Pouiller --- drivers/net/wireless/silabs/wfx/main.c | 485 +++++++++++++++++++++++++ drivers/net/wireless/silabs/wfx/main.h | 42 +++ 2 files changed, 527 insertions(+) create mode 100644 drivers/net/wireless/silabs/wfx/main.c create mode 100644 drivers/net/wireless/silabs/wfx/main.h diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c new file mode 100644 index 000000000000..d3507b91263b --- /dev/null +++ b/drivers/net/wireless/silabs/wfx/main.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Device probe and register. + * + * Copyright (c) 2017-2020, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2008, Johannes Berg + * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2004-2006 Jean-Baptiste Note , et al. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "wfx.h" +#include "fwio.h" +#include "hwio.h" +#include "bus.h" +#include "bh.h" +#include "sta.h" +#include "key.h" +#include "scan.h" +#include "debug.h" +#include "data_tx.h" +#include "hif_tx_mib.h" +#include "hif_api_cmd.h" + +#define WFX_PDS_TLV_TYPE 0x4450 // "PD" (Platform Data) in ascii little-endian +#define WFX_PDS_MAX_CHUNK_SIZE 1500 + +MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WF200"); +MODULE_AUTHOR("Jérôme Pouiller "); +MODULE_LICENSE("GPL"); + +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ +} + +static struct ieee80211_rate wfx_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(55, 2, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(110, 3, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel wfx_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_supported_band wfx_band_2ghz = { + .channels = wfx_2ghz_chantable, + .n_channels = ARRAY_SIZE(wfx_2ghz_chantable), + .bitrates = wfx_rates, + .n_bitrates = ARRAY_SIZE(wfx_rates), + .ht_cap = { + /* Receive caps */ + .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_MAX_AMSDU | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask = { 0xFF }, /* MCS0 to MCS7 */ + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const struct ieee80211_iface_limit wdev_iface_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination wfx_iface_combinations[] = { + { + .num_different_channels = 2, + .max_interfaces = 2, + .limits = wdev_iface_limits, + .n_limits = ARRAY_SIZE(wdev_iface_limits), + } +}; + +static const struct ieee80211_ops wfx_ops = { + .start = wfx_start, + .stop = wfx_stop, + .add_interface = wfx_add_interface, + .remove_interface = wfx_remove_interface, + .config = wfx_config, + .tx = wfx_tx, + .join_ibss = wfx_join_ibss, + .leave_ibss = wfx_leave_ibss, + .conf_tx = wfx_conf_tx, + .hw_scan = wfx_hw_scan, + .cancel_hw_scan = wfx_cancel_hw_scan, + .start_ap = wfx_start_ap, + .stop_ap = wfx_stop_ap, + .sta_add = wfx_sta_add, + .sta_remove = wfx_sta_remove, + .set_tim = wfx_set_tim, + .set_key = wfx_set_key, + .set_rts_threshold = wfx_set_rts_threshold, + .set_default_unicast_key = wfx_set_default_unicast_key, + .bss_info_changed = wfx_bss_info_changed, + .configure_filter = wfx_configure_filter, + .ampdu_action = wfx_ampdu_action, + .flush = wfx_flush, + .add_chanctx = wfx_add_chanctx, + .remove_chanctx = wfx_remove_chanctx, + .change_chanctx = wfx_change_chanctx, + .assign_vif_chanctx = wfx_assign_vif_chanctx, + .unassign_vif_chanctx = wfx_unassign_vif_chanctx, +}; + +bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) +{ + if (wdev->hw_caps.api_version_major < major) + return true; + if (wdev->hw_caps.api_version_major > major) + return false; + if (wdev->hw_caps.api_version_minor < minor) + return true; + return false; +} + +/* The device needs data about the antenna configuration. This information in + * provided by PDS (Platform Data Set, this is the wording used in WF200 + * documentation) files. For hardware integrators, the full process to create + * PDS files is described here: + * https:github.com/SiliconLabs/wfx-firmware/blob/master/PDS/README.md + * + * The PDS file is an array of Time-Length-Value structs. + */ + int wfx_send_pds(struct wfx_dev *wdev, u8 *buf, size_t len) +{ + int ret, chunk_type, chunk_len, chunk_num = 0; + + if (*buf == '{') { + dev_err(wdev->dev, "PDS: malformed file (legacy format?)\n"); + return -EINVAL; + } + while (len > 0) { + chunk_type = get_unaligned_le16(buf + 0); + chunk_len = get_unaligned_le16(buf + 2); + if (chunk_len > len) { + dev_err(wdev->dev, "PDS:%d: corrupted file\n", chunk_num); + return -EINVAL; + } + if (chunk_type != WFX_PDS_TLV_TYPE) { + dev_info(wdev->dev, "PDS:%d: skip unknown data\n", chunk_num); + goto next; + } + if (chunk_len > WFX_PDS_MAX_CHUNK_SIZE) + dev_warn(wdev->dev, "PDS:%d: unexpectly large chunk\n", chunk_num); + if (buf[4] != '{' || buf[chunk_len - 1] != '}') + dev_warn(wdev->dev, "PDS:%d: unexpected content\n", chunk_num); + + ret = wfx_hif_configuration(wdev, buf + 4, chunk_len - 4); + if (ret > 0) { + dev_err(wdev->dev, "PDS:%d: invalid data (unsupported options?)\n", + chunk_num); + return -EINVAL; + } + if (ret == -ETIMEDOUT) { + dev_err(wdev->dev, "PDS:%d: chip didn't reply (corrupted file?)\n", + chunk_num); + return ret; + } + if (ret) { + dev_err(wdev->dev, "PDS:%d: chip returned an unknown error\n", chunk_num); + return -EIO; + } +next: + chunk_num++; + len -= chunk_len; + buf += chunk_len; + } + return 0; +} + +static int wfx_send_pdata_pds(struct wfx_dev *wdev) +{ + int ret = 0; + const struct firmware *pds; + u8 *tmp_buf; + + ret = request_firmware(&pds, wdev->pdata.file_pds, wdev->dev); + if (ret) { + dev_err(wdev->dev, "can't load antenna parameters (PDS file %s). The device may be unstable.\n", + wdev->pdata.file_pds); + return ret; + } + tmp_buf = kmemdup(pds->data, pds->size, GFP_KERNEL); + if (!tmp_buf) { + ret = -ENOMEM; + goto release_fw; + } + ret = wfx_send_pds(wdev, tmp_buf, pds->size); + kfree(tmp_buf); +release_fw: + release_firmware(pds); + return ret; +} + +static void wfx_free_common(void *data) +{ + struct wfx_dev *wdev = data; + + mutex_destroy(&wdev->tx_power_loop_info_lock); + mutex_destroy(&wdev->rx_stats_lock); + mutex_destroy(&wdev->conf_mutex); + ieee80211_free_hw(wdev->hw); +} + +struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, + const struct wfx_hwbus_ops *hwbus_ops, void *hwbus_priv) +{ + struct ieee80211_hw *hw; + struct wfx_dev *wdev; + + hw = ieee80211_alloc_hw(sizeof(struct wfx_dev), &wfx_ops); + if (!hw) + return NULL; + + SET_IEEE80211_DEV(hw, dev); + + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, MFP_CAPABLE); + + hw->vif_data_size = sizeof(struct wfx_vif); + hw->sta_data_size = sizeof(struct wfx_sta_priv); + hw->queues = 4; + hw->max_rates = 8; + hw->max_rate_tries = 8; + hw->extra_tx_headroom = sizeof(struct wfx_hif_msg) + sizeof(struct wfx_hif_req_tx) + + 4 /* alignment */ + 8 /* TKIP IV */; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + hw->wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; + hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; + hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + hw->wiphy->max_ap_assoc_sta = HIF_LINK_ID_MAX; + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(wfx_iface_combinations); + hw->wiphy->iface_combinations = wfx_iface_combinations; + hw->wiphy->bands[NL80211_BAND_2GHZ] = devm_kmalloc(dev, sizeof(wfx_band_2ghz), GFP_KERNEL); + /* FIXME: also copy wfx_rates and wfx_2ghz_chantable */ + memcpy(hw->wiphy->bands[NL80211_BAND_2GHZ], &wfx_band_2ghz, sizeof(wfx_band_2ghz)); + + wdev = hw->priv; + wdev->hw = hw; + wdev->dev = dev; + wdev->hwbus_ops = hwbus_ops; + wdev->hwbus_priv = hwbus_priv; + memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + of_property_read_string(dev->of_node, "silabs,antenna-config-file", &wdev->pdata.file_pds); + wdev->pdata.gpio_wakeup = devm_gpiod_get_optional(dev, "wakeup", GPIOD_OUT_LOW); + if (IS_ERR(wdev->pdata.gpio_wakeup)) + return NULL; + if (wdev->pdata.gpio_wakeup) + gpiod_set_consumer_name(wdev->pdata.gpio_wakeup, "wfx wakeup"); + + mutex_init(&wdev->conf_mutex); + mutex_init(&wdev->rx_stats_lock); + mutex_init(&wdev->tx_power_loop_info_lock); + init_completion(&wdev->firmware_ready); + INIT_DELAYED_WORK(&wdev->cooling_timeout_work, wfx_cooling_timeout_work); + skb_queue_head_init(&wdev->tx_pending); + init_waitqueue_head(&wdev->tx_dequeue); + wfx_init_hif_cmd(&wdev->hif_cmd); + + if (devm_add_action_or_reset(dev, wfx_free_common, wdev)) + return NULL; + + return wdev; +} + +int wfx_probe(struct wfx_dev *wdev) +{ + int i; + int err; + struct gpio_desc *gpio_saved; + + /* During first part of boot, gpio_wakeup cannot yet been used. So prevent bh() to touch + * it. + */ + gpio_saved = wdev->pdata.gpio_wakeup; + wdev->pdata.gpio_wakeup = NULL; + wdev->poll_irq = true; + + wfx_bh_register(wdev); + + err = wfx_init_device(wdev); + if (err) + goto bh_unregister; + + wfx_bh_poll_irq(wdev); + err = wait_for_completion_timeout(&wdev->firmware_ready, 1 * HZ); + if (err <= 0) { + if (err == 0) { + dev_err(wdev->dev, "timeout while waiting for startup indication\n"); + err = -ETIMEDOUT; + } else if (err == -ERESTARTSYS) { + dev_info(wdev->dev, "probe interrupted by user\n"); + } + goto bh_unregister; + } + + /* FIXME: fill wiphy::hw_version */ + dev_info(wdev->dev, "started firmware %d.%d.%d \"%s\" (API: %d.%d, keyset: %02X, caps: 0x%.8X)\n", + wdev->hw_caps.firmware_major, wdev->hw_caps.firmware_minor, + wdev->hw_caps.firmware_build, wdev->hw_caps.firmware_label, + wdev->hw_caps.api_version_major, wdev->hw_caps.api_version_minor, + wdev->keyset, wdev->hw_caps.link_mode); + snprintf(wdev->hw->wiphy->fw_version, + sizeof(wdev->hw->wiphy->fw_version), + "%d.%d.%d", + wdev->hw_caps.firmware_major, + wdev->hw_caps.firmware_minor, + wdev->hw_caps.firmware_build); + + if (wfx_api_older_than(wdev, 1, 0)) { + dev_err(wdev->dev, "unsupported firmware API version (expect 1 while firmware returns %d)\n", + wdev->hw_caps.api_version_major); + err = -EOPNOTSUPP; + goto bh_unregister; + } + + if (wdev->hw_caps.link_mode == SEC_LINK_ENFORCED) { + dev_err(wdev->dev, "chip require secure_link, but can't negotiate it\n"); + goto bh_unregister; + } + + if (wdev->hw_caps.region_sel_mode) { + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[11].flags |= + IEEE80211_CHAN_NO_IR; + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[12].flags |= + IEEE80211_CHAN_NO_IR; + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[13].flags |= + IEEE80211_CHAN_DISABLED; + } + + dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds); + err = wfx_send_pdata_pds(wdev); + if (err < 0 && err != -ENOENT) + goto bh_unregister; + + wdev->poll_irq = false; + err = wdev->hwbus_ops->irq_subscribe(wdev->hwbus_priv); + if (err) + goto bh_unregister; + + err = wfx_hif_use_multi_tx_conf(wdev, true); + if (err) + dev_err(wdev->dev, "misconfigured IRQ?\n"); + + wdev->pdata.gpio_wakeup = gpio_saved; + if (wdev->pdata.gpio_wakeup) { + dev_dbg(wdev->dev, "enable 'quiescent' power mode with wakeup GPIO and PDS file %s\n", + wdev->pdata.file_pds); + gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 1); + wfx_control_reg_write(wdev, 0); + wfx_hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT); + } else { + wfx_hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_DOZE); + } + + for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) { + eth_zero_addr(wdev->addresses[i].addr); + err = of_get_mac_address(wdev->dev->of_node, wdev->addresses[i].addr); + if (!err) + wdev->addresses[i].addr[ETH_ALEN - 1] += i; + else + ether_addr_copy(wdev->addresses[i].addr, wdev->hw_caps.mac_addr[i]); + if (!is_valid_ether_addr(wdev->addresses[i].addr)) { + dev_warn(wdev->dev, "using random MAC address\n"); + eth_random_addr(wdev->addresses[i].addr); + } + dev_info(wdev->dev, "MAC address %d: %pM\n", i, wdev->addresses[i].addr); + } + wdev->hw->wiphy->n_addresses = ARRAY_SIZE(wdev->addresses); + wdev->hw->wiphy->addresses = wdev->addresses; + + if (!wfx_api_older_than(wdev, 3, 8)) + wdev->hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + + err = ieee80211_register_hw(wdev->hw); + if (err) + goto irq_unsubscribe; + + err = wfx_debug_init(wdev); + if (err) + goto ieee80211_unregister; + + return 0; + +ieee80211_unregister: + ieee80211_unregister_hw(wdev->hw); +irq_unsubscribe: + wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv); +bh_unregister: + wfx_bh_unregister(wdev); + return err; +} + +void wfx_release(struct wfx_dev *wdev) +{ + ieee80211_unregister_hw(wdev->hw); + wfx_hif_shutdown(wdev); + wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv); + wfx_bh_unregister(wdev); +} + +static int __init wfx_core_init(void) +{ + int ret = 0; + + if (IS_ENABLED(CONFIG_SPI)) + ret = spi_register_driver(&wfx_spi_driver); + if (IS_ENABLED(CONFIG_MMC) && !ret) + ret = sdio_register_driver(&wfx_sdio_driver); + return ret; +} +module_init(wfx_core_init); + +static void __exit wfx_core_exit(void) +{ + if (IS_ENABLED(CONFIG_MMC)) + sdio_unregister_driver(&wfx_sdio_driver); + if (IS_ENABLED(CONFIG_SPI)) + spi_unregister_driver(&wfx_spi_driver); +} +module_exit(wfx_core_exit); diff --git a/drivers/net/wireless/silabs/wfx/main.h b/drivers/net/wireless/silabs/wfx/main.h new file mode 100644 index 000000000000..fcd26b24519e --- /dev/null +++ b/drivers/net/wireless/silabs/wfx/main.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Device probe and register. + * + * Copyright (c) 2017-2020, 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 "hif_api_general.h" + +struct wfx_dev; +struct wfx_hwbus_ops; + +struct wfx_platform_data { + /* Keyset and ".sec" extension will be appended to this string */ + const char *file_fw; + const char *file_pds; + struct gpio_desc *gpio_wakeup; + bool reset_inverted; + /* if true HIF D_out is sampled on the rising edge of the clock (intended to be used in + * 50Mhz SDIO) + */ + bool use_rising_clk; +}; + +struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, + const struct wfx_hwbus_ops *hwbus_ops, void *hwbus_priv); + +int wfx_probe(struct wfx_dev *wdev); +void wfx_release(struct wfx_dev *wdev); + +bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor); +int wfx_send_pds(struct wfx_dev *wdev, u8 *buf, size_t len); + +#endif