[PATCH] ath10k: implement host memory chunks feature

Bartosz Markowski bartosz.markowski at tieto.com
Wed Sep 18 05:47:31 EDT 2013


Firmware can request a memory pool from host to offload
it's own resources. This is a feature designed especially
for AP mode where the target has to deal with large number
of peers.

So we allocate and map a consistent DMA memory which FW can
use to store e.g. peer rate contol maps.

Signed-off-by: Bartosz Markowski <bartosz.markowski at tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |   12 +++
 drivers/net/wireless/ath/ath10k/hif.h  |   23 ++++++
 drivers/net/wireless/ath/ath10k/pci.c  |   45 +++++++++++
 drivers/net/wireless/ath/ath10k/wmi.c  |  129 ++++++++++++++++++++++++++++++--
 drivers/net/wireless/ath/ath10k/wmi.h  |   12 ++-
 5 files changed, 209 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index fcf94ee..c194f61 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -278,6 +278,15 @@ enum ath10k_fw_features {
 	ATH10K_FW_FEATURE_COUNT,
 };
 
+#define MAX_MEM_CHUNKS 32
+
+struct ath10k_mem_chunk {
+	void *vaddr;
+	dma_addr_t paddr;
+	u32 len;
+	u32 req_id;
+};
+
 struct ath10k {
 	struct ath_common ath_common;
 	struct ieee80211_hw *hw;
@@ -297,6 +306,9 @@ struct ath10k {
 	u32 vht_cap_info;
 	u32 num_rf_chains;
 
+	u32 num_mem_chunks;
+	struct ath10k_mem_chunk mem_chunks[MAX_MEM_CHUNKS];
+
 	DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT);
 
 	struct targetdef *targetdef;
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index dcdea68..4af6a66 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -83,6 +83,12 @@ struct ath10k_hif_ops {
 
 	int (*suspend)(struct ath10k *ar);
 	int (*resume)(struct ath10k *ar);
+
+	/* Allocate/Free the host memory for firmware use */
+	int (*chunk_alloc)(struct ath10k *ar, u32 req_id, u32 index,
+			   u32 num_units, u32 unit_len);
+
+	void (*chunk_free)(struct ath10k *ar, int idx);
 };
 
 
@@ -173,4 +179,21 @@ static inline int ath10k_hif_resume(struct ath10k *ar)
 	return ar->hif.ops->resume(ar);
 }
 
+static inline int ath10k_hif_chunk_alloc(struct ath10k *ar,
+					 u32 req_id,
+					 u32 idx,
+					 u32 num_units,
+					 u32 unit_len)
+{
+	if (!ar->hif.ops->chunk_alloc)
+		return -EOPNOTSUPP;
+
+	return ar->hif.ops->chunk_alloc(ar, req_id, idx, num_units, unit_len);
+}
+
+static inline void ath10k_hif_chunk_free(struct ath10k *ar, int idx)
+{
+	ar->hif.ops->chunk_free(ar, idx);
+}
+
 #endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index f1faf46..547d67d 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1966,6 +1966,49 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
 }
 #endif
 
+static int ath10k_pci_hif_chunk_alloc(struct ath10k *ar,
+				      u32 req_id,
+				      u32 idx,
+				      u32 num_units,
+				      u32 unit_len)
+{
+	dma_addr_t paddr;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	if (!num_units  || !unit_len)
+		return 0;
+
+	/*
+	 * TODO: allocate a chunk of memory at the index indicated and
+	 * if allocation fail allocate smallest size possible and
+	 * return number of units allocated.
+	 */
+	ar->mem_chunks[idx].vaddr = pci_alloc_consistent(ar_pci->pdev,
+							 num_units * unit_len,
+							 &paddr);
+	if(!ar->mem_chunks[idx].vaddr)
+		return 0;
+
+	memset(ar->mem_chunks[idx].vaddr, 0, num_units * unit_len);
+
+	ar->mem_chunks[idx].paddr = paddr;
+	ar->mem_chunks[idx].len = num_units * unit_len;
+	ar->mem_chunks[idx].req_id = req_id;
+
+	return num_units;
+
+}
+
+static void ath10k_pci_hif_chunk_free(struct ath10k *ar, int idx)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	pci_free_consistent(ar_pci->pdev,
+			    ar->mem_chunks[idx].len,
+			    ar->mem_chunks[idx].vaddr,
+			    ar->mem_chunks[idx].paddr);
+}
+
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.send_head		= ath10k_pci_hif_send_head,
 	.exchange_bmi_msg	= ath10k_pci_hif_exchange_bmi_msg,
@@ -1982,6 +2025,8 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
 	.suspend		= ath10k_pci_hif_suspend,
 	.resume			= ath10k_pci_hif_resume,
 #endif
+	.chunk_alloc		= ath10k_pci_hif_chunk_alloc,
+	.chunk_free		= ath10k_pci_hif_chunk_free,
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 6803ead..89de893 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -22,6 +22,7 @@
 #include "debug.h"
 #include "wmi.h"
 #include "mac.h"
+#include "hif.h"
 
 int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
 {
@@ -964,6 +965,46 @@ static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
 	ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
 }
 
+#define HOST_MEM_SIZE_UNIT 4
+
+static void ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+				      u32 num_units, u32 unit_len)
+{
+	u32 remaining_units, allocated_units, idx;
+
+	/* adjust the length to nearest multiple of unit size */
+	unit_len = (unit_len + (HOST_MEM_SIZE_UNIT - 1)) &
+		   (~(HOST_MEM_SIZE_UNIT - 1));
+
+	idx = ar->num_mem_chunks;
+	remaining_units = num_units;
+	while (remaining_units) {
+		allocated_units = ath10k_hif_chunk_alloc(ar, req_id, idx,
+							 remaining_units,
+							 unit_len);
+		if (allocated_units == 0) {
+			ath10k_warn("Memory allocation has failed (unit len %d units requested %d units allocated %d)\n",
+				    unit_len, num_units,
+				    (num_units - remaining_units));
+			ar->num_mem_chunks = idx;
+			break;
+		}
+
+		remaining_units -= allocated_units;
+		++idx;
+		if (idx == MAX_MEM_CHUNKS ) {
+			ath10k_warn("Maximum chunk limit has been reached (for memory units %d unit len %d requested by FW, allocated only %d\n",
+				    num_units,unit_len,
+				    (num_units - remaining_units));
+			ar->num_mem_chunks = idx;
+			break;
+		}
+	}
+	ar->num_mem_chunks = idx;
+
+}
+
+
 static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
 					      struct sk_buff *skb)
 {
@@ -1013,12 +1054,44 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
 			 ar->fw_version_build);
 	}
 
-	/* FIXME: it probably should be better to support this */
-	if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
-		ath10k_warn("target requested %d memory chunks; ignoring\n",
+	WARN_ON(__le32_to_cpu(ev->num_mem_reqs) > WMI_MAX_MEM_REQS);
+
+	if (__le32_to_cpu(ev->num_mem_reqs)) {
+		u32 num_units, i;
+
+		ath10k_warn("target requested %d memory chunks\n",
 			    __le32_to_cpu(ev->num_mem_reqs));
+
+		for (i = 0; i < __le32_to_cpu(ev->num_mem_reqs); ++i) {
+			num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
+			if (__le32_to_cpu(ev->mem_reqs[i].n_unit_info)) {
+				if (__le32_to_cpu(ev->mem_reqs[i].n_unit_info)
+				    & NUM_UNITS_IS_NUM_PEERS) {
+					/* number of units to allocate is
+					 * number of peers, 1 extra for self
+					 * peer on target
+					 * this needs to be tied, host and
+					 * target can get out of sync */
+					num_units = TARGET_NUM_PEERS + 1;
+				}
+			}
+
+			ath10k_dbg(ATH10K_DBG_WMI, "i %d req %d  num_units %d n_unit_info %d unit size %d actual units %d \n",
+				   i,
+				   __le32_to_cpu(ev->mem_reqs[i].req_id),
+				   __le32_to_cpu(ev->mem_reqs[i].num_units),
+				   __le32_to_cpu(ev->mem_reqs[i].n_unit_info),
+				   __le32_to_cpu(ev->mem_reqs[i].unit_size),
+				   num_units);
+
+			ath10k_wmi_alloc_host_mem(ar,
+				__le32_to_cpu(ev->mem_reqs[i].req_id),
+				num_units,
+				__le32_to_cpu(ev->mem_reqs[i].unit_size));
+		}
 	}
 
+
 	ath10k_dbg(ATH10K_DBG_WMI,
 		   "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
 		   __le32_to_cpu(ev->sw_version),
@@ -1186,6 +1259,18 @@ int ath10k_wmi_attach(struct ath10k *ar)
 
 void ath10k_wmi_detach(struct ath10k *ar)
 {
+	int i;
+
+	for (i = 0;  i < ar->num_mem_chunks; i++) {
+		ath10k_dbg(ATH10K_DBG_WMI, "free chunk %d len %d vaddr 0x%p paddr 0x%08llx\n",
+			   ar->num_mem_chunks,
+			   ar->mem_chunks[i].len,
+			   ar->mem_chunks[i].vaddr,
+			   ar->mem_chunks[i].paddr);
+
+		ar->hif.ops->chunk_free(ar, i);
+	}
+	ar->num_mem_chunks = 0;
 }
 
 int ath10k_wmi_connect_htc_service(struct ath10k *ar)
@@ -1373,12 +1458,40 @@ int ath10k_wmi_cmd_init(struct ath10k *ar)
 	config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
 	config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
 
-	buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
-	if (!buf)
-		return -ENOMEM;
+	if (ar->num_mem_chunks) {
+		int i;
+
+		buf = ath10k_wmi_alloc_skb(sizeof(*cmd) +
+			(sizeof(struct host_memory_chunk) * MAX_MEM_CHUNKS));
+		if (!buf)
+			return -ENOMEM;
+
+		cmd = (struct wmi_init_cmd *)buf->data;
+
+		ath10k_dbg(ATH10K_DBG_WMI, "Sending memory chunks info.\n");
+		cmd->num_host_mem_chunks = __cpu_to_le32(ar->num_mem_chunks);
+
+		for (i = 0; i < ar->num_mem_chunks; i++) {
+			cmd->host_mem_chunks[i].ptr =
+				__cpu_to_le32(ar->mem_chunks[i].paddr);
+			cmd->host_mem_chunks[i].size =
+				__cpu_to_le32(ar->mem_chunks[i].len);
+			cmd->host_mem_chunks[i].req_id =
+				__cpu_to_le32(ar->mem_chunks[i].req_id);
+
+			ath10k_dbg(ATH10K_DBG_WMI, "chunk %d len %d requested, addr 0x%x\n",
+				   i,
+				   cmd->host_mem_chunks[i].size,
+				   cmd->host_mem_chunks[i].ptr);
+		}
+	} else {
+		buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+		if (!buf)
+			return -ENOMEM;
+		cmd = (struct wmi_init_cmd *)buf->data;
+		cmd->num_host_mem_chunks = 0;
+	}
 
-	cmd = (struct wmi_init_cmd *)buf->data;
-	cmd->num_host_mem_chunks = 0;
 	memcpy(&cmd->resource_config, &config, sizeof(config));
 
 	ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 2c52c23..f586fb6 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -744,13 +744,12 @@ struct wlan_host_mem_req {
 	__le32 req_id;
 	/* size of the  of each unit */
 	__le32 unit_size;
-	/* flags to  indicate that
-	 * the number units is dependent
+	/* flags to  indicate that the number units is dependent
 	 * on number of resources(num vdevs num peers .. etc)
 	 */
-	__le32 num_unit_info;
+	__le32 n_unit_info;
 	/*
-	 * actual number of units to allocate . if flags in the num_unit_info
+	 * actual number of units to allocate . if flags in the n_unit_info
 	 * indicate that number of units is tied to number of a particular
 	 * resource to allocate then  num_units filed is set to 0 and host
 	 * will derive the number units from number of the resources it is
@@ -1012,6 +1011,11 @@ struct wmi_resource_config {
 	__le32 max_frag_entries;
 } __packed;
 
+#define WMI_MAX_MEM_REQS 16
+
+#define NUM_UNITS_IS_NUM_VDEVS   0x1
+#define NUM_UNITS_IS_NUM_PEERS   0x2
+
 /* strucutre describing host memory chunk. */
 struct host_memory_chunk {
 	/* id of the request that is passed up in service ready */
-- 
1.7.10




More information about the ath10k mailing list