summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2026-08-06 14:00:02 +0200
committerJohannes Berg <johannes.berg@intel.com>2026-08-06 14:00:22 +0200
commit0307efd32f29111a7040f544ca3ca23d58e80ff2 (patch)
treead9dbdfa3fa1cdd8a5ca1c389b205840f8b196b9
parent6c5fc504d0d6934132637aa3db4b9b58148eaa78 (diff)
parent4f25071afe9218aaae1c63fbf75e229aa6405319 (diff)
Merge tag 'ath-next-20260803' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath
Jeff Johnson says: ================== ath.git patches for v7.3 (PR #2) For ath12k, add MultiPD support for AHB platforms. Other than that, just an assortment of cleanups and minor bug fixes across ath6kl, ath10k, ath11k, and ath12k. ================== Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c9
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode.c14
-rw-r--r--drivers/net/wireless/ath/ath11k/ahb.c79
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c30
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.c209
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.h34
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c7
-rw-r--r--drivers/net/wireless/ath/ath12k/core.h11
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.c2
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.h3
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_tx.c46
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_tx.h2
-rw-r--r--drivers/net/wireless/ath/ath12k/hw.h1
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c2
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.c3
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.c8
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.h14
-rw-r--r--drivers/net/wireless/ath/ath12k/wifi7/ahb.c90
-rw-r--r--drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c64
-rw-r--r--drivers/net/wireless/ath/ath12k/wifi7/hw.c3
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c32
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c5
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c2
23 files changed, 550 insertions, 120 deletions
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 310650227578..33c98927e8fe 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -6,6 +6,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -1475,11 +1476,15 @@ static void ath10k_msa_dump_memory(struct ath10k *ar,
hdr->length = cpu_to_le32(ar->msa.mem_size);
if (current_region->len < ar->msa.mem_size) {
- memcpy(buf, ar->msa.vaddr, current_region->len);
+ memcpy_fromio(buf,
+ (const void __iomem __force *)ar->msa.vaddr,
+ current_region->len);
ath10k_warn(ar, "msa dump length is less than msa size %x, %x\n",
current_region->len, ar->msa.mem_size);
} else {
- memcpy(buf, ar->msa.vaddr, ar->msa.mem_size);
+ memcpy_fromio(buf,
+ (const void __iomem __force *)ar->msa.vaddr,
+ ar->msa.mem_size);
}
}
diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c
index d3bd385694d6..282ae6e20c8e 100644
--- a/drivers/net/wireless/ath/ath10k/testmode.c
+++ b/drivers/net/wireless/ath/ath10k/testmode.c
@@ -156,6 +156,14 @@ static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_b
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
}
+static bool ath10k_tm_is_utf_event(u32 cmd_id)
+{
+ return cmd_id == WMI_10X_PDEV_UTF_EVENTID ||
+ cmd_id == WMI_10_2_PDEV_UTF_EVENTID ||
+ cmd_id == WMI_10_4_PDEV_UTF_EVENTID ||
+ cmd_id == WMI_TLV_PDEV_UTF_EVENTID;
+}
+
/* Returns true if callee consumes the skb and the skb should be discarded.
* Returns false if skb is not used. Does not sleep.
*/
@@ -182,6 +190,12 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
*/
consumed = true;
+ if (!ath10k_tm_is_utf_event(cmd_id)) {
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+ "testmode drop non-utf event cmd_id %u\n", cmd_id);
+ goto out;
+ }
+
if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)
ath10k_tm_event_segmented(ar, cmd_id, skb);
else
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 1e1dea485760..ec5bf5c9fd79 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -431,36 +431,44 @@ static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab)
ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
}
-static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab)
+static void ath11k_ahb_free_ext_irq_grp(struct ath11k_base *ab,
+ struct ath11k_ext_irq_grp *irq_grp)
{
- int i, j;
-
- for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
- struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+ int j;
- for (j = 0; j < irq_grp->num_irq; j++)
- free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
+ for (j = 0; j < irq_grp->num_irq; j++)
+ free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
- netif_napi_del(&irq_grp->napi);
- free_netdev(irq_grp->napi_ndev);
- }
+ netif_napi_del(&irq_grp->napi);
+ free_netdev(irq_grp->napi_ndev);
}
-static void ath11k_ahb_free_irq(struct ath11k_base *ab)
+static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab)
{
- int irq_idx;
int i;
- if (ab->hw_params.hybrid_bus_type)
- return ath11k_pcic_free_irq(ab);
+ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++)
+ ath11k_ahb_free_ext_irq_grp(ab, &ab->ext_irq_grp[i]);
+}
- for (i = 0; i < ab->hw_params.ce_count; i++) {
+static void ath11k_ahb_free_ce_irqs(struct ath11k_base *ab, int max_idx)
+{
+ int irq_idx, i;
+
+ for (i = 0; i < max_idx; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
irq_idx = ATH11K_IRQ_CE0_OFFSET + i;
free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
}
+}
+
+static void ath11k_ahb_free_irq(struct ath11k_base *ab)
+{
+ if (ab->hw_params.hybrid_bus_type)
+ return ath11k_pcic_free_irq(ab);
+ ath11k_ahb_free_ce_irqs(ab, ab->hw_params.ce_count);
ath11k_ahb_free_ext_irq(ab);
}
@@ -524,20 +532,25 @@ static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg)
static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
{
struct ath11k_hw_params *hw = &ab->hw_params;
+ struct ath11k_ext_irq_grp *irq_grp;
int i, j;
int irq;
int ret;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
- struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
u32 num_irq = 0;
+ irq_grp = &ab->ext_irq_grp[i];
+
irq_grp->ab = ab;
irq_grp->grp_id = i;
irq_grp->napi_ndev = alloc_netdev_dummy(0);
- if (!irq_grp->napi_ndev)
- return -ENOMEM;
+ if (!irq_grp->napi_ndev) {
+ ret = -ENOMEM;
+ irq_grp->num_irq = 0;
+ goto err_request_irq;
+ }
netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
ath11k_ahb_ext_grp_napi_poll);
@@ -585,14 +598,11 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
}
}
}
- irq_grp->num_irq = num_irq;
-
- for (j = 0; j < irq_grp->num_irq; j++) {
+ for (j = 0; j < num_irq; j++) {
int irq_idx = irq_grp->irqs[j];
irq = platform_get_irq_byname(ab->pdev,
irq_name[irq_idx]);
- ab->irq_num[irq_idx] = irq;
irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY);
ret = request_irq(irq, ath11k_ahb_ext_interrupt_handler,
IRQF_TRIGGER_RISING,
@@ -600,11 +610,24 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
if (ret) {
ath11k_err(ab, "failed request_irq for %d\n",
irq);
+ irq_grp->num_irq = j;
+ ath11k_ahb_free_ext_irq_grp(ab, irq_grp);
+ goto err_request_irq;
}
+ ab->irq_num[irq_idx] = irq;
}
+
+ irq_grp->num_irq = num_irq;
}
return 0;
+
+err_request_irq:
+ for (i--; i >= 0; i--) {
+ irq_grp = &ab->ext_irq_grp[i];
+ ath11k_ahb_free_ext_irq_grp(ab, irq_grp);
+ }
+ return ret;
}
static int ath11k_ahb_config_irq(struct ath11k_base *ab)
@@ -629,16 +652,24 @@ static int ath11k_ahb_config_irq(struct ath11k_base *ab)
ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler,
IRQF_TRIGGER_RISING, irq_name[irq_idx],
ce_pipe);
- if (ret)
+ if (ret) {
+ ath11k_err(ab, "failed request_irq for %d\n", irq);
+ ath11k_ahb_free_ce_irqs(ab, i);
return ret;
+ }
ab->irq_num[irq_idx] = irq;
}
/* Configure external interrupts */
ret = ath11k_ahb_config_ext_irq(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to configure ext irq: %d\n", ret);
+ ath11k_ahb_free_ce_irqs(ab, ab->hw_params.ce_count);
+ return ret;
+ }
- return ret;
+ return 0;
}
static int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 4cbd7293845a..bbca275a8289 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -159,6 +159,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
.min_len = sizeof(struct ath11k_wmi_p2p_noa_info) },
[WMI_TAG_P2P_NOA_EVENT] = {
.min_len = sizeof(struct wmi_p2p_noa_event) },
+ [WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT] = {
+ .min_len = sizeof(struct wmi_pdev_csa_switch_ev) },
};
#define PRIMAP(_hw_mode_) \
@@ -262,6 +264,13 @@ const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab,
return tb;
}
+static u32 ath11k_wmi_tlv_data_len(const void *data)
+{
+ const struct wmi_tlv *tlv = (const struct wmi_tlv *)data - 1;
+
+ return FIELD_GET(WMI_TLV_LEN, tlv->header);
+}
+
static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
u32 cmd_id)
{
@@ -4800,14 +4809,16 @@ static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc,
if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id)
return -ENOBUFS;
- 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 = kcalloc(svc_rdy_ext->tot_phy_id,
- len, GFP_ATOMIC);
+ svc_rdy_ext->mac_phy_caps =
+ kzalloc_objs(*svc_rdy_ext->mac_phy_caps,
+ svc_rdy_ext->tot_phy_id,
+ GFP_ATOMIC);
if (!svc_rdy_ext->mac_phy_caps)
return -ENOMEM;
}
+ len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities));
memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len);
svc_rdy_ext->n_mac_phy_caps++;
return 0;
@@ -5124,6 +5135,7 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab,
return 0;
err:
+ kfree(svc_rdy_ext.mac_phy_caps);
ath11k_wmi_free_dbring_caps(ab);
return ret;
}
@@ -8359,15 +8371,23 @@ ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab,
const struct wmi_pdev_csa_switch_ev *ev,
const u32 *vdev_ids)
{
- int i;
+ u32 vdev_ids_len = ath11k_wmi_tlv_data_len(vdev_ids);
+ u32 num_vdevs = ev->num_vdevs;
struct ath11k_vif *arvif;
+ int i;
/* Finish CSA once the switch count becomes NULL */
if (ev->current_switch_count)
return;
+ if (num_vdevs > vdev_ids_len / sizeof(*vdev_ids)) {
+ ath11k_warn(ab, "csa switch count num_vdevs %u exceeds tlv array length %u\n",
+ num_vdevs, vdev_ids_len);
+ return;
+ }
+
rcu_read_lock();
- for (i = 0; i < ev->num_vdevs; i++) {
+ for (i = 0; i < num_vdevs; i++) {
arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]);
if (!arvif) {
diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
index 07bb83710b1f..0fc55c9169e1 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.c
+++ b/drivers/net/wireless/ath/ath12k/ahb.c
@@ -25,6 +25,22 @@ static const char ath12k_userpd_irq[][9] = {"spawn",
"ready",
"stop-ack"};
+/*
+ * Multi-UserPD Architecture:
+ *
+ * One Q6 RootPD (managed by separate rproc driver) supports multiple
+ * ath12k UserPDs. Each UserPD represents a WiFi radio instance.
+ *
+ * Lifecycle:
+ * - RootPD boots when first UserPD probes
+ * - All UserPDs share RootPD's SSR notifier
+ *
+ * Locking:
+ * - ath12k_rproc_info_lock: Protects g_rproc_info allocation/free
+ */
+static struct ath12k_ahb_rproc_info *g_rproc_info;
+static DEFINE_MUTEX(ath12k_rproc_info_lock);
+
static const char *irq_name[ATH12K_IRQ_NUM_MAX] = {
"misc-pulse1",
"misc-latch",
@@ -704,7 +720,7 @@ static int ath12k_ahb_map_service_to_pipe(struct ath12k_base *ab, u16 service_id
return 0;
}
-static const struct ath12k_hif_ops ath12k_ahb_hif_ops = {
+const struct ath12k_hif_ops ath12k_ahb_hif_ops = {
.start = ath12k_ahb_start,
.stop = ath12k_ahb_stop,
.read32 = ath12k_ahb_read32,
@@ -715,6 +731,7 @@ static const struct ath12k_hif_ops ath12k_ahb_hif_ops = {
.power_up = ath12k_ahb_power_up,
.power_down = ath12k_ahb_power_down,
};
+EXPORT_SYMBOL(ath12k_ahb_hif_ops);
static irqreturn_t ath12k_userpd_irq_handler(int irq, void *data)
{
@@ -785,44 +802,85 @@ static int ath12k_ahb_config_rproc_irq(struct ath12k_base *ab)
static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb,
const unsigned long event, void *data)
{
- struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb);
- struct ath12k_base *ab = ab_ahb->ab;
+ struct ath12k_ahb_rproc_info *rproc_info =
+ container_of(nb, struct ath12k_ahb_rproc_info, root_pd_nb);
if (event == ATH12K_RPROC_AFTER_POWERUP) {
- ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n");
- complete(&ab_ahb->rootpd_ready);
+ ath12k_generic_dbg(ATH12K_DBG_AHB, "Root PD is UP\n");
+ complete(&rproc_info->rootpd_ready);
}
return 0;
}
-static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab)
+static int ath12k_ahb_register_rproc_notifier(void)
{
- struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ int ret;
+
+ lockdep_assert_held(&ath12k_rproc_info_lock);
- ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier;
- init_completion(&ab_ahb->rootpd_ready);
+ if (g_rproc_info->root_pd_notifier)
+ return 0;
- ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name,
- &ab_ahb->root_pd_nb);
- if (IS_ERR(ab_ahb->root_pd_notifier))
- return PTR_ERR(ab_ahb->root_pd_notifier);
+ g_rproc_info->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier;
+
+ g_rproc_info->root_pd_notifier =
+ qcom_register_ssr_notifier(g_rproc_info->tgt_rproc->name,
+ &g_rproc_info->root_pd_nb);
+ if (IS_ERR(g_rproc_info->root_pd_notifier)) {
+ ret = PTR_ERR(g_rproc_info->root_pd_notifier);
+ g_rproc_info->root_pd_notifier = NULL;
+ return ret;
+ }
return 0;
}
-static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab)
+static void ath12k_ahb_unregister_rproc_notifier(void)
+{
+ lockdep_assert_held(&ath12k_rproc_info_lock);
+
+ if (!g_rproc_info->root_pd_notifier)
+ return;
+
+ qcom_unregister_ssr_notifier(g_rproc_info->root_pd_notifier,
+ &g_rproc_info->root_pd_nb);
+ g_rproc_info->root_pd_notifier = NULL;
+}
+
+static void ath12k_ahb_cleanup_userpd(struct ath12k_base *ab)
{
struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info;
- if (!ab_ahb->root_pd_notifier) {
- ath12k_err(ab, "Rproc notifier not registered\n");
+ lockdep_assert_held(&ath12k_rproc_info_lock);
+
+ if (!rproc_info)
return;
- }
- qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier,
- &ab_ahb->root_pd_nb);
- ab_ahb->root_pd_notifier = NULL;
+ rproc_info->userpd[ab_ahb->userpd_id - 1] = NULL;
+ rproc_info->num_userpd--;
+ ab_ahb->rproc_info = NULL;
+}
+
+static struct ath12k_ahb_rproc_info *ath12k_ahb_rproc_info_alloc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ struct ath12k_ahb_rproc_info *rproc_info;
+
+ lockdep_assert_held(&ath12k_rproc_info_lock);
+
+ rproc_info = kzalloc_obj(*rproc_info, GFP_KERNEL);
+ if (!rproc_info)
+ return NULL;
+
+ rproc_info->rootpd_booted_by_driver = false;
+ rproc_info->userpd[ab_ahb->userpd_id - 1] = ab_ahb;
+ rproc_info->num_userpd = 1;
+ init_completion(&rproc_info->rootpd_ready);
+ ab_ahb->rproc_info = rproc_info;
+
+ return rproc_info;
}
static int ath12k_ahb_get_rproc(struct ath12k_base *ab)
@@ -831,37 +889,69 @@ static int ath12k_ahb_get_rproc(struct ath12k_base *ab)
struct device *dev = ab->dev;
struct device_node *np;
struct rproc *prproc;
+ int ret;
+
+ lockdep_assert_held(&ath12k_rproc_info_lock);
+
+ if (ab_ahb->userpd_id > ATH12K_MAX_DEVICES)
+ return -ENOSPC;
+
+ if (g_rproc_info) {
+ if (g_rproc_info->num_userpd >= ATH12K_MAX_DEVICES) {
+ ath12k_err(ab, "Max UserPD limit reached\n");
+ return -ENOSPC;
+ }
+
+ g_rproc_info->userpd[ab_ahb->userpd_id - 1] = ab_ahb;
+ g_rproc_info->num_userpd++;
+ ab_ahb->rproc_info = g_rproc_info;
+ return 0;
+ }
+
+ g_rproc_info = ath12k_ahb_rproc_info_alloc(ab);
+ if (!g_rproc_info)
+ return -ENOMEM;
np = of_parse_phandle(dev->of_node, "qcom,rproc", 0);
if (!np) {
ath12k_err(ab, "failed to get q6_rproc handle\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto err_free_rproc_info;
}
prproc = rproc_get_by_phandle(np->phandle);
of_node_put(np);
- if (!prproc)
- return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER,
- "failed to get rproc\n");
-
- ab_ahb->tgt_rproc = prproc;
+ if (!prproc) {
+ ret = dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER,
+ "failed to get rproc\n");
+ goto err_free_rproc_info;
+ }
+ g_rproc_info->tgt_rproc = prproc;
return 0;
+
+err_free_rproc_info:
+ ab_ahb->rproc_info = NULL;
+ kfree(g_rproc_info);
+ g_rproc_info = NULL;
+ return ret;
}
static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab)
{
- struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
unsigned long time_left;
int ret;
- ret = rproc_boot(ab_ahb->tgt_rproc);
+ lockdep_assert_held(&ath12k_rproc_info_lock);
+ reinit_completion(&g_rproc_info->rootpd_ready);
+
+ ret = rproc_boot(g_rproc_info->tgt_rproc);
if (ret < 0) {
ath12k_err(ab, "RootPD boot failed\n");
return ret;
}
- time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready,
+ time_left = wait_for_completion_timeout(&g_rproc_info->rootpd_ready,
ATH12K_ROOTPD_READY_TIMEOUT);
if (!time_left) {
ath12k_err(ab, "RootPD ready wait timed out\n");
@@ -873,44 +963,74 @@ static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab)
static int ath12k_ahb_configure_rproc(struct ath12k_base *ab)
{
- struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
int ret;
+ mutex_lock(&ath12k_rproc_info_lock);
+
ret = ath12k_ahb_get_rproc(ab);
- if (ret < 0)
+ if (ret < 0) {
+ mutex_unlock(&ath12k_rproc_info_lock);
return ret;
+ }
- ret = ath12k_ahb_register_rproc_notifier(ab);
+ ret = ath12k_ahb_register_rproc_notifier();
if (ret < 0) {
ret = dev_err_probe(&ab->pdev->dev, ret,
"failed to register rproc notifier\n");
- goto err_put_rproc;
+ goto err_cleanup_userpd;
}
- if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) {
+ if (g_rproc_info->tgt_rproc->state != RPROC_RUNNING) {
ret = ath12k_ahb_boot_root_pd(ab);
if (ret < 0) {
ath12k_err(ab, "failed to boot the remote processor Q6\n");
goto err_unreg_notifier;
}
+ g_rproc_info->rootpd_booted_by_driver = true;
}
- return ath12k_ahb_config_rproc_irq(ab);
+ mutex_unlock(&ath12k_rproc_info_lock);
+ return 0;
err_unreg_notifier:
- ath12k_ahb_unregister_rproc_notifier(ab);
+ ath12k_ahb_unregister_rproc_notifier();
+
+err_cleanup_userpd:
+ ath12k_ahb_cleanup_userpd(ab);
+
+ if (g_rproc_info && !g_rproc_info->num_userpd) {
+ rproc_put(g_rproc_info->tgt_rproc);
+ kfree(g_rproc_info);
+ g_rproc_info = NULL;
+ }
-err_put_rproc:
- rproc_put(ab_ahb->tgt_rproc);
+ mutex_unlock(&ath12k_rproc_info_lock);
return ret;
}
static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab)
{
struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info;
+
+ lockdep_assert_held(&ath12k_rproc_info_lock);
+
+ if (!rproc_info || !g_rproc_info)
+ return;
- ath12k_ahb_unregister_rproc_notifier(ab);
- rproc_put(ab_ahb->tgt_rproc);
+ ath12k_ahb_cleanup_userpd(ab);
+
+ if (!g_rproc_info->num_userpd) {
+ ath12k_ahb_unregister_rproc_notifier();
+
+ if (g_rproc_info->rootpd_booted_by_driver &&
+ g_rproc_info->tgt_rproc->state == RPROC_RUNNING)
+ rproc_shutdown(g_rproc_info->tgt_rproc);
+
+ rproc_put(g_rproc_info->tgt_rproc);
+ kfree(g_rproc_info);
+ g_rproc_info = NULL;
+ }
}
static int ath12k_ahb_resource_init(struct ath12k_base *ab)
@@ -1038,7 +1158,6 @@ static int ath12k_ahb_probe(struct platform_device *pdev)
ab_ahb = ath12k_ab_to_ahb(ab);
ab_ahb->ab = ab;
- ab->hif.ops = &ath12k_ahb_hif_ops;
ab->pdev = pdev;
platform_set_drvdata(pdev, ab);
@@ -1094,6 +1213,10 @@ static int ath12k_ahb_probe(struct platform_device *pdev)
if (ret)
goto err_ce_free;
+ ret = ath12k_ahb_config_rproc_irq(ab);
+ if (ret)
+ goto err_rproc_deconfigure;
+
ret = ath12k_ahb_config_irq(ab);
if (ret) {
ath12k_err(ab, "failed to configure irq: %d\n", ret);
@@ -1121,7 +1244,9 @@ err_deinit_arch:
ab_ahb->device_family_ops->arch_deinit(ab);
err_rproc_deconfigure:
+ mutex_lock(&ath12k_rproc_info_lock);
ath12k_ahb_deconfigure_rproc(ab);
+ mutex_unlock(&ath12k_rproc_info_lock);
err_ce_free:
ath12k_ce_free_pipes(ab);
@@ -1163,7 +1288,9 @@ static void ath12k_ahb_free_resources(struct ath12k_base *ab)
ath12k_hal_srng_deinit(ab);
ath12k_ce_free_pipes(ab);
ath12k_ahb_resource_deinit(ab);
+ mutex_lock(&ath12k_rproc_info_lock);
ath12k_ahb_deconfigure_rproc(ab);
+ mutex_unlock(&ath12k_rproc_info_lock);
ab_ahb->device_family_ops->arch_deinit(ab);
ath12k_core_free(ab);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h
index a153db6cf1d3..cdb58b07338f 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.h
+++ b/drivers/net/wireless/ath/ath12k/ahb.h
@@ -30,6 +30,24 @@
#define ATH12K_USERPD_ID_MASK GENMASK(10, 8)
#define ATH12K_USERPD_FW_NAME_LEN 35
+enum ath12k_ahb_userpd_id {
+ ATH12K_AHB_USERPD_ID_0 = 1,
+ ATH12K_AHB_USERPD_ID_1,
+ ATH12K_AHB_USERPD_ID_2,
+};
+
+struct ath12k_ahb_userpd_map {
+ phys_addr_t io_start;
+ const char *node_name;
+ u32 upd_id;
+};
+
+struct ath12k_ahb_desc {
+ enum ath12k_hw_rev hw_rev;
+ bool auth_enabled;
+ const struct ath12k_hif_ops *ops;
+};
+
enum ath12k_ahb_smp2p_msg_id {
ATH12K_AHB_POWER_SAVE_ENTER = 1,
ATH12K_AHB_POWER_SAVE_EXIT,
@@ -43,6 +61,7 @@ enum ath12k_ahb_userpd_irq {
};
struct ath12k_base;
+extern const struct ath12k_hif_ops ath12k_ahb_hif_ops;
struct ath12k_ahb_device_family_ops {
int (*probe)(struct platform_device *pdev);
@@ -50,13 +69,19 @@ struct ath12k_ahb_device_family_ops {
void (*arch_deinit)(struct ath12k_base *ab);
};
-struct ath12k_ahb {
- struct ath12k_base *ab;
+struct ath12k_ahb_rproc_info {
struct rproc *tgt_rproc;
- struct clk *xo_clk;
- struct completion rootpd_ready;
struct notifier_block root_pd_nb;
void *root_pd_notifier;
+ struct completion rootpd_ready;
+ u8 num_userpd;
+ bool rootpd_booted_by_driver;
+ struct ath12k_ahb *userpd[ATH12K_MAX_DEVICES];
+};
+
+struct ath12k_ahb {
+ struct ath12k_base *ab;
+ struct clk *xo_clk;
struct qcom_smem_state *spawn_state;
struct qcom_smem_state *stop_state;
struct completion userpd_spawned;
@@ -69,6 +94,7 @@ struct ath12k_ahb {
const struct ath12k_ahb_ops *ahb_ops;
const struct ath12k_ahb_device_family_ops *device_family_ops;
bool scm_auth_enabled;
+ struct ath12k_ahb_rproc_info *rproc_info;
};
struct ath12k_ahb_driver {
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index a9112760185f..cb4ca93f625c 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -23,6 +23,7 @@
#include "wow.h"
#include "dp_cmn.h"
#include "peer.h"
+#include "qmi.h"
unsigned int ath12k_debug_mask;
module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
@@ -52,6 +53,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = {
.rxdma_monitor_dst_ring_size = 8192,
.num_pool_tx_desc = 32768,
.rx_desc_count = 12288,
+ .rx_release_ring_size = 16384,
},
},
[ATH12K_QMI_MEMORY_MODE_LOW_512_M] = {
@@ -65,6 +67,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = {
.rxdma_monitor_dst_ring_size = 512,
.num_pool_tx_desc = 16384,
.rx_desc_count = 6144,
+ .rx_release_ring_size = 8192,
},
},
};
@@ -794,7 +797,7 @@ static int ath12k_core_soc_create(struct ath12k_base *ab)
int ret;
if (ath12k_ftm_mode) {
- ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM;
+ ab->fw_mode = ATH12K_QMI_FIRMWARE_MODE_FTM;
ath12k_info(ab, "Booting in ftm mode\n");
}
@@ -1188,7 +1191,7 @@ err_mac_destroy:
}
static int ath12k_core_start_firmware(struct ath12k_base *ab,
- enum ath12k_firmware_mode mode)
+ enum ath12k_qmi_firmware_mode mode)
{
int ret;
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 37a194e00248..3bd71bf02afb 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -160,14 +160,6 @@ enum ath12k_hw_rev {
ATH12K_HW_IPQ5424_HW10,
};
-enum ath12k_firmware_mode {
- /* the default mode, standard 802.11 functionality */
- ATH12K_FIRMWARE_MODE_NORMAL,
-
- /* factory tests etc */
- ATH12K_FIRMWARE_MODE_FTM,
-};
-
#define ATH12K_IRQ_NUM_MAX 57
#define ATH12K_EXT_IRQ_NUM_MAX 16
#define ATH12K_MAX_TCL_RING_NUM 3
@@ -939,6 +931,7 @@ struct ath12k_dp_profile_params {
u32 rxdma_monitor_dst_ring_size;
u32 num_pool_tx_desc;
u32 rx_desc_count;
+ u32 rx_release_ring_size;
};
struct ath12k_mem_profile_based_param {
@@ -1147,7 +1140,7 @@ struct ath12k_base {
struct ath12k_hw_group *ag;
struct ath12k_wsi_info wsi_info;
- enum ath12k_firmware_mode fw_mode;
+ enum ath12k_qmi_firmware_mode fw_mode;
struct ath12k_ftm_event_obj ftm_event_obj;
bool hw_group_ref;
diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c
index fbc0788b37a0..f9b37d75956d 100644
--- a/drivers/net/wireless/ath/ath12k/dp.c
+++ b/drivers/net/wireless/ath/ath12k/dp.c
@@ -487,7 +487,7 @@ static int ath12k_dp_srng_common_setup(struct ath12k_base *ab)
ret = ath12k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE,
HAL_WBM2SW_REL_ERR_RING_NUM, 0,
- DP_RX_RELEASE_RING_SIZE);
+ DP_RX_RELEASE_RING_SIZE(ab));
if (ret) {
ath12k_warn(ab, "failed to set up rx_rel ring :%d\n", ret);
goto err;
diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h
index a94bbc337df4..bef0f2ba0560 100644
--- a/drivers/net/wireless/ath/ath12k/dp.h
+++ b/drivers/net/wireless/ath/ath12k/dp.h
@@ -200,7 +200,8 @@ struct ath12k_pdev_dp {
#define DP_REO_DST_RING_MAX 8
#define DP_REO_DST_RING_SIZE 2048
#define DP_REO_REINJECT_RING_SIZE 32
-#define DP_RX_RELEASE_RING_SIZE 1024
+#define DP_RX_RELEASE_RING_SIZE(ab) \
+ ((ab)->profile_param->dp_params.rx_release_ring_size)
#define DP_REO_EXCEPTION_RING_SIZE 128
#define DP_REO_CMD_RING_SIZE 256
#define DP_REO_STATUS_RING_SIZE 2048
diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c
index c10da6195c9c..9644f9ef2c74 100644
--- a/drivers/net/wireless/ath/ath12k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_tx.c
@@ -82,6 +82,52 @@ enum hal_encrypt_type ath12k_dp_tx_get_encrypt_type(u32 cipher)
}
EXPORT_SYMBOL(ath12k_dp_tx_get_encrypt_type);
+u8 ath12k_dp_tx_crypto_iv_len(enum hal_encrypt_type enc_type)
+{
+ switch (enc_type) {
+ case HAL_ENCRYPT_TYPE_TKIP_NO_MIC:
+ case HAL_ENCRYPT_TYPE_TKIP_MIC:
+ return IEEE80211_TKIP_IV_LEN;
+ case HAL_ENCRYPT_TYPE_CCMP_128:
+ return IEEE80211_CCMP_HDR_LEN;
+ case HAL_ENCRYPT_TYPE_CCMP_256:
+ return IEEE80211_CCMP_256_HDR_LEN;
+ case HAL_ENCRYPT_TYPE_GCMP_128:
+ case HAL_ENCRYPT_TYPE_AES_GCMP_256:
+ return IEEE80211_GCMP_HDR_LEN;
+ case HAL_ENCRYPT_TYPE_WEP_40:
+ case HAL_ENCRYPT_TYPE_WEP_104:
+ case HAL_ENCRYPT_TYPE_WEP_128:
+ return IEEE80211_WEP_IV_LEN;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(ath12k_dp_tx_crypto_iv_len);
+
+u8 ath12k_dp_tx_crypto_icv_len(enum hal_encrypt_type enc_type)
+{
+ switch (enc_type) {
+ case HAL_ENCRYPT_TYPE_CCMP_128:
+ return IEEE80211_CCMP_MIC_LEN;
+ case HAL_ENCRYPT_TYPE_CCMP_256:
+ return IEEE80211_CCMP_256_MIC_LEN;
+ case HAL_ENCRYPT_TYPE_GCMP_128:
+ case HAL_ENCRYPT_TYPE_AES_GCMP_256:
+ return IEEE80211_GCMP_MIC_LEN;
+ case HAL_ENCRYPT_TYPE_TKIP_NO_MIC:
+ case HAL_ENCRYPT_TYPE_TKIP_MIC:
+ return IEEE80211_TKIP_ICV_LEN;
+ case HAL_ENCRYPT_TYPE_WEP_40:
+ case HAL_ENCRYPT_TYPE_WEP_104:
+ case HAL_ENCRYPT_TYPE_WEP_128:
+ return IEEE80211_WEP_ICV_LEN;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(ath12k_dp_tx_crypto_icv_len);
+
void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp,
struct ath12k_tx_desc_info *tx_desc,
u8 pool_id)
diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h b/drivers/net/wireless/ath/ath12k/dp_tx.h
index 7cef20540179..1af79af2ada2 100644
--- a/drivers/net/wireless/ath/ath12k/dp_tx.h
+++ b/drivers/net/wireless/ath/ath12k/dp_tx.h
@@ -19,6 +19,8 @@ enum hal_tcl_encap_type
ath12k_dp_tx_get_encap_type(struct ath12k_base *ab, struct sk_buff *skb);
void ath12k_dp_tx_encap_nwifi(struct sk_buff *skb);
u8 ath12k_dp_tx_get_tid(struct sk_buff *skb);
+u8 ath12k_dp_tx_crypto_iv_len(enum hal_encrypt_type enc_type);
+u8 ath12k_dp_tx_crypto_icv_len(enum hal_encrypt_type enc_type);
void *ath12k_dp_metadata_align_skb(struct sk_buff *skb, u8 tail_len);
int ath12k_dp_tx_align_payload(struct ath12k_dp *dp, struct sk_buff **pskb);
void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp,
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index 49cfd5dfc70a..3ed38f8f2b48 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -100,7 +100,6 @@ struct ieee80211_rx_status;
#define ATH12K_REGDB_FILE_NAME "regdb.bin"
#define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128
-#define ATH12K_IPQ5332_USERPD_ID 1
enum ath12k_hw_rate_cck {
ATH12K_HW_RATE_CCK_LP_11M = 0,
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 310976247dbb..9a775602775d 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -859,7 +859,7 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id)
return NULL;
for (i = 0; i < ab->num_radios; i++) {
- if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM)
+ if (ab->fw_mode == ATH12K_QMI_FIRMWARE_MODE_FTM)
pdev = &ab->pdevs[i];
else
pdev = rcu_dereference(ab->pdevs_active[i]);
diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
index ad74140e0fa5..6441927b5382 100644
--- a/drivers/net/wireless/ath/ath12k/pci.c
+++ b/drivers/net/wireless/ath/ath12k/pci.c
@@ -17,6 +17,7 @@
#include "mhi.h"
#include "debug.h"
#include "hal.h"
+#include "qmi.h"
#define ATH12K_PCI_BAR_NUM 0
#define ATH12K_PCI_DMA_MASK 36
@@ -1555,7 +1556,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev,
ab_pci->ab = ab;
ab_pci->pdev = pdev;
ab->hif.ops = &ath12k_pci_hif_ops;
- ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL;
+ ab->fw_mode = ATH12K_QMI_FIRMWARE_MODE_NORMAL;
pci_set_drvdata(pdev, ab);
spin_lock_init(&ab_pci->window_lock);
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
index bb61c78e5c29..280e50a1f31d 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.c
+++ b/drivers/net/wireless/ath/ath12k/qmi.c
@@ -3431,7 +3431,7 @@ out:
}
static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab,
- u32 mode)
+ enum ath12k_qmi_firmware_mode mode)
{
struct qmi_wlanfw_wlan_mode_req_msg_v01 req = {};
struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp = {};
@@ -3460,7 +3460,7 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab,
ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS));
if (ret < 0) {
- if (mode == ATH12K_FIRMWARE_MODE_OFF && ret == -ENETRESET) {
+ if (mode == ATH12K_QMI_FIRMWARE_MODE_OFF && ret == -ENETRESET) {
ath12k_warn(ab, "WLFW service is dis-connected\n");
return 0;
}
@@ -3623,7 +3623,7 @@ void ath12k_qmi_firmware_stop(struct ath12k_base *ab)
clear_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags);
- ret = ath12k_qmi_wlanfw_mode_send(ab, ATH12K_FIRMWARE_MODE_OFF);
+ ret = ath12k_qmi_wlanfw_mode_send(ab, ATH12K_QMI_FIRMWARE_MODE_OFF);
if (ret < 0) {
ath12k_warn(ab, "qmi failed to send wlan mode off\n");
return;
@@ -3631,7 +3631,7 @@ void ath12k_qmi_firmware_stop(struct ath12k_base *ab)
}
int ath12k_qmi_firmware_start(struct ath12k_base *ab,
- u32 mode)
+ enum ath12k_qmi_firmware_mode mode)
{
int ret;
diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
index cbe5be30053a..6da10f3cb597 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.h
+++ b/drivers/net/wireless/ath/ath12k/qmi.h
@@ -32,7 +32,6 @@
#define QMI_WLFW_FW_READY_IND_V01 0x0038
#define QMI_WLANFW_MAX_DATA_SIZE_V01 6144
-#define ATH12K_FIRMWARE_MODE_OFF 4
#define ATH12K_BOARD_ID_DEFAULT 0xFF
@@ -602,6 +601,17 @@ enum ath12k_qmi_mem_mode {
ATH12K_QMI_MEMORY_MODE_LOW_512_M,
};
+enum ath12k_qmi_firmware_mode {
+ /* the default mode, standard 802.11 functionality */
+ ATH12K_QMI_FIRMWARE_MODE_NORMAL,
+
+ /* factory tests etc */
+ ATH12K_QMI_FIRMWARE_MODE_FTM,
+
+ /* firmware offline; values 2-3 reserved by firmware ABI */
+ ATH12K_QMI_FIRMWARE_MODE_OFF = 4,
+};
+
static inline void ath12k_qmi_set_event_block(struct ath12k_qmi *qmi, bool block)
{
lockdep_assert_held(&qmi->event_lock);
@@ -617,7 +627,7 @@ static inline bool ath12k_qmi_get_event_block(struct ath12k_qmi *qmi)
}
int ath12k_qmi_firmware_start(struct ath12k_base *ab,
- u32 mode);
+ enum ath12k_qmi_firmware_mode mode);
void ath12k_qmi_firmware_stop(struct ath12k_base *ab);
void ath12k_qmi_deinit_service(struct ath12k_base *ab);
int ath12k_qmi_init_service(struct ath12k_base *ab);
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c
index 6a8b8b2a56f9..98a6606ffd76 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c
@@ -15,44 +15,100 @@
#include "dp.h"
#include "core.h"
+/*
+ * Node name to UserPD ID mapping
+ *
+ * The io_start field is used for additional validation when the reg
+ * property is present in the device tree. If io_start is 0, only
+ * node_name matching is performed.
+ *
+ * For platforms where not all WiFi nodes have a 'reg' property, set
+ * io_start to 0 for those entries. The driver will match purely by
+ * node name in such cases.
+ */
+static const struct ath12k_ahb_userpd_map ath12k_wifi7_ahb_userpd_map[] = {
+ { .io_start = 0x0c000000, .node_name = "wifi", .upd_id = ATH12K_AHB_USERPD_ID_0 },
+};
+
+static const struct ath12k_ahb_desc ath12k_wifi7_ahb_desc[] = {
+ [ATH12K_HW_IPQ5332_HW10] = {
+ .hw_rev = ATH12K_HW_IPQ5332_HW10,
+ .auth_enabled = true,
+ .ops = &ath12k_ahb_hif_ops,
+ },
+ [ATH12K_HW_IPQ5424_HW10] = {
+ .hw_rev = ATH12K_HW_IPQ5424_HW10,
+ .auth_enabled = false,
+ .ops = &ath12k_ahb_hif_ops,
+ },
+};
+
static const struct of_device_id ath12k_wifi7_ahb_of_match[] = {
{ .compatible = "qcom,ipq5332-wifi",
- .data = (void *)ATH12K_HW_IPQ5332_HW10,
+ .data = (void *)&ath12k_wifi7_ahb_desc[ATH12K_HW_IPQ5332_HW10],
},
{ .compatible = "qcom,ipq5424-wifi",
- .data = (void *)ATH12K_HW_IPQ5424_HW10,
+ .data = (void *)&ath12k_wifi7_ahb_desc[ATH12K_HW_IPQ5424_HW10],
},
{ }
};
MODULE_DEVICE_TABLE(of, ath12k_wifi7_ahb_of_match);
+/*
+ * ath12k_wifi7_ahb_get_userpd_id - Resolve UserPD ID from DT properties
+ * @ab: ath12k base structure
+ *
+ * Returns: UserPD ID (1-based) on success, 0 on failure
+ *
+ * Resolution logic:
+ * 1. If reg property exist in DT, get userpd_id from io_start
+ * 2. If reg property is absent, get userpd_id from DT node name
+ * 3. Return 0 if no match found (probe will fail)
+ */
+static u32 ath12k_wifi7_ahb_get_userpd_id(struct ath12k_base *ab)
+{
+ const struct ath12k_ahb_userpd_map *map;
+ struct resource *res;
+ size_t i;
+
+ res = platform_get_resource(ab->pdev, IORESOURCE_MEM, 0);
+
+ for (i = 0; i < ARRAY_SIZE(ath12k_wifi7_ahb_userpd_map); i++) {
+ map = &ath12k_wifi7_ahb_userpd_map[i];
+
+ if (res) {
+ if (map->io_start && map->io_start == res->start)
+ return map->upd_id;
+ } else if (map->node_name &&
+ of_node_name_eq(ab->dev->of_node, map->node_name)) {
+ return map->upd_id;
+ }
+ }
+
+ return 0;
+}
+
static int ath12k_wifi7_ahb_probe(struct platform_device *pdev)
{
+ const struct ath12k_ahb_desc *desc;
struct ath12k_ahb *ab_ahb;
- enum ath12k_hw_rev hw_rev;
struct ath12k_base *ab;
int ret;
ab = platform_get_drvdata(pdev);
ab_ahb = ath12k_ab_to_ahb(ab);
-
- hw_rev = (enum ath12k_hw_rev)(kernel_ulong_t)of_device_get_match_data(&pdev->dev);
- switch (hw_rev) {
- case ATH12K_HW_IPQ5332_HW10:
- ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID;
- ab_ahb->scm_auth_enabled = true;
- break;
- case ATH12K_HW_IPQ5424_HW10:
- ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID;
- ab_ahb->scm_auth_enabled = false;
- break;
- default:
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
return -EOPNOTSUPP;
- }
ab->target_mem_mode = ATH12K_QMI_MEMORY_MODE_DEFAULT;
- ab->hw_rev = hw_rev;
+ ab->hw_rev = desc->hw_rev;
+ ab->hif.ops = desc->ops;
+ ab_ahb->scm_auth_enabled = desc->auth_enabled;
+ ab_ahb->userpd_id = ath12k_wifi7_ahb_get_userpd_id(ab);
+ if (!ab_ahb->userpd_id)
+ return -EOPNOTSUPP;
ret = ath12k_wifi7_hw_init(ab);
if (ret) {
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c
index d2749de44553..587d58eeccfa 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c
@@ -13,6 +13,49 @@
#include "hal.h"
#include "hal_tx.h"
+/*
+ * Convert an encrypted EAPOL frame from native-WiFi format to
+ * the layout expected by the firmware RAW encrypt pipeline:
+ *
+ * [802.11 hdr][IV (zeroed)][LLC/SNAP][EAPOL payload][ICV (zeroed)]
+ *
+ * mac80211 delivers the frame as [802.11 hdr][LLC/SNAP][EAPOL payload].
+ * The MAC header length is read from the unmodified skb and is safe because
+ * ieee80211_hdrlen() only inspects the 2-byte frame_control field.
+ * pskb_expand_head() is used to grow both head (for the IV) and tail
+ * (for the ICV) in a single call and allocation.
+ */
+static int
+ath12k_wifi7_dp_tx_encap_eapol(struct sk_buff *skb,
+ struct hal_tx_info *ti,
+ struct ath12k_skb_cb *skb_cb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ enum hal_encrypt_type enc_type =
+ ath12k_dp_tx_get_encrypt_type(skb_cb->cipher);
+ u16 mac_hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ u8 iv_len = ath12k_dp_tx_crypto_iv_len(enc_type);
+ u8 icv_len = ath12k_dp_tx_crypto_icv_len(enc_type);
+
+ if (pskb_expand_head(skb, iv_len, icv_len, GFP_ATOMIC))
+ return -ENOMEM;
+
+ if (iv_len) {
+ skb_push(skb, iv_len);
+ memmove(skb->data, skb->data + iv_len, mac_hdr_len);
+ memset(skb->data + mac_hdr_len, 0, iv_len);
+ }
+
+ if (icv_len)
+ memset(skb_put(skb, icv_len), 0, icv_len);
+
+ ti->flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW);
+ ti->encap_type = HAL_TCL_ENCAP_TYPE_RAW;
+ ti->encrypt_type = enc_type;
+
+ return 0;
+}
+
static void
ath12k_wifi7_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab,
struct hal_tx_msdu_ext_desc *tcl_ext_cmd,
@@ -91,6 +134,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a
u32 iova_mask = dp->hw_params->iova_mask;
bool is_diff_encap = false;
bool is_null_frame = false;
+ bool eapol_encap_done = false;
if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
return -ESHUTDOWN;
@@ -211,9 +255,27 @@ tcl_ring_sel:
case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI:
is_null_frame = ieee80211_is_nullfunc(hdr->frame_control);
if (ahvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) {
- if (skb->protocol == cpu_to_be16(ETH_P_PAE) || is_null_frame)
+ if ((skb->protocol == cpu_to_be16(ETH_P_PAE) &&
+ !(skb_cb->flags & ATH12K_SKB_CIPHER_SET)) || is_null_frame)
is_diff_encap = true;
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE) &&
+ (skb_cb->flags & ATH12K_SKB_CIPHER_SET)) {
+ if (!eapol_encap_done) {
+ ret = ath12k_wifi7_dp_tx_encap_eapol(skb, &ti,
+ skb_cb);
+ if (ret)
+ goto fail_remove_tx_buf;
+ hdr = (void *)skb->data;
+ eapol_encap_done = true;
+ } else {
+ ti.flags0 |= u32_encode_bits(1,
+ HAL_TCL_DATA_CMD_INFO2_TO_FW);
+ ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW;
+ ti.encrypt_type =
+ ath12k_dp_tx_get_encrypt_type(skb_cb->cipher);
+ }
+ }
/* Firmware expects msdu ext descriptor for nwifi/raw packets
* received in ETH mode. Without this, observed tx fail for
* Multicast packets in ETH mode.
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index 4c1119edcaed..d890801d5822 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -1032,6 +1032,9 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw,
continue;
tmp_ar = tmp_arvif->ar;
+ if (unlikely(test_bit(ATH12K_FLAG_CRASH_FLUSH, &tmp_ar->ab->dev_flags)))
+ continue;
+
tmp_dp = ath12k_ab_to_dp(tmp_ar->ab);
tmp_dp_pdev = ath12k_dp_to_pdev_dp(tmp_dp,
tmp_ar->pdev_idx);
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 2b707ffc1a20..d5160af60e00 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -207,6 +207,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_per_chain_rssi_stat_params) },
[WMI_TAG_OBSS_COLOR_COLLISION_EVT] = {
.min_len = sizeof(struct wmi_obss_color_collision_event) },
+ [WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT] = {
+ .min_len = sizeof(struct ath12k_wmi_pdev_csa_event) },
};
__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
@@ -374,6 +376,13 @@ ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb)
return tb;
}
+static u32 ath12k_wmi_tlv_data_len(const void *data)
+{
+ const struct wmi_tlv *tlv = (const struct wmi_tlv *)data - 1;
+
+ return le32_get_bits(tlv->header, WMI_TLV_LEN);
+}
+
static int ath12k_wmi_cmd_send_nowait(struct ath12k_wmi_pdev *wmi, struct sk_buff *skb,
u32 cmd_id)
{
@@ -4765,14 +4774,16 @@ static int ath12k_wmi_mac_phy_caps_parse(struct ath12k_base *soc,
if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id)
return -ENOBUFS;
- len = min_t(u16, len, sizeof(struct ath12k_wmi_mac_phy_caps_params));
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 =
+ kzalloc_objs(*svc_rdy_ext->mac_phy_caps,
+ svc_rdy_ext->tot_phy_id,
+ GFP_ATOMIC);
if (!svc_rdy_ext->mac_phy_caps)
return -ENOMEM;
}
+ len = min_t(u16, len, sizeof(struct ath12k_wmi_mac_phy_caps_params));
memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len);
svc_rdy_ext->n_mac_phy_caps++;
return 0;
@@ -9075,12 +9086,19 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab,
const u32 *vdev_ids)
{
u32 current_switch_count = le32_to_cpu(ev->current_switch_count);
+ u32 vdev_ids_len = ath12k_wmi_tlv_data_len(vdev_ids);
u32 num_vdevs = le32_to_cpu(ev->num_vdevs);
struct ieee80211_bss_conf *conf;
struct ath12k_link_vif *arvif;
struct ath12k_vif *ahvif;
int i;
+ if (num_vdevs > vdev_ids_len / sizeof(*vdev_ids)) {
+ ath12k_warn(ab, "csa switch count num_vdevs %u exceeds tlv array length %u\n",
+ num_vdevs, vdev_ids_len);
+ return;
+ }
+
rcu_read_lock();
for (i = 0; i < num_vdevs; i++) {
arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]);
@@ -9970,6 +9988,7 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab,
void *ptr = skb->data;
struct ath12k *ar;
u16 tlv_tag;
+ u16 tlv_len;
u32 event_count;
int ret;
@@ -9985,6 +10004,7 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab,
tlv = (struct wmi_tlv *)ptr;
tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG);
+ tlv_len = le32_get_bits(tlv->header, WMI_TLV_LEN);
ptr += sizeof(*tlv);
if (tlv_tag != WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM) {
@@ -9992,6 +10012,12 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab,
return;
}
+ if (tlv_len < sizeof(*fixed_param)) {
+ ath12k_warn(ab, "TPC stats fixed param tlv len %u too short\n",
+ tlv_len);
+ return;
+ }
+
fixed_param = (struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *)ptr;
rcu_read_lock();
ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(fixed_param->pdev_id) + 1);
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 34025d64d615..8bbe554a9b36 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -754,6 +754,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
assoc_resp_ie_offset;
+ if (assoc_req_len < assoc_req_ie_offset)
+ assoc_req_len = assoc_req_ie_offset;
+ if (assoc_resp_len < assoc_resp_ie_offset)
+ assoc_resp_len = assoc_resp_ie_offset;
+
assoc_req_len -= assoc_req_ie_offset;
assoc_resp_len -= assoc_resp_ie_offset;
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 782209dcb782..6481da4c1991 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -1570,7 +1570,7 @@ static int ath6kl_init_upload(struct ath6kl *ar)
if (status)
return status;
- return status;
+ return 0;
}
int ath6kl_init_hw_params(struct ath6kl *ar)