From patchwork Mon Mar 15 13:24:42 2021 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: 400959 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-21.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, MSGID_FROM_MTA_HEADER, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4742AC433E6 for ; Mon, 15 Mar 2021 13:26:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1280364EF8 for ; Mon, 15 Mar 2021 13:26:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230450AbhCON0L (ORCPT ); Mon, 15 Mar 2021 09:26:11 -0400 Received: from mail-eopbgr700087.outbound.protection.outlook.com ([40.107.70.87]:60257 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S229506AbhCONZh (ORCPT ); Mon, 15 Mar 2021 09:25:37 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=BqIEQyPBI0ChOmAY1Au+TJIc56cd2di8PpaPEINu03Kl7HL30KiWwHokO1uB7DLSvtABKAIfrOg7jQU2gdjxoQTgi7X2YN6ilcdLFdrt5kSSIZ32gupNf47L2OF5xuZJnrLjfy+mzvo4mi46cJUO3aIRnX5GqDYSZQSkQmrYHihN6jLme9CCYIHqFZwO7nrbMHKsF8yHfM3ZGbud6zVjDF2Y41F7aA5UcvUY2hI15rlIReErtR9IgMjQ/WG4oHuxSN/3igsY96HPN7EJsr/fxT9ksRO9IC0DSQsFMKMOXZNm/Ows+7k0mNEb8gvl5BNES+oIhG/lDa+sNCKXVv6H4g== 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=J8WC8zAlBc+Dsn1EMSLYwI+1Ox5dZ5wxVtA7cOhjgQw=; b=gh3/j9vB8PW36vP4PRrWUTguh2pDaoAW+fVmpLBJusqgMLr54u7n2lCeu1TmMqqyUab7fcdTAaQZrwuyHyO/ih8fqX7laRGFlpnfLNNv8BOSbx0n6ho0T3tBxqGiVHH2VdpS49p0T0WiT8KUdPe+vs24DeTxUHM//ahjms4c/OxB2VuvZ5ySiz69LSmQecxOGCrEkWtsDDoxgTwKqcaaXiOPox1bREISJEC9DKOvmGslSAcln40/9vdmHlMCCLoD4SF9+AcSbuRpQEbWCbrUAW5dX91684NReRg+zikyzpd1Qz4n8mNdVttUcXAD8eNDXI5En+IG5fiXoP1D4ZVYXQ== 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=J8WC8zAlBc+Dsn1EMSLYwI+1Ox5dZ5wxVtA7cOhjgQw=; b=aogpuS1cqKBIAinzNpoys0kUOv/DPsZHL3vlEceoDG5QftlwI4mvawKbONEHJeeX/DzjcMpsRck7GPIt91/WbCRBH37IJJ1S6jxqrU3dvtpmpCF7y/nfUlhi1UtJ2VKUWgytelrkTP6hg4VFRIPKhH3+rO/6us0SvLS2kw7XEBU= Authentication-Results: vger.kernel.org; dkim=none (message not signed) header.d=none; vger.kernel.org; dmarc=none action=none header.from=silabs.com; Received: from SN6PR11MB2718.namprd11.prod.outlook.com (2603:10b6:805:63::18) by SA2PR11MB5099.namprd11.prod.outlook.com (2603:10b6:806:f9::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3933.32; Mon, 15 Mar 2021 13:25:36 +0000 Received: from SN6PR11MB2718.namprd11.prod.outlook.com ([fe80::41bc:5ce:dfa0:9701]) by SN6PR11MB2718.namprd11.prod.outlook.com ([fe80::41bc:5ce:dfa0:9701%7]) with mapi id 15.20.3933.032; Mon, 15 Mar 2021 13:25:36 +0000 From: Jerome Pouiller To: linux-wireless@vger.kernel.org, netdev@vger.kernel.org Cc: devel@driverdev.osuosl.org, linux-kernel@vger.kernel.org, Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , devicetree@vger.kernel.org, Rob Herring , linux-mmc@vger.kernel.org, =?utf-8?q?Pali_Roh=C3=A1r?= , Ulf Hansson , =?utf-8?b?SsOpcsO0bWUg?= =?utf-8?q?Pouiller?= Subject: [PATCH v5 05/24] wfx: add main.c/main.h Date: Mon, 15 Mar 2021 14:24:42 +0100 Message-Id: <20210315132501.441681-6-Jerome.Pouiller@silabs.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210315132501.441681-1-Jerome.Pouiller@silabs.com> References: <20210315132501.441681-1-Jerome.Pouiller@silabs.com> X-Originating-IP: [2a01:e35:2435:66a0:544b:f17b:7ae8:fb7] X-ClientProxiedBy: SN4PR0801CA0014.namprd08.prod.outlook.com (2603:10b6:803:29::24) To SN6PR11MB2718.namprd11.prod.outlook.com (2603:10b6:805:63::18) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from pc-42.silabs.com (2a01:e35:2435:66a0:544b:f17b:7ae8:fb7) by SN4PR0801CA0014.namprd08.prod.outlook.com (2603:10b6:803:29::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3933.32 via Frontend Transport; Mon, 15 Mar 2021 13:25:34 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: f367e137-825f-49c3-29d7-08d8e7b5d159 X-MS-TrafficTypeDiagnostic: SA2PR11MB5099: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:208; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 5AEaxRxOfmiPNdKNK4e9eC6q52sVV4SbDMym2YGHfAKWC0sQRln9CpIcnHazH70X1I9OUkhGOj3c2P/MCmLEbSc+b6m6+CSp7m6Uz7fcJt9t6a5TSyL7XplwIOdo2v2lRwaBu6UmuF0ldeGHDCV+9QYfMCpf6fZbd3A+OYQGVholtoJW7nyZtUehorRkxFPdFoPp723glXdHeGsmATcTk5rQajS/SK9si8q2bcCx4/CEEDKYArUqHxxE1Wpx5Qf3Fr9yCgEcFmsu2CJeRGeoOUo0UqXmyF7gqM3cEa+URWQX5HAoCQWT8XY5wSPrsfQnO9kew+Q+KDrva6owMSSrIuZ+OSh3O/ux4O5lbzcaZ0nQDTyPEEsefwBYzf9aoTg2lvo5Xr9Abfep3m7JZGxbtwhqW3a5DBR0tS/83WUtPBQrx+dMqCoygFmCUuU2cqDmtOnbXawBEUO0i25rUTeS/8rZVXxo9tnbiEfIvAtOxohAqHsh7rFwIPjI0YZSBb8hdIoaSlWLvONtnavR6umkKyASwzsPC+BSZChzUusYkRUHlPo+eGBWqkYgva25YXM1 X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SN6PR11MB2718.namprd11.prod.outlook.com; PTR:; CAT:NONE; SFS:(346002)(136003)(396003)(39850400004)(376002)(366004)(186003)(7416002)(2616005)(107886003)(4326008)(478600001)(83380400001)(16526019)(66476007)(86362001)(66946007)(54906003)(66556008)(8936002)(30864003)(316002)(5660300002)(66574015)(8676002)(1076003)(52116002)(7696005)(6666004)(36756003)(2906002)(6486002); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData: =?utf-8?q?kd98C/IQwrMvzD1MleIAncPdGel?= =?utf-8?q?I/hOJ5VCvUrwCLSEN3lS8l4Dr8ltGrPypRgwDVPag/Pn1uDR7Zp6m7wp?= =?utf-8?q?Ll6V493JKKTVD+5MdPPRFfF5arpOgDP9vVV6CaXXbmBUz6XW3LWnY0iB?= =?utf-8?q?5fm5Mhsq1jleQtsHKMBkrodrKiggE1kMuKWFQF54KxohG1x2RyeOyYC1?= =?utf-8?q?ABnolgyS+2bg15trpJX6eKcUWnHOgX4tDOwtZmmdsuTtd17vmMcpaMqX?= =?utf-8?q?34CvWk1BB42CDIO4wMyYf0yyjDkmWIYheFukjnfVn7v0Vk/d54VdgITi?= =?utf-8?q?u/ihx+kTWM64traBKNS/sIAF/CLAxZNzP0eZ7DJmZb1DdYgRDacL3lmW?= =?utf-8?q?QWna1G9JMK09siTy8dYxOebXZc/bV/REkGctqN0zQt9qaru1Cvu+7/bt?= =?utf-8?q?cWxKSwqaxU6MZA0J9GCJwNzOA5WJls87xxonfunBCyDafm16JXM0x6K3?= =?utf-8?q?xJAZiYuczi4f1TrgV0U8yvvBc4qKGFN1O9YrbP97xDBx21G9Xo1sLw+1?= =?utf-8?q?hnHkbv2fiUt1j8RwMyYJ8vn6/iH1fAzM6lVLrYLj3Iypbf+NrCn9wdSK?= =?utf-8?q?DF8QWVFQgN3dxFyE2d5EAPVch4JZax6w9RVA6bWS0iwd1EWDAK9c5XKl?= =?utf-8?q?c7aEjFTAicLMTyPjnQJ40zZOCyfKudDrBbqS1YC37Zp2YHy2MQ+UOGg4?= =?utf-8?q?zz1JTKrj1SMkcHGYBp+WgTGg63/e4nR9uTZlEbnU4x2QNfr3g7LcDjQa?= =?utf-8?q?m8SdHaZQdd2gtPbxX+mV2bNDUQe4obP802TAG4/MnRUM4JiJL7FkwhDu?= =?utf-8?q?aIBAdpayxbKM3tPixh3yjp2CpytkN6nfljvbImvmGTDwMHUbWvnbb2q1?= =?utf-8?q?ORCmq5iuVbD7j0rG1723Aox/erpf19dFbbt9slWmVKbubrerM7y/YvJu?= =?utf-8?q?vdn/iI3xakLCzFioCydNrANWze0XU9P+IJAuB26mKJN+LABN6ryCrClI?= =?utf-8?q?LDQoHXyGk5bX/W0iBh0n6YCsvzwqomjt7zIkokgGFTdO991WR+M0t/qJ?= =?utf-8?q?1OGKW/PNWEJqHOQvvS/Rgqw5uNLQisPa9zlWx/yEcryWaV1jFFVDdL+p?= =?utf-8?q?kz0D1Ukt3lrEEhGEeOQw24+C8pZQkzAAzIfV+3wOPBmz/InPPZKpOfnw?= =?utf-8?q?FXMiMHCWs6zu8hskWaqBGV7j5d8Kp3OPTVosTAhHSozH03EZRBneXX1I?= =?utf-8?q?3fsXRsDEUGbt/MzZjZm/tUYtn1JEINtwd5c9EMAK2CCjww2hGejrM1ye?= =?utf-8?q?SIg7mOnLPpXbKNEUdUX4QPD9l/+yevJbzLklCsEIZzNMi6MYhwiKl4Ih?= =?utf-8?q?X2D20kSm4t8Mbwgrln2ufRkVuu4OMtY0bkdLEvNPkAx6qtbzG+TDZv+G?= =?utf-8?q?ifc7BX5M5XIy3yJhPt+0WezGsEo8Uxhv8ZYUX?= X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: f367e137-825f-49c3-29d7-08d8e7b5d159 X-MS-Exchange-CrossTenant-AuthSource: SN6PR11MB2718.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Mar 2021 13:25:36.4251 (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: GoqlzVTRYcmfYAfz/LPG9SBGYPfrDubbxnjoRm7/jEc404Z5iweKTjYP2qAHtbsoIbB2IQ0J1Eo5bYrB7qK7ug== X-MS-Exchange-Transport-CrossTenantHeadersStamped: SA2PR11MB5099 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 | 503 +++++++++++++++++++++++++ drivers/net/wireless/silabs/wfx/main.h | 43 +++ 2 files changed, 546 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..16c4806fc932 --- /dev/null +++ b/drivers/net/wireless/silabs/wfx/main.c @@ -0,0 +1,503 @@ +// 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_MAX_SIZE 1500 + +MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx"); +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 + * + * So this function aims to send PDS to the device. However, the PDS file is + * often bigger than Rx buffers of the chip, so it has to be sent in multiple + * parts. + * + * In add, the PDS data cannot be split anywhere. The PDS files contains tree + * structures. Braces are used to enter/leave a level of the tree (in a JSON + * fashion). PDS files can only been split between root nodes. + */ +int wfx_send_pds(struct wfx_dev *wdev, u8 *buf, size_t len) +{ + int ret; + int start, brace_level, i; + + start = 0; + brace_level = 0; + if (buf[0] != '{') { + dev_err(wdev->dev, "valid PDS start with '{'. Did you forget to compress it?\n"); + return -EINVAL; + } + for (i = 1; i < len - 1; i++) { + if (buf[i] == '{') + brace_level++; + if (buf[i] == '}') + brace_level--; + if (buf[i] == '}' && !brace_level) { + i++; + if (i - start + 1 > WFX_PDS_MAX_SIZE) + return -EFBIG; + buf[start] = '{'; + buf[i] = 0; + dev_dbg(wdev->dev, "send PDS '%s}'\n", buf + start); + buf[i] = '}'; + ret = hif_configuration(wdev, buf + start, + i - start + 1); + if (ret > 0) { + dev_err(wdev->dev, "PDS bytes %d to %d: invalid data (unsupported options?)\n", + start, i); + return -EINVAL; + } + if (ret == -ETIMEDOUT) { + dev_err(wdev->dev, "PDS bytes %d to %d: chip didn't reply (corrupted file?)\n", + start, i); + return ret; + } + if (ret) { + dev_err(wdev->dev, "PDS bytes %d to %d: chip returned an unknown error\n", + start, i); + return -EIO; + } + buf[i] = ','; + start = i; + } + } + 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 PDS file %s\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 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 hif_msg) + + sizeof(struct 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); + wdev->force_ps_timeout = -1; + + 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; + const void *macaddr; + 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 err0; + + 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 err0; + } + + /* 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 = -ENOTSUPP; + goto err0; + } + + if (wdev->hw_caps.link_mode == SEC_LINK_ENFORCED) { + dev_err(wdev->dev, + "chip require secure_link, but can't negotiate it\n"); + goto err0; + } + + 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) + goto err0; + + wdev->poll_irq = false; + err = wdev->hwbus_ops->irq_subscribe(wdev->hwbus_priv); + if (err) + goto err0; + + err = 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); + control_reg_write(wdev, 0); + hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT); + } else { + 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); + macaddr = of_get_mac_address(wdev->dev->of_node); + if (!IS_ERR_OR_NULL(macaddr)) { + ether_addr_copy(wdev->addresses[i].addr, macaddr); + 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; + + err = ieee80211_register_hw(wdev->hw); + if (err) + goto err1; + + err = wfx_debug_init(wdev); + if (err) + goto err2; + + return 0; + +err2: + ieee80211_unregister_hw(wdev->hw); +err1: + wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv); +err0: + wfx_bh_unregister(wdev); + return err; +} + +void wfx_release(struct wfx_dev *wdev) +{ + ieee80211_unregister_hw(wdev->hw); + 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..115abd2d4378 --- /dev/null +++ b/drivers/net/wireless/silabs/wfx/main.h @@ -0,0 +1,43 @@ +/* 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 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; + /* 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 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