diff options
author | Amitkumar Karwar <akarwar@marvell.com> | 2014-06-20 00:38:52 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-06-25 15:32:46 -0400 |
commit | 92c2538f55132d8e0e53945204cfb137e7d64b6b (patch) | |
tree | d9f22d8e73487b306740fa3653af85243235d8cc | |
parent | 5f4ef7197db98a40e43904806612a8ccf609c0a3 (diff) |
mwifiex: add firmware dump feature for PCIe
Firmware dump feature is added for PCIe based chipsets which can
be used with the help of ethtool commands.
1) Trigger firmware dump operation:
ethtool --set-dump mlan0 0xff
When the operation is completed, udev event will be sent to
trigger external application.
2) Following udev rule can be used to get the data from ethtool:
DRIVER=="mwifiex_pcie", ACTION=="change", RUN+="/sbin/mwifiex_pcie_fw_dump.sh"
mwifiex_pcie_fw_dump.sh: #!/bin/bash
ethtool --set-dump mlan0 0
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/ITCM.log
ethtool --set-dump mlan0 1
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/DTCM.log
ethtool --set-dump mlan0 2
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/SQRAM.log
ethtool --set-dump mlan0 3
ethtool --get-dump mlan0
ethtool --get-dump mlan0 data /tmp/IRAM.log
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/mwifiex/cmdevt.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/ethtool.c | 83 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/init.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.h | 29 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.c | 193 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.h | 10 |
7 files changed, 332 insertions, 1 deletions
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 8dee6c86f4f1..421322f5e5fb 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c | |||
@@ -960,6 +960,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context) | |||
960 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) | 960 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) |
961 | mwifiex_init_fw_complete(adapter); | 961 | mwifiex_init_fw_complete(adapter); |
962 | 962 | ||
963 | if (adapter->if_ops.fw_dump) | ||
964 | adapter->if_ops.fw_dump(adapter); | ||
965 | |||
963 | if (adapter->if_ops.card_reset) | 966 | if (adapter->if_ops.card_reset) |
964 | adapter->if_ops.card_reset(adapter); | 967 | adapter->if_ops.card_reset(adapter); |
965 | } | 968 | } |
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c index bfb39908b2c6..528bdfa6c60c 100644 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ b/drivers/net/wireless/mwifiex/ethtool.c | |||
@@ -64,7 +64,90 @@ static int mwifiex_ethtool_set_wol(struct net_device *dev, | |||
64 | return 0; | 64 | return 0; |
65 | } | 65 | } |
66 | 66 | ||
67 | static int | ||
68 | mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump) | ||
69 | { | ||
70 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
71 | struct mwifiex_adapter *adapter = priv->adapter; | ||
72 | struct memory_type_mapping *entry; | ||
73 | |||
74 | if (!adapter->if_ops.fw_dump) | ||
75 | return -ENOTSUPP; | ||
76 | |||
77 | dump->flag = adapter->curr_mem_idx; | ||
78 | dump->version = 1; | ||
79 | if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) { | ||
80 | entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx]; | ||
81 | dump->len = entry->mem_size; | ||
82 | } else { | ||
83 | dump->len = 0; | ||
84 | } | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int | ||
90 | mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump, | ||
91 | void *buffer) | ||
92 | { | ||
93 | u8 *p = buffer; | ||
94 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
95 | struct mwifiex_adapter *adapter = priv->adapter; | ||
96 | struct memory_type_mapping *entry; | ||
97 | |||
98 | if (!adapter->if_ops.fw_dump) | ||
99 | return -ENOTSUPP; | ||
100 | |||
101 | if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { | ||
102 | dev_err(adapter->dev, "firmware dump in progress!!\n"); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | |||
106 | entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx]; | ||
107 | |||
108 | if (!entry->mem_ptr) | ||
109 | return -EFAULT; | ||
110 | |||
111 | memcpy(p, entry->mem_ptr, entry->mem_size); | ||
112 | |||
113 | entry->mem_size = 0; | ||
114 | vfree(entry->mem_ptr); | ||
115 | entry->mem_ptr = NULL; | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val) | ||
121 | { | ||
122 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
123 | struct mwifiex_adapter *adapter = priv->adapter; | ||
124 | |||
125 | if (!adapter->if_ops.fw_dump) | ||
126 | return -ENOTSUPP; | ||
127 | |||
128 | if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { | ||
129 | dev_err(adapter->dev, "firmware dump in progress!!\n"); | ||
130 | return -EBUSY; | ||
131 | } | ||
132 | |||
133 | if (val->flag == MWIFIEX_FW_DUMP_IDX) { | ||
134 | adapter->curr_mem_idx = val->flag; | ||
135 | adapter->if_ops.fw_dump(adapter); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | if (val->flag < 0 || val->flag >= adapter->num_mem_types) | ||
140 | return -EINVAL; | ||
141 | |||
142 | adapter->curr_mem_idx = val->flag; | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
67 | const struct ethtool_ops mwifiex_ethtool_ops = { | 147 | const struct ethtool_ops mwifiex_ethtool_ops = { |
68 | .get_wol = mwifiex_ethtool_get_wol, | 148 | .get_wol = mwifiex_ethtool_get_wol, |
69 | .set_wol = mwifiex_ethtool_set_wol, | 149 | .set_wol = mwifiex_ethtool_set_wol, |
150 | .get_dump_flag = mwifiex_get_dump_flag, | ||
151 | .get_dump_data = mwifiex_get_dump_data, | ||
152 | .set_dump = mwifiex_set_dump, | ||
70 | }; | 153 | }; |
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 4ecd0b208ac6..2b68cf3aa336 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c | |||
@@ -382,6 +382,8 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) | |||
382 | static void | 382 | static void |
383 | mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) | 383 | mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) |
384 | { | 384 | { |
385 | int idx; | ||
386 | |||
385 | if (!adapter) { | 387 | if (!adapter) { |
386 | pr_err("%s: adapter is NULL\n", __func__); | 388 | pr_err("%s: adapter is NULL\n", __func__); |
387 | return; | 389 | return; |
@@ -396,7 +398,16 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) | |||
396 | dev_dbg(adapter->dev, "info: free cmd buffer\n"); | 398 | dev_dbg(adapter->dev, "info: free cmd buffer\n"); |
397 | mwifiex_free_cmd_buffer(adapter); | 399 | mwifiex_free_cmd_buffer(adapter); |
398 | 400 | ||
399 | dev_dbg(adapter->dev, "info: free scan table\n"); | 401 | for (idx = 0; idx < adapter->num_mem_types; idx++) { |
402 | struct memory_type_mapping *entry = | ||
403 | &adapter->mem_type_mapping_tbl[idx]; | ||
404 | |||
405 | if (entry->mem_ptr) { | ||
406 | vfree(entry->mem_ptr); | ||
407 | entry->mem_ptr = NULL; | ||
408 | } | ||
409 | entry->mem_size = 0; | ||
410 | } | ||
400 | 411 | ||
401 | if (adapter->sleep_cfm) | 412 | if (adapter->sleep_cfm) |
402 | dev_kfree_skb_any(adapter->sleep_cfm); | 413 | dev_kfree_skb_any(adapter->sleep_cfm); |
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 40e4dbd5b89d..31bd34d0f79b 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c | |||
@@ -879,6 +879,8 @@ mwifiex_add_card(void *card, struct semaphore *sem, | |||
879 | goto err_kmalloc; | 879 | goto err_kmalloc; |
880 | 880 | ||
881 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); | 881 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); |
882 | if (adapter->if_ops.iface_work) | ||
883 | INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work); | ||
882 | 884 | ||
883 | /* Register the device. Fill up the private data structure with relevant | 885 | /* Register the device. Fill up the private data structure with relevant |
884 | information from the card. */ | 886 | information from the card. */ |
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 1398afa84064..24791e2acf24 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/etherdevice.h> | 30 | #include <linux/etherdevice.h> |
31 | #include <net/sock.h> | 31 | #include <net/sock.h> |
32 | #include <net/lib80211.h> | 32 | #include <net/lib80211.h> |
33 | #include <linux/vmalloc.h> | ||
33 | #include <linux/firmware.h> | 34 | #include <linux/firmware.h> |
34 | #include <linux/ctype.h> | 35 | #include <linux/ctype.h> |
35 | #include <linux/of.h> | 36 | #include <linux/of.h> |
@@ -410,6 +411,28 @@ struct mwifiex_roc_cfg { | |||
410 | struct ieee80211_channel chan; | 411 | struct ieee80211_channel chan; |
411 | }; | 412 | }; |
412 | 413 | ||
414 | #define MWIFIEX_FW_DUMP_IDX 0xff | ||
415 | #define FW_DUMP_MAX_NAME_LEN 8 | ||
416 | #define FW_DUMP_HOST_READY 0xEE | ||
417 | #define FW_DUMP_DONE 0xFF | ||
418 | |||
419 | struct memory_type_mapping { | ||
420 | u8 mem_name[FW_DUMP_MAX_NAME_LEN]; | ||
421 | u8 *mem_ptr; | ||
422 | u32 mem_size; | ||
423 | u8 done_flag; | ||
424 | }; | ||
425 | |||
426 | enum rdwr_status { | ||
427 | RDWR_STATUS_SUCCESS = 0, | ||
428 | RDWR_STATUS_FAILURE = 1, | ||
429 | RDWR_STATUS_DONE = 2 | ||
430 | }; | ||
431 | |||
432 | enum mwifiex_iface_work_flags { | ||
433 | MWIFIEX_IFACE_WORK_FW_DUMP, | ||
434 | }; | ||
435 | |||
413 | struct mwifiex_adapter; | 436 | struct mwifiex_adapter; |
414 | struct mwifiex_private; | 437 | struct mwifiex_private; |
415 | 438 | ||
@@ -674,6 +697,7 @@ struct mwifiex_if_ops { | |||
674 | void (*card_reset) (struct mwifiex_adapter *); | 697 | void (*card_reset) (struct mwifiex_adapter *); |
675 | void (*fw_dump)(struct mwifiex_adapter *); | 698 | void (*fw_dump)(struct mwifiex_adapter *); |
676 | int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); | 699 | int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); |
700 | void (*iface_work)(struct work_struct *work); | ||
677 | }; | 701 | }; |
678 | 702 | ||
679 | struct mwifiex_adapter { | 703 | struct mwifiex_adapter { |
@@ -809,6 +833,11 @@ struct mwifiex_adapter { | |||
809 | bool ext_scan; | 833 | bool ext_scan; |
810 | u8 fw_api_ver; | 834 | u8 fw_api_ver; |
811 | u8 fw_key_api_major_ver, fw_key_api_minor_ver; | 835 | u8 fw_key_api_major_ver, fw_key_api_minor_ver; |
836 | struct work_struct iface_work; | ||
837 | unsigned long iface_work_flags; | ||
838 | struct memory_type_mapping *mem_type_mapping_tbl; | ||
839 | u8 num_mem_types; | ||
840 | u8 curr_mem_idx; | ||
812 | }; | 841 | }; |
813 | 842 | ||
814 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); | 843 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); |
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 574d4b597468..6ea8e9d314b7 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c | |||
@@ -37,6 +37,13 @@ static struct mwifiex_if_ops pcie_ops; | |||
37 | 37 | ||
38 | static struct semaphore add_remove_card_sem; | 38 | static struct semaphore add_remove_card_sem; |
39 | 39 | ||
40 | static struct memory_type_mapping mem_type_mapping_tbl[] = { | ||
41 | {"ITCM", NULL, 0, 0xF0}, | ||
42 | {"DTCM", NULL, 0, 0xF1}, | ||
43 | {"SQRAM", NULL, 0, 0xF2}, | ||
44 | {"IRAM", NULL, 0, 0xF3}, | ||
45 | }; | ||
46 | |||
40 | static int | 47 | static int |
41 | mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, | 48 | mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, |
42 | size_t size, int flags) | 49 | size_t size, int flags) |
@@ -192,6 +199,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, | |||
192 | card->pcie.reg = data->reg; | 199 | card->pcie.reg = data->reg; |
193 | card->pcie.blksz_fw_dl = data->blksz_fw_dl; | 200 | card->pcie.blksz_fw_dl = data->blksz_fw_dl; |
194 | card->pcie.tx_buf_size = data->tx_buf_size; | 201 | card->pcie.tx_buf_size = data->tx_buf_size; |
202 | card->pcie.supports_fw_dump = data->supports_fw_dump; | ||
195 | } | 203 | } |
196 | 204 | ||
197 | if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, | 205 | if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, |
@@ -221,6 +229,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) | |||
221 | if (!adapter || !adapter->priv_num) | 229 | if (!adapter || !adapter->priv_num) |
222 | return; | 230 | return; |
223 | 231 | ||
232 | cancel_work_sync(&adapter->iface_work); | ||
233 | |||
224 | if (user_rmmod) { | 234 | if (user_rmmod) { |
225 | #ifdef CONFIG_PM_SLEEP | 235 | #ifdef CONFIG_PM_SLEEP |
226 | if (adapter->is_suspended) | 236 | if (adapter->is_suspended) |
@@ -307,6 +317,17 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) | |||
307 | return 0; | 317 | return 0; |
308 | } | 318 | } |
309 | 319 | ||
320 | /* This function reads u8 data from PCIE card register. */ | ||
321 | static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, | ||
322 | int reg, u8 *data) | ||
323 | { | ||
324 | struct pcie_service_card *card = adapter->card; | ||
325 | |||
326 | *data = ioread8(card->pci_mmap1 + reg); | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
310 | /* | 331 | /* |
311 | * This function adds delay loop to ensure FW is awake before proceeding. | 332 | * This function adds delay loop to ensure FW is awake before proceeding. |
312 | */ | 333 | */ |
@@ -2173,6 +2194,174 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, | |||
2173 | return 0; | 2194 | return 0; |
2174 | } | 2195 | } |
2175 | 2196 | ||
2197 | /* This function read/write firmware */ | ||
2198 | static enum rdwr_status | ||
2199 | mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) | ||
2200 | { | ||
2201 | int ret, tries; | ||
2202 | u8 ctrl_data; | ||
2203 | struct pcie_service_card *card = adapter->card; | ||
2204 | const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; | ||
2205 | |||
2206 | ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); | ||
2207 | if (ret) { | ||
2208 | dev_err(adapter->dev, "PCIE write err\n"); | ||
2209 | return RDWR_STATUS_FAILURE; | ||
2210 | } | ||
2211 | |||
2212 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | ||
2213 | mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); | ||
2214 | if (ctrl_data == FW_DUMP_DONE) | ||
2215 | return RDWR_STATUS_SUCCESS; | ||
2216 | if (doneflag && ctrl_data == doneflag) | ||
2217 | return RDWR_STATUS_DONE; | ||
2218 | if (ctrl_data != FW_DUMP_HOST_READY) { | ||
2219 | dev_info(adapter->dev, | ||
2220 | "The ctrl reg was changed, re-try again!\n"); | ||
2221 | mwifiex_write_reg(adapter, reg->fw_dump_ctrl, | ||
2222 | FW_DUMP_HOST_READY); | ||
2223 | if (ret) { | ||
2224 | dev_err(adapter->dev, "PCIE write err\n"); | ||
2225 | return RDWR_STATUS_FAILURE; | ||
2226 | } | ||
2227 | } | ||
2228 | usleep_range(100, 200); | ||
2229 | } | ||
2230 | |||
2231 | dev_err(adapter->dev, "Fail to pull ctrl_data\n"); | ||
2232 | return RDWR_STATUS_FAILURE; | ||
2233 | } | ||
2234 | |||
2235 | /* This function dump firmware memory to file */ | ||
2236 | static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter) | ||
2237 | { | ||
2238 | struct pcie_service_card *card = adapter->card; | ||
2239 | const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; | ||
2240 | unsigned int reg, reg_start, reg_end; | ||
2241 | struct timeval t; | ||
2242 | u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; | ||
2243 | enum rdwr_status stat; | ||
2244 | u32 memory_size; | ||
2245 | static char *env[] = { "DRIVER=mwifiex_pcie", "EVENT=fw_dump", NULL }; | ||
2246 | |||
2247 | if (!card->pcie.supports_fw_dump) | ||
2248 | return; | ||
2249 | |||
2250 | for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { | ||
2251 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; | ||
2252 | |||
2253 | if (entry->mem_ptr) { | ||
2254 | vfree(entry->mem_ptr); | ||
2255 | entry->mem_ptr = NULL; | ||
2256 | } | ||
2257 | entry->mem_size = 0; | ||
2258 | } | ||
2259 | |||
2260 | do_gettimeofday(&t); | ||
2261 | dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n", | ||
2262 | (u32)t.tv_sec, (u32)t.tv_usec); | ||
2263 | |||
2264 | /* Read the number of the memories which will dump */ | ||
2265 | stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); | ||
2266 | if (stat == RDWR_STATUS_FAILURE) | ||
2267 | goto done; | ||
2268 | |||
2269 | reg = creg->fw_dump_start; | ||
2270 | mwifiex_read_reg_byte(adapter, reg, &dump_num); | ||
2271 | |||
2272 | /* Read the length of every memory which will dump */ | ||
2273 | for (idx = 0; idx < dump_num; idx++) { | ||
2274 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; | ||
2275 | |||
2276 | stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); | ||
2277 | if (stat == RDWR_STATUS_FAILURE) | ||
2278 | goto done; | ||
2279 | |||
2280 | memory_size = 0; | ||
2281 | reg = creg->fw_dump_start; | ||
2282 | for (i = 0; i < 4; i++) { | ||
2283 | mwifiex_read_reg_byte(adapter, reg, &read_reg); | ||
2284 | memory_size |= (read_reg << (i * 8)); | ||
2285 | reg++; | ||
2286 | } | ||
2287 | |||
2288 | if (memory_size == 0) { | ||
2289 | dev_info(adapter->dev, "Firmware dump Finished!\n"); | ||
2290 | break; | ||
2291 | } | ||
2292 | |||
2293 | dev_info(adapter->dev, | ||
2294 | "%s_SIZE=0x%x\n", entry->mem_name, memory_size); | ||
2295 | entry->mem_ptr = vmalloc(memory_size + 1); | ||
2296 | entry->mem_size = memory_size; | ||
2297 | if (!entry->mem_ptr) { | ||
2298 | dev_err(adapter->dev, | ||
2299 | "Vmalloc %s failed\n", entry->mem_name); | ||
2300 | goto done; | ||
2301 | } | ||
2302 | dbg_ptr = entry->mem_ptr; | ||
2303 | end_ptr = dbg_ptr + memory_size; | ||
2304 | |||
2305 | doneflag = entry->done_flag; | ||
2306 | do_gettimeofday(&t); | ||
2307 | dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n", | ||
2308 | entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec); | ||
2309 | |||
2310 | do { | ||
2311 | stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); | ||
2312 | if (RDWR_STATUS_FAILURE == stat) | ||
2313 | goto done; | ||
2314 | |||
2315 | reg_start = creg->fw_dump_start; | ||
2316 | reg_end = creg->fw_dump_end; | ||
2317 | for (reg = reg_start; reg <= reg_end; reg++) { | ||
2318 | mwifiex_read_reg_byte(adapter, reg, dbg_ptr); | ||
2319 | if (dbg_ptr < end_ptr) | ||
2320 | dbg_ptr++; | ||
2321 | else | ||
2322 | dev_err(adapter->dev, | ||
2323 | "Allocated buf not enough\n"); | ||
2324 | } | ||
2325 | |||
2326 | if (stat != RDWR_STATUS_DONE) | ||
2327 | continue; | ||
2328 | |||
2329 | dev_info(adapter->dev, "%s done: size=0x%tx\n", | ||
2330 | entry->mem_name, dbg_ptr - entry->mem_ptr); | ||
2331 | break; | ||
2332 | } while (true); | ||
2333 | } | ||
2334 | do_gettimeofday(&t); | ||
2335 | dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n", | ||
2336 | (u32)t.tv_sec, (u32)t.tv_usec); | ||
2337 | |||
2338 | kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env); | ||
2339 | |||
2340 | done: | ||
2341 | adapter->curr_mem_idx = 0; | ||
2342 | } | ||
2343 | |||
2344 | static void mwifiex_pcie_work(struct work_struct *work) | ||
2345 | { | ||
2346 | struct mwifiex_adapter *adapter = | ||
2347 | container_of(work, struct mwifiex_adapter, iface_work); | ||
2348 | |||
2349 | if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP, | ||
2350 | &adapter->iface_work_flags)) | ||
2351 | mwifiex_pcie_fw_dump_work(adapter); | ||
2352 | } | ||
2353 | |||
2354 | /* This function dumps FW information */ | ||
2355 | static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) | ||
2356 | { | ||
2357 | if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags)) | ||
2358 | return; | ||
2359 | |||
2360 | set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags); | ||
2361 | |||
2362 | schedule_work(&adapter->iface_work); | ||
2363 | } | ||
2364 | |||
2176 | /* | 2365 | /* |
2177 | * This function initializes the PCI-E host memory space, WCB rings, etc. | 2366 | * This function initializes the PCI-E host memory space, WCB rings, etc. |
2178 | * | 2367 | * |
@@ -2342,6 +2531,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) | |||
2342 | 2531 | ||
2343 | adapter->dev = &pdev->dev; | 2532 | adapter->dev = &pdev->dev; |
2344 | adapter->tx_buf_size = card->pcie.tx_buf_size; | 2533 | adapter->tx_buf_size = card->pcie.tx_buf_size; |
2534 | adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; | ||
2535 | adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); | ||
2345 | strcpy(adapter->fw_name, card->pcie.firmware); | 2536 | strcpy(adapter->fw_name, card->pcie.firmware); |
2346 | 2537 | ||
2347 | return 0; | 2538 | return 0; |
@@ -2394,6 +2585,8 @@ static struct mwifiex_if_ops pcie_ops = { | |||
2394 | .cleanup_mpa_buf = NULL, | 2585 | .cleanup_mpa_buf = NULL, |
2395 | .init_fw_port = mwifiex_pcie_init_fw_port, | 2586 | .init_fw_port = mwifiex_pcie_init_fw_port, |
2396 | .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, | 2587 | .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, |
2588 | .fw_dump = mwifiex_pcie_fw_dump, | ||
2589 | .iface_work = mwifiex_pcie_work, | ||
2397 | }; | 2590 | }; |
2398 | 2591 | ||
2399 | /* | 2592 | /* |
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index e8ec561f8a64..ee073f5c9f0d 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h | |||
@@ -129,6 +129,9 @@ struct mwifiex_pcie_card_reg { | |||
129 | u32 ring_tx_start_ptr; | 129 | u32 ring_tx_start_ptr; |
130 | u8 pfu_enabled; | 130 | u8 pfu_enabled; |
131 | u8 sleep_cookie; | 131 | u8 sleep_cookie; |
132 | u16 fw_dump_ctrl; | ||
133 | u16 fw_dump_start; | ||
134 | u16 fw_dump_end; | ||
132 | }; | 135 | }; |
133 | 136 | ||
134 | static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { | 137 | static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { |
@@ -191,6 +194,9 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { | |||
191 | .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, | 194 | .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, |
192 | .pfu_enabled = 1, | 195 | .pfu_enabled = 1, |
193 | .sleep_cookie = 0, | 196 | .sleep_cookie = 0, |
197 | .fw_dump_ctrl = 0xcf4, | ||
198 | .fw_dump_start = 0xcf8, | ||
199 | .fw_dump_end = 0xcff | ||
194 | }; | 200 | }; |
195 | 201 | ||
196 | struct mwifiex_pcie_device { | 202 | struct mwifiex_pcie_device { |
@@ -198,6 +204,7 @@ struct mwifiex_pcie_device { | |||
198 | const struct mwifiex_pcie_card_reg *reg; | 204 | const struct mwifiex_pcie_card_reg *reg; |
199 | u16 blksz_fw_dl; | 205 | u16 blksz_fw_dl; |
200 | u16 tx_buf_size; | 206 | u16 tx_buf_size; |
207 | bool supports_fw_dump; | ||
201 | }; | 208 | }; |
202 | 209 | ||
203 | static const struct mwifiex_pcie_device mwifiex_pcie8766 = { | 210 | static const struct mwifiex_pcie_device mwifiex_pcie8766 = { |
@@ -205,6 +212,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8766 = { | |||
205 | .reg = &mwifiex_reg_8766, | 212 | .reg = &mwifiex_reg_8766, |
206 | .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, | 213 | .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, |
207 | .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, | 214 | .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, |
215 | .supports_fw_dump = false, | ||
208 | }; | 216 | }; |
209 | 217 | ||
210 | static const struct mwifiex_pcie_device mwifiex_pcie8897 = { | 218 | static const struct mwifiex_pcie_device mwifiex_pcie8897 = { |
@@ -212,6 +220,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = { | |||
212 | .reg = &mwifiex_reg_8897, | 220 | .reg = &mwifiex_reg_8897, |
213 | .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, | 221 | .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, |
214 | .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, | 222 | .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, |
223 | .supports_fw_dump = true, | ||
215 | }; | 224 | }; |
216 | 225 | ||
217 | struct mwifiex_evt_buf_desc { | 226 | struct mwifiex_evt_buf_desc { |
@@ -322,4 +331,5 @@ mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) | |||
322 | 331 | ||
323 | return 0; | 332 | return 0; |
324 | } | 333 | } |
334 | |||
325 | #endif /* _MWIFIEX_PCIE_H */ | 335 | #endif /* _MWIFIEX_PCIE_H */ |