From 747ff7d3d7424876111b7559b7f6704762f89796 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 5 Oct 2021 16:55:53 +0300 Subject: [PATCH 01/16] ath10k: Don't always treat modem stop events as crashes When rebooting on sc7180 Trogdor devices I see the following crash from the wifi driver. ath10k_snoc 18800000.wifi: firmware crashed! (guid 83493570-29a2-4e98-a83e-70048c47669c) This is because a modem stop event looks just like a firmware crash to the driver, the qmi connection is closed in both cases. Use the qcom ssr notifier block to stop treating the qmi connection close event as a firmware crash signal when the modem hasn't actually crashed. See ath10k_qmi_event_server_exit() for more details. This silences the crash message seen during every reboot. Fixes: 3f14b73c3843 ("ath10k: Enable MSA region dump support for WCN3990") Cc: Youghandhar Chintala Cc: Abhishek Kumar Cc: Steev Klimaszewski Cc: Matthias Kaehlcke Cc: Rakesh Pillai Signed-off-by: Stephen Boyd Reviewed-by: Rakesh Pillai Tested-By: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210922233341.182624-1-swboyd@chromium.org --- drivers/net/wireless/ath/ath10k/qmi.c | 3 +- drivers/net/wireless/ath/ath10k/snoc.c | 77 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/snoc.h | 5 ++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 07e478f9a808..80fcb917fe4e 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -864,7 +864,8 @@ static void ath10k_qmi_event_server_exit(struct ath10k_qmi *qmi) ath10k_qmi_remove_msa_permission(qmi); ath10k_core_free_board_files(ar); - if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags)) + if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags) && + !test_bit(ATH10K_SNOC_FLAG_MODEM_STOPPED, &ar_snoc->flags)) ath10k_snoc_fw_crashed_dump(ar); ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_DOWN_IND); diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index ea00fbb15601..9513ab696fff 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -1477,6 +1478,74 @@ void ath10k_snoc_fw_crashed_dump(struct ath10k *ar) mutex_unlock(&ar->dump_mutex); } +static int ath10k_snoc_modem_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, nb); + struct ath10k *ar = ar_snoc->ar; + struct qcom_ssr_notify_data *notify_data = data; + + switch (action) { + case QCOM_SSR_BEFORE_POWERUP: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem starting event\n"); + clear_bit(ATH10K_SNOC_FLAG_MODEM_STOPPED, &ar_snoc->flags); + break; + + case QCOM_SSR_AFTER_POWERUP: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem running event\n"); + break; + + case QCOM_SSR_BEFORE_SHUTDOWN: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem %s event\n", + notify_data->crashed ? "crashed" : "stopping"); + if (!notify_data->crashed) + set_bit(ATH10K_SNOC_FLAG_MODEM_STOPPED, &ar_snoc->flags); + else + clear_bit(ATH10K_SNOC_FLAG_MODEM_STOPPED, &ar_snoc->flags); + break; + + case QCOM_SSR_AFTER_SHUTDOWN: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "received modem offline event\n"); + break; + + default: + ath10k_err(ar, "received unrecognized event %lu\n", action); + break; + } + + return NOTIFY_OK; +} + +static int ath10k_modem_init(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + void *notifier; + int ret; + + ar_snoc->nb.notifier_call = ath10k_snoc_modem_notify; + + notifier = qcom_register_ssr_notifier("mpss", &ar_snoc->nb); + if (IS_ERR(notifier)) { + ret = PTR_ERR(notifier); + ath10k_err(ar, "failed to initialize modem notifier: %d\n", ret); + return ret; + } + + ar_snoc->notifier = notifier; + + return 0; +} + +static void ath10k_modem_deinit(struct ath10k *ar) +{ + int ret; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + ret = qcom_unregister_ssr_notifier(ar_snoc->notifier, &ar_snoc->nb); + if (ret) + ath10k_err(ar, "error %d unregistering notifier\n", ret); +} + static int ath10k_setup_msa_resources(struct ath10k *ar, u32 msa_size) { struct device *dev = ar->dev; @@ -1740,10 +1809,17 @@ static int ath10k_snoc_probe(struct platform_device *pdev) goto err_fw_deinit; } + ret = ath10k_modem_init(ar); + if (ret) + goto err_qmi_deinit; + ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n"); return 0; +err_qmi_deinit: + ath10k_qmi_deinit(ar); + err_fw_deinit: ath10k_fw_deinit(ar); @@ -1771,6 +1847,7 @@ static int ath10k_snoc_free_resources(struct ath10k *ar) ath10k_fw_deinit(ar); ath10k_snoc_free_irq(ar); ath10k_snoc_release_resource(ar); + ath10k_modem_deinit(ar); ath10k_qmi_deinit(ar); ath10k_core_destroy(ar); diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 5095d1893681..d4bce1707696 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -6,6 +6,8 @@ #ifndef _SNOC_H_ #define _SNOC_H_ +#include + #include "hw.h" #include "ce.h" #include "qmi.h" @@ -45,6 +47,7 @@ struct ath10k_snoc_ce_irq { enum ath10k_snoc_flags { ATH10K_SNOC_FLAG_REGISTERED, ATH10K_SNOC_FLAG_UNREGISTERING, + ATH10K_SNOC_FLAG_MODEM_STOPPED, ATH10K_SNOC_FLAG_RECOVERY, ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, }; @@ -75,6 +78,8 @@ struct ath10k_snoc { struct clk_bulk_data *clks; size_t num_clks; struct ath10k_qmi *qmi; + struct notifier_block nb; + void *notifier; unsigned long flags; bool xo_cal_supported; u32 xo_cal_data; From 053f9852b95e7721cf3b615539d701bae3e17ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 5 Oct 2021 16:55:53 +0300 Subject: [PATCH 02/16] ath9k: add option to reset the wifi chip via debugfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes, in yet unknown cases the wifi chip stops working. To allow a watchdog in userspace to easily and quickly reset the wifi chip, add the according functionality to userspace. A reset can then be triggered via: $ echo 1 > /sys/kernel/debug/ieee80211/phy0/ath9k/reset The number of user resets can further be tracked in the row "User reset" in the same file. So far people usually used "iw scan" to fix ath9k chip hangs from userspace. Which triggers the ath9k_queue_reset(), too. The reset file however has the advantage of less overhead, which makes debugging bugs within ath9k_queue_reset() easier. Signed-off-by: Linus Lüssing Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210914192515.9273-2-linus.luessing@c0d3.blue --- drivers/net/wireless/ath/ath9k/debug.c | 57 ++++++++++++++++++++++++-- drivers/net/wireless/ath/ath9k/debug.h | 1 + 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 4c81b1d7f417..fb7a2952d0ce 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -749,9 +749,9 @@ static int read_file_misc(struct seq_file *file, void *data) static int read_file_reset(struct seq_file *file, void *data) { - struct ieee80211_hw *hw = dev_get_drvdata(file->private); - struct ath_softc *sc = hw->priv; + struct ath_softc *sc = file->private; static const char * const reset_cause[__RESET_TYPE_MAX] = { + [RESET_TYPE_USER] = "User reset", [RESET_TYPE_BB_HANG] = "Baseband Hang", [RESET_TYPE_BB_WATCHDOG] = "Baseband Watchdog", [RESET_TYPE_FATAL_INT] = "Fatal HW Error", @@ -779,6 +779,55 @@ static int read_file_reset(struct seq_file *file, void *data) return 0; } +static int open_file_reset(struct inode *inode, struct file *f) +{ + return single_open(f, read_file_reset, inode->i_private); +} + +static ssize_t write_file_reset(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file_inode(file)->i_private; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val != 1) + return -EINVAL; + + /* avoid rearming hw_reset_work on shutdown */ + mutex_lock(&sc->mutex); + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { + mutex_unlock(&sc->mutex); + return -EBUSY; + } + + ath9k_queue_reset(sc, RESET_TYPE_USER); + mutex_unlock(&sc->mutex); + + return count; +} + +static const struct file_operations fops_reset = { + .read = seq_read, + .write = write_file_reset, + .open = open_file_reset, + .owner = THIS_MODULE, + .llseek = seq_lseek, + .release = single_release, +}; + void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags) @@ -1393,8 +1442,8 @@ int ath9k_init_debug(struct ath_hw *ah) read_file_queues); debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy, read_file_misc); - debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy, - read_file_reset); + debugfs_create_file("reset", 0600, sc->debug.debugfs_phy, + sc, &fops_reset); ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 33826aa13687..389459c04d14 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -39,6 +39,7 @@ struct fft_sample_tlv; #endif enum ath_reset_type { + RESET_TYPE_USER, RESET_TYPE_BB_HANG, RESET_TYPE_BB_WATCHDOG, RESET_TYPE_FATAL_INT, From 4925642d541278575ad1948c5924d71ffd57ef14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 5 Oct 2021 16:55:53 +0300 Subject: [PATCH 03/16] ath9k: Fix potential interrupt storm on queue reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In tests with two Lima boards from 8devices (QCA4531 based) on OpenWrt 19.07 we could force a silent restart of a device with no serial output when we were sending a high amount of UDP traffic (iperf3 at 80 MBit/s in both directions from external hosts, saturating the wifi and causing a load of about 4.5 to 6) and were then triggering an ath9k_queue_reset(). Further debugging showed that the restart was caused by the ath79 watchdog. With disabled watchdog we could observe that the device was constantly going into ath_isr() interrupt handler and was returning early after the ATH_OP_HW_RESET flag test, without clearing any interrupts. Even though ath9k_queue_reset() calls ath9k_hw_kill_interrupts(). With JTAG we could observe the following race condition: 1) ath9k_queue_reset() ... -> ath9k_hw_kill_interrupts() -> set_bit(ATH_OP_HW_RESET, &common->op_flags); ... <- returns 2) ath9k_tasklet() ... -> ath9k_hw_resume_interrupts() ... <- returns 3) loops around: ... handle_int() -> ath_isr() ... -> if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) return IRQ_HANDLED; x) ath_reset_internal(): => never reached <= And in ath_isr() we would typically see the following interrupts / interrupt causes: * status: 0x00111030 or 0x00110030 * async_cause: 2 (AR_INTR_MAC_IPQ) * sync_cause: 0 So the ath9k_tasklet() reenables the ath9k interrupts through ath9k_hw_resume_interrupts() which ath9k_queue_reset() had just disabled. And ath_isr() then keeps firing because it returns IRQ_HANDLED without actually clearing the interrupt. To fix this IRQ storm also clear/disable the interrupts again when we are in reset state. Cc: Sven Eckelmann Cc: Simon Wunderlich Cc: Linus Lüssing Fixes: 872b5d814f99 ("ath9k: do not access hardware on IRQs during reset") Signed-off-by: Linus Lüssing Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210914192515.9273-3-linus.luessing@c0d3.blue --- drivers/net/wireless/ath/ath9k/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 139831539da3..98090e40e1cf 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -533,8 +533,10 @@ irqreturn_t ath_isr(int irq, void *dev) ath9k_debug_sync_cause(sc, sync_cause); status &= ah->imask; /* discard unasked-for bits */ - if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) { + ath9k_hw_kill_interrupts(sc->sc_ah); return IRQ_HANDLED; + } /* * If there are no status bits set, then this interrupt was not From 57bb2398bd5fb202cd9688a13c685992737d4c16 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 5 Oct 2021 16:55:54 +0300 Subject: [PATCH 04/16] dt-bindings: net: wireless: qca,ath9k: convert to the json-schema This replaces the existing .txt binding file. Furthermore, this also helps with validating DTS files. Introduced binding changes: 1. added inherited mac-address nvmem property 2. added inherited ieee80211-freq-limit property 3. added new calibration nvmem property 4. added second example (taken from the Netgear WNDR3700v2) Reason: Setting qca,no-eeprom; takes presedence over nvmem-cells. I think a different example is needed, because the driver can only reads from one calibration source per device. 5. (re-added) chip list (based on data from ath9k's pci.c) Added binding .yaml to MAINTAINERS. Reviewed-by: Rob Herring Signed-off-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210924223509.52525-1-chunkeey@gmail.com --- .../bindings/net/wireless/qca,ath9k.txt | 48 ---------- .../bindings/net/wireless/qca,ath9k.yaml | 90 +++++++++++++++++++ MAINTAINERS | 1 + 3 files changed, 91 insertions(+), 48 deletions(-) delete mode 100644 Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt create mode 100644 Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml diff --git a/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt deleted file mode 100644 index aaaeeb5f935b..000000000000 --- a/Documentation/devicetree/bindings/net/wireless/qca,ath9k.txt +++ /dev/null @@ -1,48 +0,0 @@ -* Qualcomm Atheros ath9k wireless devices - -This node provides properties for configuring the ath9k wireless device. The -node is expected to be specified as a child node of the PCI controller to -which the wireless chip is connected. - -Required properties: -- compatible: For PCI and PCIe devices this should be an identifier following - the format as defined in "PCI Bus Binding to Open Firmware" - Revision 2.1. One of the possible formats is "pciVVVV,DDDD" - where VVVV is the PCI vendor ID and DDDD is PCI device ID. - Typically QCA's PCI vendor ID 168c is used while the PCI device - ID depends on the chipset - see the following (possibly - incomplete) list: - - 0023 for AR5416 - - 0024 for AR5418 - - 0027 for AR9160 - - 0029 for AR9220 and AR9223 - - 002a for AR9280 and AR9283 - - 002b for AR9285 - - 002c for AR2427 - - 002d for AR9227 - - 002e for AR9287 - - 0030 for AR9380, AR9381 and AR9382 - - 0032 for AR9485 - - 0033 for AR9580 and AR9590 - - 0034 for AR9462 - - 0036 for AR9565 - - 0037 for AR9485 -- reg: Address and length of the register set for the device. - -Optional properties: -- qca,no-eeprom: Indicates that there is no physical EEPROM connected to the - ath9k wireless chip (in this case the calibration / - EEPROM data will be loaded from userspace using the - kernel firmware loader). - -The MAC address will be determined using the optional properties defined in -net/ethernet.txt. - -In this example, the node is defined as child node of the PCI controller: -&pci0 { - wifi@168c,002d { - compatible = "pci168c,002d"; - reg = <0x7000 0 0 0 0x1000>; - qca,no-eeprom; - }; -}; diff --git a/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml new file mode 100644 index 000000000000..8cd0adbf7021 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/qca,ath9k.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Atheros ath9k wireless devices Generic Binding + +maintainers: + - Kalle Valo + +description: | + This node provides properties for configuring the ath9k wireless device. + The node is expected to be specified as a child node of the PCI controller + to which the wireless chip is connected. + +allOf: + - $ref: ieee80211.yaml# + +properties: + compatible: + enum: + - pci168c,0023 # AR5416 + - pci168c,0024 # AR5418 + - pci168c,0027 # AR9160 + - pci168c,0029 # AR9220 and AR9223 + - pci168c,002a # AR9280 and AR9283 + - pci168c,002b # AR9285 + - pci168c,002c # AR2427 - 802.11n bonded out + - pci168c,002d # AR9227 + - pci168c,002e # AR9287 + - pci168c,0030 # AR9380, AR9381 and AR9382 + - pci168c,0032 # AR9485 + - pci168c,0033 # AR9580 and AR9590 + - pci168c,0034 # AR9462 + - pci168c,0036 # AR9565 + - pci168c,0037 # AR1111 and AR9485 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + ieee80211-freq-limit: true + + qca,no-eeprom: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates that there is no physical EEPROM connected + + nvmem-cells: + items: + - description: Reference to an nvmem node for the MAC address + - description: Reference to an nvmem node for calibration data + + nvmem-cell-names: + items: + - const: mac-address + - const: calibration + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + pcie0 { + #address-cells = <3>; + #size-cells = <2>; + wifi@0,0 { + compatible = "pci168c,002d"; + reg = <0 0 0 0 0>; + interrupts = <3>; + qca,no-eeprom; + }; + }; + - | + pci0 { + #address-cells = <3>; + #size-cells = <2>; + wifi@0,11 { + compatible = "pci168c,0029"; + reg = <0x8800 0 0 0 0>; + nvmem-cells = <&macaddr_art_c>, <&cal_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index eeb4c70b3d5b..78e911d67b28 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15456,6 +15456,7 @@ M: ath9k-devel@qca.qualcomm.com L: linux-wireless@vger.kernel.org S: Supported W: https://wireless.wiki.kernel.org/en/users/Drivers/ath9k +F: Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml F: drivers/net/wireless/ath/ath9k/ QUALCOMM CAMERA SUBSYSTEM DRIVER From 7210b4b77fe47697890d2d3b2ce57ac9f767bffc Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Tue, 5 Oct 2021 16:55:54 +0300 Subject: [PATCH 05/16] ath11k: Remove unused variable in ath11k_dp_rx_mon_merg_msdus() Coverity complains that a constant variable guards dead code. In fact, mpdu_buf is set NULL and never updated. 4834err_merge_fail: null: At condition mpdu_buf, the value of mpdu_buf must be NULL. dead_error_condition: The condition mpdu_buf cannot be true. CID 92162 (#1 of 1): 'Constant' variable guards dead code (DEADCODE) dead_error_line: Execution cannot reach the expression decap_format != DP_RX_DECAP_TYPE_RAW inside this statement: if (mpdu_buf && decap_forma.... Local variable mpdu_buf is assigned only once, to a constant value, making it effectively constant throughout its scope. If this is not the intent, examine the logic to see if there is a missing assignment that would make mpdu_buf not remain constant. 4835 if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { Fix this by removing mpdu_buf and unreachable code. Cc: Kalle Valo Cc: "David S. Miller" Cc: Jakub Kicinski Cc: ath11k@lists.infradead.org Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Tim Gardner Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210927150743.19816-1-tim.gardner@canonical.com --- drivers/net/wireless/ath/ath11k/dp_rx.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 75f6d55dca46..1232bbea4b65 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4828,7 +4828,7 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, struct ieee80211_rx_status *rxs) { struct ath11k_base *ab = ar->ab; - struct sk_buff *msdu, *mpdu_buf, *prev_buf; + struct sk_buff *msdu, *prev_buf; u32 wifi_hdr_len; struct hal_rx_desc *rx_desc; char *hdr_desc; @@ -4836,8 +4836,6 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, struct ieee80211_hdr_3addr *wh; struct rx_attention *rx_attention; - mpdu_buf = NULL; - if (!head_msdu) goto err_merge_fail; @@ -4920,12 +4918,6 @@ ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, return head_msdu; err_merge_fail: - if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { - ath11k_dbg(ab, ATH11K_DBG_DATA, - "err_merge_fail mpdu_buf %pK", mpdu_buf); - /* Free the head buffer */ - dev_kfree_skb_any(mpdu_buf); - } return NULL; } From 567ec33a76c7d8e7fbd7a73c81dd16b9efc7ae6d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Oct 2021 09:32:17 +0100 Subject: [PATCH 06/16] ath11k: Fix spelling mistake "incompaitiblity" -> "incompatibility" There is a spelling mistake in an ath11k_warn message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211006083217.349596-1-colin.king@canonical.com --- drivers/net/wireless/ath/ath11k/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 31f0cfba5bf5..89ab2fa7557c 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7081,7 +7081,7 @@ ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, if (!ath11k_mac_validate_vht_he_fixed_rate_settings(ar, band, mask)) ath11k_warn(ar->ab, - "could not update fixed rate settings to all peers due to mcs/nss incompaitiblity\n"); + "could not update fixed rate settings to all peers due to mcs/nss incompatibility\n"); nss = min_t(u32, ar->num_tx_chains, max(max(ath11k_mac_max_ht_nss(ht_mcs_mask), ath11k_mac_max_vht_nss(vht_mcs_mask)), From 4f50bdfb4e5fc3d753c8cf94b94b43aaa2c49b95 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 7 Oct 2021 18:16:24 +0300 Subject: [PATCH 07/16] ath11k: Remove redundant assignment to variable fw_size Variable fw_size is being assigned a value that is never read and being re-assigned a new value in the next statement. The assignment is redundant and can be removed. Addresses-Coverity: ("Unused value") Fixes: 336e7b53c82f ("ath11k: clean up BDF download functions") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211006105529.1011239-1-colin.king@canonical.com --- drivers/net/wireless/ath/ath11k/qmi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 8c615bc788ca..fa73118de6db 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -2135,7 +2135,6 @@ static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi bdf_type %d\n", bdf_type); - fw_size = bd.len; fw_size = min_t(u32, ab->hw_params.fw.board_size, bd.len); ret = ath11k_qmi_load_file_target_mem(ab, bd.data, fw_size, bdf_type); From ec4363384c3f110561dc5ee2e59adee02dbd9f73 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 7 Oct 2021 18:16:24 +0300 Subject: [PATCH 08/16] ath11k: Use kcalloc() instead of kzalloc() Use 2-factor multiplication argument form kcalloc() instead of kzalloc(). Link: https://github.com/KSPP/linux/issues/162 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211006181204.GA913553@embeddedor --- drivers/net/wireless/ath/ath11k/wmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 2d0acfb748cf..8961c355a81b 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4065,8 +4065,8 @@ static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc, len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities)); if (!svc_rdy_ext->n_mac_phy_caps) { - svc_rdy_ext->mac_phy_caps = kzalloc((svc_rdy_ext->tot_phy_id) * len, - GFP_ATOMIC); + svc_rdy_ext->mac_phy_caps = kcalloc(svc_rdy_ext->tot_phy_id, + len, GFP_ATOMIC); if (!svc_rdy_ext->mac_phy_caps) return -ENOMEM; } @@ -4466,8 +4466,8 @@ static struct cur_reg_rule struct cur_reg_rule *reg_rule_ptr; u32 count; - reg_rule_ptr = kzalloc((num_reg_rules * sizeof(*reg_rule_ptr)), - GFP_ATOMIC); + reg_rule_ptr = kcalloc(num_reg_rules, sizeof(*reg_rule_ptr), + GFP_ATOMIC); if (!reg_rule_ptr) return NULL; From 4b6012a7830b813799a7faf40daa02a837e0fd5b Mon Sep 17 00:00:00 2001 From: Tuo Li Date: Thu, 5 Aug 2021 08:38:53 -0700 Subject: [PATCH 09/16] ath: dfs_pattern_detector: Fix possible null-pointer dereference in channel_detector_create() kzalloc() is used to allocate memory for cd->detectors, and if it fails, channel_detector_exit() behind the label fail will be called: channel_detector_exit(dpd, cd); In channel_detector_exit(), cd->detectors is dereferenced through: struct pri_detector *de = cd->detectors[i]; To fix this possible null-pointer dereference, check cd->detectors before the for loop to dereference cd->detectors. Reported-by: TOTE Robot Signed-off-by: Tuo Li Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210805153854.154066-1-islituo@gmail.com --- drivers/net/wireless/ath/dfs_pattern_detector.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 80390495ea25..75cb53a3ec15 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -183,10 +183,12 @@ static void channel_detector_exit(struct dfs_pattern_detector *dpd, if (cd == NULL) return; list_del(&cd->head); - for (i = 0; i < dpd->num_radar_types; i++) { - struct pri_detector *de = cd->detectors[i]; - if (de != NULL) - de->exit(de); + if (cd->detectors) { + for (i = 0; i < dpd->num_radar_types; i++) { + struct pri_detector *de = cd->detectors[i]; + if (de != NULL) + de->exit(de); + } } kfree(cd->detectors); kfree(cd); From 96527d527b271d950367ad13e3de8b0673545622 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 11 Oct 2021 09:33:08 +0300 Subject: [PATCH 10/16] ath11k: Handle MSI enablement during rmmod and SSR When doing "rmmod ath11k_pci", ath11k performs global SOC reset and MHI reset, where 0 address access is captured by IOMMU. See log below: ... [ 133.953860] ath11k_pci 0000:02:00.0: setting mhi state: DEINIT(1) [ 133.959714] ath11k_pci 0000:02:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x000a address=0x0 flags=0x0020] [ 133.973854] ath11k_pci 0000:02:00.0: MHISTATUS 0xff04 [ 133.974095] ath11k_pci 0000:02:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x000a address=0x0 flags=0x0020] ... This issue is also observed in SSR process, cause a similar sequence as above is performed. Such an invalid access occurs because, during rmmod or SSR, MSI address is cleared but HW MSI functionality not disabled, thus HW target is able to raise an MSI transaction with 0 as MSI address. So it can be fixed by simply disabling MSI before reset. For SSR, since MSI functionality is still needed after target is brought back, we need to reenable it. Also change naming of some interfaces related. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Baochen Qiang Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210913180246.193388-5-jouni@codeaurora.org --- drivers/net/wireless/ath/ath11k/pci.c | 41 +++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 7b3bce0ba76e..3d353e7c9d5c 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -855,7 +855,32 @@ static void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab) } } -static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci) +static void ath11k_pci_msi_config(struct ath11k_pci *ab_pci, bool enable) +{ + struct pci_dev *dev = ab_pci->pdev; + u16 control; + + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); + + if (enable) + control |= PCI_MSI_FLAGS_ENABLE; + else + control &= ~PCI_MSI_FLAGS_ENABLE; + + pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); +} + +static void ath11k_pci_msi_enable(struct ath11k_pci *ab_pci) +{ + ath11k_pci_msi_config(ab_pci, true); +} + +static void ath11k_pci_msi_disable(struct ath11k_pci *ab_pci) +{ + ath11k_pci_msi_config(ab_pci, false); +} + +static int ath11k_pci_alloc_msi(struct ath11k_pci *ab_pci) { struct ath11k_base *ab = ab_pci->ab; const struct ath11k_msi_config *msi_config = ab_pci->msi_config; @@ -876,6 +901,7 @@ static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci) else return num_vectors; } + ath11k_pci_msi_disable(ab_pci); msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); if (!msi_desc) { @@ -898,7 +924,7 @@ free_msi_vector: return ret; } -static void ath11k_pci_disable_msi(struct ath11k_pci *ab_pci) +static void ath11k_pci_free_msi(struct ath11k_pci *ab_pci) { pci_free_irq_vectors(ab_pci->pdev); } @@ -1019,6 +1045,8 @@ static int ath11k_pci_power_up(struct ath11k_base *ab) */ ath11k_pci_aspm_disable(ab_pci); + ath11k_pci_msi_enable(ab_pci); + ret = ath11k_mhi_start(ab_pci); if (ret) { ath11k_err(ab, "failed to start mhi: %d\n", ret); @@ -1039,6 +1067,9 @@ static void ath11k_pci_power_down(struct ath11k_base *ab) ath11k_pci_aspm_restore(ab_pci); ath11k_pci_force_wake(ab_pci->ab); + + ath11k_pci_msi_disable(ab_pci); + ath11k_mhi_stop(ab_pci); clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags); ath11k_pci_sw_reset(ab_pci->ab, false); @@ -1263,7 +1294,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev, goto err_pci_free_region; } - ret = ath11k_pci_enable_msi(ab_pci); + ret = ath11k_pci_alloc_msi(ab_pci); if (ret) { ath11k_err(ab, "failed to enable msi: %d\n", ret); goto err_pci_free_region; @@ -1317,7 +1348,7 @@ err_mhi_unregister: ath11k_mhi_unregister(ab_pci); err_pci_disable_msi: - ath11k_pci_disable_msi(ab_pci); + ath11k_pci_free_msi(ab_pci); err_pci_free_region: ath11k_pci_free_region(ab_pci); @@ -1348,7 +1379,7 @@ qmi_fail: ath11k_mhi_unregister(ab_pci); ath11k_pci_free_irq(ab); - ath11k_pci_disable_msi(ab_pci); + ath11k_pci_free_msi(ab_pci); ath11k_pci_free_region(ab_pci); ath11k_hal_srng_deinit(ab); From 31582373a4a8e888b29ed759d28330a1995f2162 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 11 Oct 2021 09:33:09 +0300 Subject: [PATCH 11/16] ath11k: Change number of TCL rings to one for QCA6390 Some targets, QCA6390 for example, use only one TCL ring, it is better to initialize only one ring and leave others untouched for such targets. This is a theoretical fix found during code review, no visible impact. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Signed-off-by: Baochen Qiang Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20210914163726.38604-1-jouni@codeaurora.org --- drivers/net/wireless/ath/ath11k/core.c | 10 +++++----- drivers/net/wireless/ath/ath11k/debugfs.c | 2 +- drivers/net/wireless/ath/ath11k/dp.c | 10 +++++----- drivers/net/wireless/ath/ath11k/dp.h | 1 + drivers/net/wireless/ath/ath11k/dp_tx.c | 13 +++++-------- drivers/net/wireless/ath/ath11k/hw.h | 2 +- drivers/net/wireless/ath/ath11k/mac.c | 2 +- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 2328e594a96a..b0d6922802ed 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -58,7 +58,6 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, - .tcl_0_only = false, .spectral = { .fft_sz = 2, @@ -81,6 +80,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, }, { .hw_rev = ATH11K_HW_IPQ6018_HW10, @@ -109,7 +109,6 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, - .tcl_0_only = false, .spectral = { .fft_sz = 4, @@ -129,6 +128,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, }, { .name = "qca6390 hw2.0", @@ -157,7 +157,6 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, - .tcl_0_only = true, .spectral = { .fft_sz = 0, @@ -176,6 +175,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, }, { .name = "qcn9074 hw1.0", @@ -203,7 +203,6 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .rx_mac_buf_ring = false, .vdev_start_delay = false, .htt_peer_map_v2 = true, - .tcl_0_only = false, .spectral = { .fft_sz = 2, @@ -223,6 +222,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), .fix_l1ss = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX, }, { .name = "wcn6855 hw2.0", @@ -251,7 +251,6 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .rx_mac_buf_ring = true, .vdev_start_delay = true, .htt_peer_map_v2 = false, - .tcl_0_only = true, .spectral = { .fft_sz = 0, @@ -270,6 +269,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), .fix_l1ss = false, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, }, }; diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 17f0bbbac7ae..80afd35337a1 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -806,7 +806,7 @@ static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file, len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n"); len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n"); - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + for (i = 0; i < ab->hw_params.max_tx_ring; i++) len += scnprintf(buf + len, size - len, "ring%d: %u\n", i, soc_stats->tx_err.desc_na[i]); diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index b0c8f6290099..c52d49cb8a04 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -311,7 +311,7 @@ void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab) if (!ab->hw_params.supports_shadow_regs) return; - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + for (i = 0; i < ab->hw_params.max_tx_ring; i++) ath11k_dp_shadow_stop_timer(ab, &ab->dp.tx_ring_timer[i]); ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer); @@ -326,7 +326,7 @@ static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); } @@ -366,7 +366,7 @@ static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) goto err; } - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_data_ring, HAL_TCL_DATA, i, 0, DP_TCL_DATA_RING_SIZE); @@ -996,7 +996,7 @@ void ath11k_dp_free(struct ath11k_base *ab) ath11k_dp_reo_cmd_list_cleanup(ab); - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + for (i = 0; i < ab->hw_params.max_tx_ring; i++) { spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); idr_for_each(&dp->tx_ring[i].txbuf_idr, ath11k_dp_tx_pending_cleanup, ab); @@ -1046,7 +1046,7 @@ int ath11k_dp_alloc(struct ath11k_base *ab) size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + for (i = 0; i < ab->hw_params.max_tx_ring; i++) { idr_init(&dp->tx_ring[i].txbuf_idr); spin_lock_init(&dp->tx_ring[i].tx_idr_lock); dp->tx_ring[i].tcl_data_ring_id = i; diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index d6267bfa0264..4794ca04f213 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -170,6 +170,7 @@ struct ath11k_pdev_dp { #define DP_BA_WIN_SZ_MAX 256 #define DP_TCL_NUM_RING_MAX 3 +#define DP_TCL_NUM_RING_MAX_QCA6390 1 #define DP_IDLE_SCATTER_BUFS_MAX 16 diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 70d2cf010a68..879fb2a9dc0c 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -115,11 +115,8 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, tcl_ring_sel: tcl_ring_retry = false; - /* For some chip, it can only use tcl0 to tx */ - if (ar->ab->hw_params.tcl_0_only) - ti.ring_id = 0; - else - ti.ring_id = ring_selector % DP_TCL_NUM_RING_MAX; + + ti.ring_id = ring_selector % ab->hw_params.max_tx_ring; ring_map |= BIT(ti.ring_id); @@ -131,7 +128,7 @@ tcl_ring_sel: spin_unlock_bh(&tx_ring->tx_idr_lock); if (ret < 0) { - if (ring_map == (BIT(DP_TCL_NUM_RING_MAX) - 1)) { + if (ring_map == (BIT(ab->hw_params.max_tx_ring) - 1)) { atomic_inc(&ab->soc_stats.tx_err.misc_fail); return -ENOSPC; } @@ -248,8 +245,8 @@ tcl_ring_sel: * checking this ring earlier for each pkt tx. * Restart ring selection if some rings are not checked yet. */ - if (ring_map != (BIT(DP_TCL_NUM_RING_MAX) - 1) && - !ar->ab->hw_params.tcl_0_only) { + if (ring_map != (BIT(ab->hw_params.max_tx_ring) - 1) && + ab->hw_params.max_tx_ring > 1) { tcl_ring_retry = true; ring_selector++; } diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 1535075eed03..8f92210edc8f 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -152,7 +152,6 @@ struct ath11k_hw_params { bool rx_mac_buf_ring; bool vdev_start_delay; bool htt_peer_map_v2; - bool tcl_0_only; struct { u8 fft_sz; @@ -170,6 +169,7 @@ struct ath11k_hw_params { bool supports_suspend; u32 hal_desc_sz; bool fix_l1ss; + u8 max_tx_ring; }; struct ath11k_hw_ops { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 89ab2fa7557c..791c96b283e3 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5797,7 +5797,7 @@ err_vdev_del: idr_for_each(&ar->txmgmt_idr, ath11k_mac_vif_txmgmt_idr_remove, vif); - for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + for (i = 0; i < ab->hw_params.max_tx_ring; i++) { spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); idr_for_each(&ab->dp.tx_ring[i].txbuf_idr, ath11k_mac_vif_unref, vif); From 03469e79fee9e8e908dae3bd1a80bcd9a66f2a88 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 11 Oct 2021 18:18:00 +0300 Subject: [PATCH 12/16] ath9k: support DT ieee80211-freq-limit property to limit channels The common DT property can be used to limit the available channels but ath9k has to manually call wiphy_read_of_freq_limits(). I would have put this into ath9k_of_init(). But it didn't work there. The reason is that in ath9k_of_init() the channels and bands are not yet registered in the wiphy struct. So there isn't any channel to flag as disabled. Signed-off-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211009212847.1781986-1-chunkeey@gmail.com --- drivers/net/wireless/ath/ath9k/init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 1568730fc01e..0344d6f27275 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -1094,6 +1094,8 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, ARRAY_SIZE(ath9k_tpt_blink)); #endif + wiphy_read_of_freq_limits(hw->wiphy); + /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) From 8cd5c0847160aa9482d7f93ed63c4d72bad70cdf Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 11 Oct 2021 18:18:00 +0300 Subject: [PATCH 13/16] ath11k: Identify DFS channel when sending scan channel list command WMI_CHAN_INFO_DFS flag should be set when configuring a DFS channel included in scan channel list. Without it, firmware will not send a probe request frame which is needed in connection to an AP configured with hidden SSID/network_id. So fix this to allow probe request frames to be sent in cases where a beacon frame has been seen on the channel first. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Baochen Qiang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211011054919.77071-1-bqiang@codeaurora.org --- drivers/net/wireless/ath/ath11k/wmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 8961c355a81b..5ae2ef4680d6 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2371,6 +2371,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE; if (tchan_info->psc_channel) chan_info->info |= WMI_CHAN_INFO_PSC; + if (tchan_info->dfs_set) + chan_info->info |= WMI_CHAN_INFO_DFS; chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, tchan_info->phy_mode); From 57671351379b2051cfb07fc14e0bead9916a0880 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 11 Oct 2021 18:18:01 +0300 Subject: [PATCH 14/16] ath9k: fix an IS_ERR() vs NULL check The devm_kmemdup() function doesn't return error pointers, it returns NULL on error. Fixes: eb3a97a69be8 ("ath9k: fetch calibration data via nvmem subsystem") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20211011123533.GA15188@kili --- drivers/net/wireless/ath/ath9k/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0344d6f27275..4f00400c7ffb 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -610,8 +610,8 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) /* devres manages the calibration values release on shutdown */ ah->nvmem_blob = (u16 *)devm_kmemdup(sc->dev, buf, len, GFP_KERNEL); kfree(buf); - if (IS_ERR(ah->nvmem_blob)) - return PTR_ERR(ah->nvmem_blob); + if (!ah->nvmem_blob) + return -ENOMEM; ah->nvmem_blob_len = len; ah->ah_flags &= ~AH_USE_EEPROM; From 0a491167fe0cf9f26062462de2a8688b96125d48 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 11 Jun 2019 19:21:31 +0200 Subject: [PATCH 15/16] ath10k: fix max antenna gain unit Most of the txpower for the ath10k firmware is stored as twicepower (0.5 dB steps). This isn't the case for max_antenna_gain - which is still expected by the firmware as dB. The firmware is converting it from dB to the internal (twicepower) representation when it calculates the limits of a channel. This can be seen in tpc_stats when configuring "12" as max_antenna_gain. Instead of the expected 12 (6 dB), the tpc_stats shows 24 (12 dB). Tested on QCA9888 and IPQ4019 with firmware 10.4-3.5.3-00057. Fixes: 02256930d9b8 ("ath10k: use proper tx power unit") Signed-off-by: Sven Eckelmann Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20190611172131.6064-1-sven@narfation.org --- drivers/net/wireless/ath/ath10k/mac.c | 6 +++--- drivers/net/wireless/ath/ath10k/wmi.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 7ca68c81d9b6..5ec19d91cf37 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1052,7 +1052,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) arg.channel.min_power = 0; arg.channel.max_power = channel->max_power * 2; arg.channel.max_reg_power = channel->max_reg_power * 2; - arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; + arg.channel.max_antenna_gain = channel->max_antenna_gain; reinit_completion(&ar->vdev_setup_done); reinit_completion(&ar->vdev_delete_done); @@ -1498,7 +1498,7 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, arg.channel.min_power = 0; arg.channel.max_power = chandef->chan->max_power * 2; arg.channel.max_reg_power = chandef->chan->max_reg_power * 2; - arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2; + arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain; if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { arg.ssid = arvif->u.ap.ssid; @@ -3426,7 +3426,7 @@ static int ath10k_update_channel_list(struct ath10k *ar) ch->min_power = 0; ch->max_power = channel->max_power * 2; ch->max_reg_power = channel->max_reg_power * 2; - ch->max_antenna_gain = channel->max_antenna_gain * 2; + ch->max_antenna_gain = channel->max_antenna_gain; ch->reg_class_id = 0; /* FIXME */ /* FIXME: why use only legacy modes, why not any diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 41c1a3d339c2..01bfd09a9d88 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2066,7 +2066,9 @@ struct wmi_channel { union { __le32 reginfo1; struct { + /* note: power unit is 1 dBm */ u8 antenna_max; + /* note: power unit is 0.5 dBm */ u8 max_tx_power; } __packed; } __packed; @@ -2086,6 +2088,7 @@ struct wmi_channel_arg { u32 min_power; u32 max_power; u32 max_reg_power; + /* note: power unit is 1 dBm */ u32 max_antenna_gain; u32 reg_class_id; enum wmi_phy_mode mode; From 65b4b8aa0f59ec34508b262cdd0f8d731a09a074 Mon Sep 17 00:00:00 2001 From: Qing Wang Date: Tue, 12 Oct 2021 20:27:31 -0700 Subject: [PATCH 16/16] ath5k: replace snprintf in show functions with sysfs_emit coccicheck complains about the use of snprintf() in sysfs show functions. Fix the coccicheck warning: WARNING: use scnprintf or sprintf. Use sysfs_emit instead of scnprintf or sprintf makes more sense. Signed-off-by: Qing Wang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1634095651-4273-1-git-send-email-wangqing@vivo.com --- drivers/net/wireless/ath/ath5k/sysfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/sysfs.c b/drivers/net/wireless/ath/ath5k/sysfs.c index 8113baddd8fc..37bf64106473 100644 --- a/drivers/net/wireless/ath/ath5k/sysfs.c +++ b/drivers/net/wireless/ath/ath5k/sysfs.c @@ -14,7 +14,7 @@ static ssize_t ath5k_attr_show_##name(struct device *dev, \ { \ struct ieee80211_hw *hw = dev_get_drvdata(dev); \ struct ath5k_hw *ah = hw->priv; \ - return snprintf(buf, PAGE_SIZE, "%d\n", get); \ + return sysfs_emit(buf, "%d\n", get); \ } \ \ static ssize_t ath5k_attr_store_##name(struct device *dev, \ @@ -41,7 +41,7 @@ static ssize_t ath5k_attr_show_##name(struct device *dev, \ { \ struct ieee80211_hw *hw = dev_get_drvdata(dev); \ struct ath5k_hw *ah = hw->priv; \ - return snprintf(buf, PAGE_SIZE, "%d\n", get); \ + return sysfs_emit(buf, "%d\n", get); \ } \ static DEVICE_ATTR(name, 0444, ath5k_attr_show_##name, NULL) @@ -64,7 +64,7 @@ static ssize_t ath5k_attr_show_noise_immunity_level_max(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", ATH5K_ANI_MAX_NOISE_IMM_LVL); + return sysfs_emit(buf, "%d\n", ATH5K_ANI_MAX_NOISE_IMM_LVL); } static DEVICE_ATTR(noise_immunity_level_max, 0444, ath5k_attr_show_noise_immunity_level_max, NULL); @@ -73,7 +73,7 @@ static ssize_t ath5k_attr_show_firstep_level_max(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", ATH5K_ANI_MAX_FIRSTEP_LVL); + return sysfs_emit(buf, "%d\n", ATH5K_ANI_MAX_FIRSTEP_LVL); } static DEVICE_ATTR(firstep_level_max, 0444, ath5k_attr_show_firstep_level_max, NULL);