mac80211: ath11k: hack for multiple card support
authorRobert Marko <robimarko@gmail.com>
Thu, 15 Dec 2022 12:11:01 +0000 (13:11 +0100)
committerRobert Marko <robimarko@gmail.com>
Mon, 16 Jan 2023 11:42:23 +0000 (12:42 +0100)
This is a temporary workaround for supporting multiple cards or AHB+PCI.

There is ongoing upstream work to properly support this based of
advertised FW features, but that is still ongoing.

This is only supported on QCN9074 cards due to FW limitation, so HW ID
is checked in order to prevent breaking QCA6390 and other popular cards.

Signed-off-by: Robert Marko <robimarko@gmail.com>
package/kernel/mac80211/patches/ath11k/0021-wifi-ath11k-debugfs-fix-to-work-with-multiple-PCI-de.patch [new file with mode: 0644]
package/kernel/mac80211/patches/ath11k/100-wifi-ath11k-use-unique-QRTR-instance-ID.patch [new file with mode: 0644]
package/kernel/mac80211/patches/ath11k/901-wifi-ath11k-pci-fix-compilation-in-5.16-and-older.patch

diff --git a/package/kernel/mac80211/patches/ath11k/0021-wifi-ath11k-debugfs-fix-to-work-with-multiple-PCI-de.patch b/package/kernel/mac80211/patches/ath11k/0021-wifi-ath11k-debugfs-fix-to-work-with-multiple-PCI-de.patch
new file mode 100644 (file)
index 0000000..bd72b63
--- /dev/null
@@ -0,0 +1,139 @@
+From 323d91d4684d238f6bc3693fed93caf795378fe0 Mon Sep 17 00:00:00 2001
+From: Kalle Valo <quic_kvalo@quicinc.com>
+Date: Thu, 22 Dec 2022 19:15:59 +0200
+Subject: [PATCH] wifi: ath11k: debugfs: fix to work with multiple PCI devices
+
+ath11k fails to load if there are multiple ath11k PCI devices with same name:
+
+ ath11k_pci 0000:01:00.0: Hardware name qcn9074 hw1.0
+ debugfs: Directory 'ath11k' with parent '/' already present!
+ ath11k_pci 0000:01:00.0: failed to create ath11k debugfs
+ ath11k_pci 0000:01:00.0: failed to create soc core: -17
+ ath11k_pci 0000:01:00.0: failed to init core: -17
+ ath11k_pci: probe of 0000:01:00.0 failed with error -17
+
+Fix this by creating a directory for each ath11k device using schema
+<bus>-<devname>, for example "pci-0000:06:00.0". This directory created under
+the top-level ath11k directory, for example /sys/kernel/debug/ath11k.
+
+The reference to the toplevel ath11k directory is not stored anymore within ath11k, instead
+it's retrieved using debugfs_lookup(). If the directory does not exist it will
+be created. After the last directory from the ath11k directory is removed, for
+example when doing rmmod ath11k, the empty ath11k directory is left in place,
+it's a minor cosmetic issue anyway.
+
+Here's an example hierarchy with one WCN6855:
+
+ath11k
+`-- pci-0000:06:00.0
+    |-- mac0
+    |   |-- dfs_block_radar_events
+    |   |-- dfs_simulate_radar
+    |   |-- ext_rx_stats
+    |   |-- ext_tx_stats
+    |   |-- fw_dbglog_config
+    |   |-- fw_stats
+    |   |   |-- beacon_stats
+    |   |   |-- pdev_stats
+    |   |   `-- vdev_stats
+    |   |-- htt_stats
+    |   |-- htt_stats_reset
+    |   |-- htt_stats_type
+    |   `-- pktlog_filter
+    |-- simulate_fw_crash
+    `-- soc_dp_stats
+
+I didn't have a test setup where I could connect multiple ath11k devices to the
+same the host, so I have only tested this with one device.
+
+Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.9
+Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
+Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
+
+Tested-by: Robert Marko <robert.marko@sartura.hr>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+Link: https://lore.kernel.org/r/20221220121231.20120-1-kvalo@kernel.org
+---
+ drivers/net/wireless/ath/ath11k/core.h    |  1 -
+ drivers/net/wireless/ath/ath11k/debugfs.c | 48 +++++++++++++++++++----
+ 2 files changed, 40 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath11k/core.h
++++ b/drivers/net/wireless/ath/ath11k/core.h
+@@ -912,7 +912,6 @@ struct ath11k_base {
+       enum ath11k_dfs_region dfs_region;
+ #ifdef CPTCFG_ATH11K_DEBUGFS
+       struct dentry *debugfs_soc;
+-      struct dentry *debugfs_ath11k;
+ #endif
+       struct ath11k_soc_dp_stats soc_stats;
+--- a/drivers/net/wireless/ath/ath11k/debugfs.c
++++ b/drivers/net/wireless/ath/ath11k/debugfs.c
+@@ -976,10 +976,6 @@ int ath11k_debugfs_pdev_create(struct at
+       if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
+               return 0;
+-      ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k);
+-      if (IS_ERR(ab->debugfs_soc))
+-              return PTR_ERR(ab->debugfs_soc);
+-
+       debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,
+                           &fops_simulate_fw_crash);
+@@ -1001,15 +997,51 @@ void ath11k_debugfs_pdev_destroy(struct
+ int ath11k_debugfs_soc_create(struct ath11k_base *ab)
+ {
+-      ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL);
++      struct dentry *root;
++      bool dput_needed;
++      char name[64];
++      int ret;
++
++      root = debugfs_lookup("ath11k", NULL);
++      if (!root) {
++              root = debugfs_create_dir("ath11k", NULL);
++              if (IS_ERR_OR_NULL(root))
++                      return PTR_ERR(root);
++
++              dput_needed = false;
++      } else {
++              /* a dentry from lookup() needs dput() after we don't use it */
++              dput_needed = true;
++      }
++
++      scnprintf(name, sizeof(name), "%s-%s", ath11k_bus_str(ab->hif.bus),
++                dev_name(ab->dev));
++
++      ab->debugfs_soc = debugfs_create_dir(name, root);
++      if (IS_ERR_OR_NULL(ab->debugfs_soc)) {
++              ret = PTR_ERR(ab->debugfs_soc);
++              goto out;
++      }
++
++      ret = 0;
++
++out:
++      if (dput_needed)
++              dput(root);
+-      return PTR_ERR_OR_ZERO(ab->debugfs_ath11k);
++      return ret;
+ }
+ void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)
+ {
+-      debugfs_remove_recursive(ab->debugfs_ath11k);
+-      ab->debugfs_ath11k = NULL;
++      debugfs_remove_recursive(ab->debugfs_soc);
++      ab->debugfs_soc = NULL;
++
++      /* We are not removing ath11k directory on purpose, even if it
++       * would be empty. This simplifies the directory handling and it's
++       * a minor cosmetic issue to leave an empty ath11k directory to
++       * debugfs.
++       */
+ }
+ EXPORT_SYMBOL(ath11k_debugfs_soc_destroy);
diff --git a/package/kernel/mac80211/patches/ath11k/100-wifi-ath11k-use-unique-QRTR-instance-ID.patch b/package/kernel/mac80211/patches/ath11k/100-wifi-ath11k-use-unique-QRTR-instance-ID.patch
new file mode 100644 (file)
index 0000000..39d5a61
--- /dev/null
@@ -0,0 +1,162 @@
+From 534a5f99d589cfa6b244b4433c192b6a278a67ff Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Sat, 5 Nov 2022 20:15:40 +0100
+Subject: [PATCH] wifi: ath11k: use unique QRTR instance ID
+
+Currently, trying to use AHB + PCI/MHI cards or multiple PCI/MHI cards
+will cause a clash in the QRTR instance node ID and prevent the driver
+from talking via QMI to the card and thus initializing it with:
+[    9.836329] ath11k c000000.wifi: host capability request failed: 1 90
+[    9.842047] ath11k c000000.wifi: failed to send qmi host cap: -22
+
+So, in order to allow for this combination of cards, especially AHB + PCI
+cards like IPQ8074 + QCN9074 (Used by me and tested on) set the desired
+QRTR instance ID offset by calculating a unique one based on PCI domain
+and bus ID-s and writing it to bits 7-0 of BHI_ERRDBG2 MHI register by
+using the SBL state callback that is added as part of the series.
+We also have to make sure that new QRTR offset is added on top of the
+default QRTR instance ID-s that are currently used in the driver.
+
+This finally allows using AHB + PCI or multiple PCI cards on the same
+system.
+
+Since this is not supported on QCA6390 and like, its limited to QCN9074
+which is known to support changing QRTR instance ID.
+
+Before:
+root@OpenWrt:/# qrtr-lookup
+  Service Version Instance Node  Port
+     1054       1        0    7     1 <unknown>
+       69       1        2    7     3 ATH10k WLAN firmware service
+
+After:
+root@OpenWrt:/# qrtr-lookup
+  Service Version Instance Node  Port
+     1054       1        0    7     1 <unknown>
+       69       1        2    7     3 ATH10k WLAN firmware service
+       15       1        0    8     1 Test service
+       69       1        8    8     2 ATH10k WLAN firmware service
+
+Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
+Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+---
+ drivers/net/wireless/ath/ath11k/mhi.c | 49 ++++++++++++++++++---------
+ drivers/net/wireless/ath/ath11k/mhi.h |  3 ++
+ drivers/net/wireless/ath/ath11k/pci.c |  9 ++++-
+ 3 files changed, 44 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath11k/mhi.c
++++ b/drivers/net/wireless/ath/ath11k/mhi.c
+@@ -294,6 +294,34 @@ static void ath11k_mhi_op_runtime_put(st
+ {
+ }
++static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
++                                void __iomem *addr,
++                                u32 *out)
++{
++      *out = readl(addr);
++
++      return 0;
++}
++
++static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
++                                  void __iomem *addr,
++                                  u32 val)
++{
++      writel(val, addr);
++}
++
++static void ath11k_mhi_qrtr_instance_set(struct mhi_controller *mhi_cntrl)
++{
++      struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
++
++      if (ab->hw_rev == ATH11K_HW_QCN9074_HW10) {
++              ath11k_mhi_op_write_reg(mhi_cntrl,
++                                      mhi_cntrl->bhi + BHI_ERRDBG2,
++                                      FIELD_PREP(QRTR_INSTANCE_MASK,
++                                      ab->qmi.service_ins_id - ab->hw_params.qmi_service_ins_id));
++      }
++}
++
+ static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason)
+ {
+       switch (reason) {
+@@ -315,6 +343,8 @@ static char *ath11k_mhi_op_callback_to_s
+               return "MHI_CB_FATAL_ERROR";
+       case MHI_CB_BW_REQ:
+               return "MHI_CB_BW_REQ";
++      case MHI_CB_EE_SBL_MODE:
++              return "MHI_CB_EE_SBL_MODE";
+       default:
+               return "UNKNOWN";
+       }
+@@ -336,27 +366,14 @@ static void ath11k_mhi_op_status_cb(stru
+               if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
+                       queue_work(ab->workqueue_aux, &ab->reset_work);
+               break;
++      case MHI_CB_EE_SBL_MODE:
++              ath11k_mhi_qrtr_instance_set(mhi_cntrl);
++              break;
+       default:
+               break;
+       }
+ }
+-static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
+-                                void __iomem *addr,
+-                                u32 *out)
+-{
+-      *out = readl(addr);
+-
+-      return 0;
+-}
+-
+-static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
+-                                  void __iomem *addr,
+-                                  u32 val)
+-{
+-      writel(val, addr);
+-}
+-
+ static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl)
+ {
+       struct device_node *np;
+--- a/drivers/net/wireless/ath/ath11k/mhi.h
++++ b/drivers/net/wireless/ath/ath11k/mhi.h
+@@ -16,6 +16,9 @@
+ #define MHICTRL                                       0x38
+ #define MHICTRL_RESET_MASK                    0x2
++#define BHI_ERRDBG2                           0x38
++#define QRTR_INSTANCE_MASK                    GENMASK(7, 0)
++
+ int ath11k_mhi_start(struct ath11k_pci *ar_pci);
+ void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
+ int ath11k_mhi_register(struct ath11k_pci *ar_pci);
+--- a/drivers/net/wireless/ath/ath11k/pci.c
++++ b/drivers/net/wireless/ath/ath11k/pci.c
+@@ -370,13 +370,20 @@ static void ath11k_pci_sw_reset(struct a
+ static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab)
+ {
+       struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
++      struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
++      struct pci_bus *bus = ab_pci->pdev->bus;
+       cfg->tgt_ce = ab->hw_params.target_ce_config;
+       cfg->tgt_ce_len = ab->hw_params.target_ce_count;
+       cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map;
+       cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len;
+-      ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
++
++      if (ab->hw_rev == ATH11K_HW_QCN9074_HW10) {
++              ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id +
++              (((pci_domain_nr(bus) & 0xF) << 4) | (bus->number & 0xF));
++      } else
++              ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
+       ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2,
+                                   &cfg->shadow_reg_v2_len);
index 2b6c18d6dd9c78ac3f3b16d5fc81d43dc35f1a10..7215656389937027e15e2b17c758a42e8b07d767 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Robert Marko <robimarko@gmail.com>
 
 --- a/drivers/net/wireless/ath/ath11k/pci.c
 +++ b/drivers/net/wireless/ath/ath11k/pci.c
-@@ -451,7 +451,11 @@ static int ath11k_pci_alloc_msi(struct a
+@@ -458,7 +458,11 @@ static int ath11k_pci_alloc_msi(struct a
        pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO,
                              &ab->pci.msi.addr_lo);