diff options
author | Bartosz Markowski <bartosz.markowski@tieto.com> | 2013-09-26 11:47:11 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2013-09-27 07:58:14 -0400 |
commit | b3effe61a1a3b8f20fa4b4d30c4390a6b81a6fc2 (patch) | |
tree | 24a06e9a7498870290d51dff79f8b9d6a5df60ad /drivers/net | |
parent | 08ba7b6b6f4bd6a912d0b6825f4e2d4e7fb4ddac (diff) |
ath10k: implement host memory chunks
10.X 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@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 12 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 126 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.h | 3 |
3 files changed, 133 insertions, 8 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index acfee7c51b9e..e2a2658cf841 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h | |||
@@ -102,12 +102,24 @@ struct ath10k_bmi { | |||
102 | bool done_sent; | 102 | bool done_sent; |
103 | }; | 103 | }; |
104 | 104 | ||
105 | #define ATH10K_MAX_MEM_REQS 16 | ||
106 | |||
107 | struct ath10k_mem_chunk { | ||
108 | void *vaddr; | ||
109 | dma_addr_t paddr; | ||
110 | u32 len; | ||
111 | u32 req_id; | ||
112 | }; | ||
113 | |||
105 | struct ath10k_wmi { | 114 | struct ath10k_wmi { |
106 | enum ath10k_htc_ep_id eid; | 115 | enum ath10k_htc_ep_id eid; |
107 | struct completion service_ready; | 116 | struct completion service_ready; |
108 | struct completion unified_ready; | 117 | struct completion unified_ready; |
109 | wait_queue_head_t tx_credits_wq; | 118 | wait_queue_head_t tx_credits_wq; |
110 | struct wmi_cmd_map *cmd; | 119 | struct wmi_cmd_map *cmd; |
120 | |||
121 | u32 num_mem_chunks; | ||
122 | struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS]; | ||
111 | }; | 123 | }; |
112 | 124 | ||
113 | struct ath10k_peer_stat { | 125 | struct ath10k_peer_stat { |
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ed79d325b610..97ba23e78abf 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c | |||
@@ -1230,6 +1230,37 @@ static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, | |||
1230 | ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); | 1230 | ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); |
1231 | } | 1231 | } |
1232 | 1232 | ||
1233 | static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, | ||
1234 | u32 num_units, u32 unit_len) | ||
1235 | { | ||
1236 | dma_addr_t paddr; | ||
1237 | u32 pool_size; | ||
1238 | int idx = ar->wmi.num_mem_chunks; | ||
1239 | |||
1240 | pool_size = num_units * round_up(unit_len, 4); | ||
1241 | |||
1242 | if (!pool_size) | ||
1243 | return -EINVAL; | ||
1244 | |||
1245 | ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev, | ||
1246 | pool_size, | ||
1247 | &paddr, | ||
1248 | GFP_ATOMIC); | ||
1249 | if (!ar->wmi.mem_chunks[idx].vaddr) { | ||
1250 | ath10k_warn("failed to allocate memory chunk\n"); | ||
1251 | return -ENOMEM; | ||
1252 | } | ||
1253 | |||
1254 | memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size); | ||
1255 | |||
1256 | ar->wmi.mem_chunks[idx].paddr = paddr; | ||
1257 | ar->wmi.mem_chunks[idx].len = pool_size; | ||
1258 | ar->wmi.mem_chunks[idx].req_id = req_id; | ||
1259 | ar->wmi.num_mem_chunks++; | ||
1260 | |||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1233 | static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, | 1264 | static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, |
1234 | struct sk_buff *skb) | 1265 | struct sk_buff *skb) |
1235 | { | 1266 | { |
@@ -1304,6 +1335,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, | |||
1304 | static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, | 1335 | static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, |
1305 | struct sk_buff *skb) | 1336 | struct sk_buff *skb) |
1306 | { | 1337 | { |
1338 | u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; | ||
1339 | int ret; | ||
1307 | struct wmi_service_ready_event_10x *ev = (void *)skb->data; | 1340 | struct wmi_service_ready_event_10x *ev = (void *)skb->data; |
1308 | 1341 | ||
1309 | if (skb->len < sizeof(*ev)) { | 1342 | if (skb->len < sizeof(*ev)) { |
@@ -1342,13 +1375,50 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, | |||
1342 | ar->fw_version_minor); | 1375 | ar->fw_version_minor); |
1343 | } | 1376 | } |
1344 | 1377 | ||
1345 | /* FIXME: it probably should be better to support this. | 1378 | num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs); |
1346 | TODO: Next patch introduce memory chunks. It's a must for 10.x FW */ | 1379 | |
1347 | if (__le32_to_cpu(ev->num_mem_reqs) > 0) { | 1380 | if (num_mem_reqs > ATH10K_MAX_MEM_REQS) { |
1348 | ath10k_warn("target requested %d memory chunks; ignoring\n", | 1381 | ath10k_warn("requested memory chunks number (%d) exceeds the limit\n", |
1349 | __le32_to_cpu(ev->num_mem_reqs)); | 1382 | num_mem_reqs); |
1383 | return; | ||
1350 | } | 1384 | } |
1351 | 1385 | ||
1386 | if (!num_mem_reqs) | ||
1387 | goto exit; | ||
1388 | |||
1389 | ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n", | ||
1390 | num_mem_reqs); | ||
1391 | |||
1392 | for (i = 0; i < num_mem_reqs; ++i) { | ||
1393 | req_id = __le32_to_cpu(ev->mem_reqs[i].req_id); | ||
1394 | num_units = __le32_to_cpu(ev->mem_reqs[i].num_units); | ||
1395 | unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size); | ||
1396 | num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info); | ||
1397 | |||
1398 | if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) | ||
1399 | /* number of units to allocate is number of | ||
1400 | * peers, 1 extra for self peer on target */ | ||
1401 | /* this needs to be tied, host and target | ||
1402 | * can get out of sync */ | ||
1403 | num_units = TARGET_NUM_PEERS + 1; | ||
1404 | else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) | ||
1405 | num_units = TARGET_NUM_VDEVS + 1; | ||
1406 | |||
1407 | ath10k_dbg(ATH10K_DBG_WMI, | ||
1408 | "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n", | ||
1409 | req_id, | ||
1410 | __le32_to_cpu(ev->mem_reqs[i].num_units), | ||
1411 | num_unit_info, | ||
1412 | unit_size, | ||
1413 | num_units); | ||
1414 | |||
1415 | ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units, | ||
1416 | unit_size); | ||
1417 | if (ret) | ||
1418 | return; | ||
1419 | } | ||
1420 | |||
1421 | exit: | ||
1352 | ath10k_dbg(ATH10K_DBG_WMI, | 1422 | ath10k_dbg(ATH10K_DBG_WMI, |
1353 | "wmi event service ready sw_ver 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", | 1423 | "wmi event service ready sw_ver 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", |
1354 | __le32_to_cpu(ev->sw_version), | 1424 | __le32_to_cpu(ev->sw_version), |
@@ -1645,6 +1715,17 @@ int ath10k_wmi_attach(struct ath10k *ar) | |||
1645 | 1715 | ||
1646 | void ath10k_wmi_detach(struct ath10k *ar) | 1716 | void ath10k_wmi_detach(struct ath10k *ar) |
1647 | { | 1717 | { |
1718 | int i; | ||
1719 | |||
1720 | /* free the host memory chunks requested by firmware */ | ||
1721 | for (i = 0; i < ar->wmi.num_mem_chunks; i++) { | ||
1722 | dma_free_coherent(ar->dev, | ||
1723 | ar->wmi.mem_chunks[i].len, | ||
1724 | ar->wmi.mem_chunks[i].vaddr, | ||
1725 | ar->wmi.mem_chunks[i].paddr); | ||
1726 | } | ||
1727 | |||
1728 | ar->wmi.num_mem_chunks = 0; | ||
1648 | } | 1729 | } |
1649 | 1730 | ||
1650 | int ath10k_wmi_connect_htc_service(struct ath10k *ar) | 1731 | int ath10k_wmi_connect_htc_service(struct ath10k *ar) |
@@ -1781,7 +1862,8 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) | |||
1781 | struct wmi_init_cmd *cmd; | 1862 | struct wmi_init_cmd *cmd; |
1782 | struct sk_buff *buf; | 1863 | struct sk_buff *buf; |
1783 | struct wmi_resource_config config = {}; | 1864 | struct wmi_resource_config config = {}; |
1784 | u32 val; | 1865 | u32 len, val; |
1866 | int i; | ||
1785 | 1867 | ||
1786 | config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); | 1868 | config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); |
1787 | config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); | 1869 | config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); |
@@ -1834,12 +1916,40 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) | |||
1834 | config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); | 1916 | config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); |
1835 | config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); | 1917 | config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); |
1836 | 1918 | ||
1837 | buf = ath10k_wmi_alloc_skb(sizeof(*cmd)); | 1919 | len = sizeof(*cmd) + |
1920 | (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); | ||
1921 | |||
1922 | buf = ath10k_wmi_alloc_skb(len); | ||
1838 | if (!buf) | 1923 | if (!buf) |
1839 | return -ENOMEM; | 1924 | return -ENOMEM; |
1840 | 1925 | ||
1841 | cmd = (struct wmi_init_cmd *)buf->data; | 1926 | cmd = (struct wmi_init_cmd *)buf->data; |
1842 | cmd->num_host_mem_chunks = 0; | 1927 | |
1928 | if (ar->wmi.num_mem_chunks == 0) { | ||
1929 | cmd->num_host_mem_chunks = 0; | ||
1930 | goto out; | ||
1931 | } | ||
1932 | |||
1933 | ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", | ||
1934 | __cpu_to_le32(ar->wmi.num_mem_chunks)); | ||
1935 | |||
1936 | cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); | ||
1937 | |||
1938 | for (i = 0; i < ar->wmi.num_mem_chunks; i++) { | ||
1939 | cmd->host_mem_chunks[i].ptr = | ||
1940 | __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); | ||
1941 | cmd->host_mem_chunks[i].size = | ||
1942 | __cpu_to_le32(ar->wmi.mem_chunks[i].len); | ||
1943 | cmd->host_mem_chunks[i].req_id = | ||
1944 | __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); | ||
1945 | |||
1946 | ath10k_dbg(ATH10K_DBG_WMI, | ||
1947 | "wmi chunk %d len %d requested, addr 0x%x\n", | ||
1948 | i, | ||
1949 | cmd->host_mem_chunks[i].size, | ||
1950 | cmd->host_mem_chunks[i].ptr); | ||
1951 | } | ||
1952 | out: | ||
1843 | memcpy(&cmd->resource_config, &config, sizeof(config)); | 1953 | memcpy(&cmd->resource_config, &config, sizeof(config)); |
1844 | 1954 | ||
1845 | ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); | 1955 | 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 a0cfdfd87faf..56339d2e338a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h | |||
@@ -1377,6 +1377,9 @@ struct wmi_resource_config { | |||
1377 | __le32 max_frag_entries; | 1377 | __le32 max_frag_entries; |
1378 | } __packed; | 1378 | } __packed; |
1379 | 1379 | ||
1380 | #define NUM_UNITS_IS_NUM_VDEVS 0x1 | ||
1381 | #define NUM_UNITS_IS_NUM_PEERS 0x2 | ||
1382 | |||
1380 | /* strucutre describing host memory chunk. */ | 1383 | /* strucutre describing host memory chunk. */ |
1381 | struct host_memory_chunk { | 1384 | struct host_memory_chunk { |
1382 | /* id of the request that is passed up in service ready */ | 1385 | /* id of the request that is passed up in service ready */ |