diff options
author | Amitkumar Karwar <akarwar@marvell.com> | 2014-04-17 14:47:00 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-04-22 15:06:31 -0400 |
commit | e050c76fcf49599c5b98e4614392dc87c69123a6 (patch) | |
tree | 2ab5ff830cc3aadb0b960077057c47b58aae449f /drivers/net/wireless | |
parent | 1c09bf682cbcad8ddeea6ac69decda411f88dd35 (diff) |
mwifiex: add firmware dump feature for PCIe
Firmware dump feature is added for PCIe based chipsets.
Separate file will be created at /var/log/fw_dump_*
for each memory segment.
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>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/mwifiex/cmdevt.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.c | 227 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.h | 27 |
5 files changed, 261 insertions, 0 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/main.c b/drivers/net/wireless/mwifiex/main.c index cbabc12fbda3..6bc645a120fa 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c | |||
@@ -881,6 +881,8 @@ mwifiex_add_card(void *card, struct semaphore *sem, | |||
881 | goto err_kmalloc; | 881 | goto err_kmalloc; |
882 | 882 | ||
883 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); | 883 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); |
884 | if (adapter->if_ops.iface_work) | ||
885 | INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work); | ||
884 | 886 | ||
885 | /* Register the device. Fill up the private data structure with relevant | 887 | /* Register the device. Fill up the private data structure with relevant |
886 | information from the card. */ | 888 | information from the card. */ |
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 34181192a666..d70457b26e26 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h | |||
@@ -674,6 +674,7 @@ struct mwifiex_if_ops { | |||
674 | void (*card_reset) (struct mwifiex_adapter *); | 674 | void (*card_reset) (struct mwifiex_adapter *); |
675 | void (*fw_dump)(struct mwifiex_adapter *); | 675 | void (*fw_dump)(struct mwifiex_adapter *); |
676 | int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); | 676 | int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); |
677 | void (*iface_work)(struct work_struct *work); | ||
677 | }; | 678 | }; |
678 | 679 | ||
679 | struct mwifiex_adapter { | 680 | struct mwifiex_adapter { |
@@ -809,6 +810,7 @@ struct mwifiex_adapter { | |||
809 | bool ext_scan; | 810 | bool ext_scan; |
810 | u8 fw_api_ver; | 811 | u8 fw_api_ver; |
811 | u8 fw_key_api_major_ver, fw_key_api_minor_ver; | 812 | u8 fw_key_api_major_ver, fw_key_api_minor_ver; |
813 | struct work_struct iface_work; | ||
812 | }; | 814 | }; |
813 | 815 | ||
814 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); | 816 | 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 c2cfeec466d8..51989b31137a 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c | |||
@@ -37,6 +37,9 @@ 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 | /* enum mwifiex_pcie_work_flags bitmap */ | ||
41 | static unsigned long pcie_work_flags; | ||
42 | |||
40 | static int | 43 | static int |
41 | mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, | 44 | mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, |
42 | size_t size, int flags) | 45 | size_t size, int flags) |
@@ -221,6 +224,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) | |||
221 | if (!adapter || !adapter->priv_num) | 224 | if (!adapter || !adapter->priv_num) |
222 | return; | 225 | return; |
223 | 226 | ||
227 | cancel_work_sync(&adapter->iface_work); | ||
228 | |||
224 | if (user_rmmod) { | 229 | if (user_rmmod) { |
225 | #ifdef CONFIG_PM_SLEEP | 230 | #ifdef CONFIG_PM_SLEEP |
226 | if (adapter->is_suspended) | 231 | if (adapter->is_suspended) |
@@ -307,6 +312,17 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) | |||
307 | return 0; | 312 | return 0; |
308 | } | 313 | } |
309 | 314 | ||
315 | /* This function reads u8 data from PCIE card register. */ | ||
316 | static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, | ||
317 | int reg, u8 *data) | ||
318 | { | ||
319 | struct pcie_service_card *card = adapter->card; | ||
320 | |||
321 | *data = ioread8(card->pci_mmap1 + reg); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
310 | /* | 326 | /* |
311 | * This function adds delay loop to ensure FW is awake before proceeding. | 327 | * This function adds delay loop to ensure FW is awake before proceeding. |
312 | */ | 328 | */ |
@@ -2172,6 +2188,215 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, | |||
2172 | return 0; | 2188 | return 0; |
2173 | } | 2189 | } |
2174 | 2190 | ||
2191 | /* This function read/write firmware */ | ||
2192 | static enum rdwr_status | ||
2193 | mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) | ||
2194 | { | ||
2195 | int ret, tries; | ||
2196 | u8 ctrl_data; | ||
2197 | |||
2198 | ret = mwifiex_write_reg(adapter, DEBUG_DUMP_CTRL_REG, DEBUG_HOST_READY); | ||
2199 | if (ret) { | ||
2200 | dev_err(adapter->dev, "PCIE write err\n"); | ||
2201 | return RDWR_STATUS_FAILURE; | ||
2202 | } | ||
2203 | |||
2204 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | ||
2205 | mwifiex_read_reg_byte(adapter, DEBUG_DUMP_CTRL_REG, &ctrl_data); | ||
2206 | if (ctrl_data == DEBUG_FW_DONE) | ||
2207 | return RDWR_STATUS_SUCCESS; | ||
2208 | if (doneflag && ctrl_data == doneflag) | ||
2209 | return RDWR_STATUS_DONE; | ||
2210 | if (ctrl_data != DEBUG_HOST_READY) { | ||
2211 | dev_info(adapter->dev, | ||
2212 | "The ctrl reg was changed, re-try again!\n"); | ||
2213 | mwifiex_write_reg(adapter, DEBUG_DUMP_CTRL_REG, | ||
2214 | DEBUG_HOST_READY); | ||
2215 | if (ret) { | ||
2216 | dev_err(adapter->dev, "PCIE write err\n"); | ||
2217 | return RDWR_STATUS_FAILURE; | ||
2218 | } | ||
2219 | } | ||
2220 | usleep_range(100, 200); | ||
2221 | } | ||
2222 | |||
2223 | dev_err(adapter->dev, "Fail to pull ctrl_data\n"); | ||
2224 | return RDWR_STATUS_FAILURE; | ||
2225 | } | ||
2226 | |||
2227 | /* This function dump firmware memory to file */ | ||
2228 | static void mwifiex_pcie_fw_dump_work(struct work_struct *work) | ||
2229 | { | ||
2230 | struct mwifiex_adapter *adapter = | ||
2231 | container_of(work, struct mwifiex_adapter, iface_work); | ||
2232 | unsigned int reg, reg_start, reg_end; | ||
2233 | u8 *dbg_ptr; | ||
2234 | struct timeval t; | ||
2235 | u8 dump_num = 0, idx, i, read_reg, doneflag = 0; | ||
2236 | enum rdwr_status stat; | ||
2237 | u32 memory_size; | ||
2238 | u8 filename[MAX_FULL_NAME_LEN]; | ||
2239 | mm_segment_t fs; | ||
2240 | loff_t pos; | ||
2241 | u8 *end_ptr; | ||
2242 | u8 *name_prefix = "/var/log/fw_dump_"; | ||
2243 | struct memory_type_mapping mem_type_mapping_tbl[] = { | ||
2244 | {"ITCM", NULL, NULL, 0xF0}, | ||
2245 | {"DTCM", NULL, NULL, 0xF1}, | ||
2246 | {"SQRAM", NULL, NULL, 0xF2}, | ||
2247 | {"IRAM", NULL, NULL, 0xF3}, | ||
2248 | }; | ||
2249 | |||
2250 | if (!adapter) { | ||
2251 | dev_err(adapter->dev, "Could not dump firmwware info\n"); | ||
2252 | return; | ||
2253 | } | ||
2254 | |||
2255 | do_gettimeofday(&t); | ||
2256 | dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n", | ||
2257 | (u32)t.tv_sec, (u32)t.tv_usec); | ||
2258 | |||
2259 | /* Read the number of the memories which will dump */ | ||
2260 | stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); | ||
2261 | if (stat == RDWR_STATUS_FAILURE) | ||
2262 | goto done; | ||
2263 | |||
2264 | reg = DEBUG_DUMP_START_REG; | ||
2265 | mwifiex_read_reg_byte(adapter, reg, &dump_num); | ||
2266 | |||
2267 | /* Read the length of every memory which will dump */ | ||
2268 | for (idx = 0; idx < dump_num; idx++) { | ||
2269 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; | ||
2270 | |||
2271 | stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); | ||
2272 | if (stat == RDWR_STATUS_FAILURE) | ||
2273 | goto done; | ||
2274 | |||
2275 | memory_size = 0; | ||
2276 | reg = DEBUG_DUMP_START_REG; | ||
2277 | for (i = 0; i < 4; i++) { | ||
2278 | mwifiex_read_reg_byte(adapter, reg, &read_reg); | ||
2279 | memory_size |= (read_reg << (i * 8)); | ||
2280 | reg++; | ||
2281 | } | ||
2282 | |||
2283 | if (memory_size == 0) { | ||
2284 | dev_info(adapter->dev, "Firmware dump Finished!\n"); | ||
2285 | break; | ||
2286 | } | ||
2287 | |||
2288 | dev_info(adapter->dev, | ||
2289 | "%s_SIZE=0x%x\n", entry->mem_name, memory_size); | ||
2290 | entry->mem_ptr = vmalloc(memory_size + 1); | ||
2291 | if (!entry->mem_ptr) { | ||
2292 | dev_err(adapter->dev, | ||
2293 | "Vmalloc %s failed\n", entry->mem_name); | ||
2294 | goto done; | ||
2295 | } | ||
2296 | dbg_ptr = entry->mem_ptr; | ||
2297 | end_ptr = dbg_ptr + memory_size; | ||
2298 | |||
2299 | doneflag = entry->done_flag; | ||
2300 | do_gettimeofday(&t); | ||
2301 | dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n", | ||
2302 | entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec); | ||
2303 | |||
2304 | do { | ||
2305 | stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); | ||
2306 | if (RDWR_STATUS_FAILURE == stat) | ||
2307 | goto done; | ||
2308 | |||
2309 | reg_start = DEBUG_DUMP_START_REG; | ||
2310 | reg_end = DEBUG_DUMP_END_REG; | ||
2311 | for (reg = reg_start; reg <= reg_end; reg++) { | ||
2312 | mwifiex_read_reg_byte(adapter, reg, dbg_ptr); | ||
2313 | if (dbg_ptr < end_ptr) | ||
2314 | dbg_ptr++; | ||
2315 | else | ||
2316 | dev_err(adapter->dev, | ||
2317 | "Allocated buf not enough\n"); | ||
2318 | } | ||
2319 | |||
2320 | if (stat != RDWR_STATUS_DONE) | ||
2321 | continue; | ||
2322 | |||
2323 | dev_info(adapter->dev, "%s done: size=0x%lx\n", | ||
2324 | entry->mem_name, dbg_ptr - entry->mem_ptr); | ||
2325 | memset(filename, 0, sizeof(filename)); | ||
2326 | memcpy(filename, name_prefix, strlen(name_prefix)); | ||
2327 | strcat(filename, entry->mem_name); | ||
2328 | do_gettimeofday(&t); | ||
2329 | entry->file_mem = filp_open(filename, O_CREAT | O_RDWR, | ||
2330 | 0644); | ||
2331 | if (IS_ERR(entry->file_mem)) { | ||
2332 | dev_info(adapter->dev, | ||
2333 | "Create %s file failed at %s, opening another dir /tmp\n", | ||
2334 | entry->mem_name, filename); | ||
2335 | memset(filename, 0, sizeof(filename)); | ||
2336 | sprintf(filename, "%s%s", "/tmp/fw_dump_", | ||
2337 | entry->mem_name); | ||
2338 | entry->file_mem = | ||
2339 | filp_open(filename, | ||
2340 | O_CREAT | O_RDWR, 0644); | ||
2341 | } | ||
2342 | if (!IS_ERR(entry->file_mem)) { | ||
2343 | dev_info(adapter->dev, | ||
2344 | "Start to save the output : %u.%06u, please wait...\n", | ||
2345 | (u32)t.tv_sec, (u32)t.tv_usec); | ||
2346 | fs = get_fs(); | ||
2347 | set_fs(KERNEL_DS); | ||
2348 | pos = 0; | ||
2349 | vfs_write(entry->file_mem, | ||
2350 | (char __user *)entry->mem_ptr, | ||
2351 | memory_size, &pos); | ||
2352 | filp_close(entry->file_mem, NULL); | ||
2353 | set_fs(fs); | ||
2354 | dev_info(adapter->dev, | ||
2355 | "The output %s have been saved to file successfully!\n", | ||
2356 | entry->mem_name); | ||
2357 | } else { | ||
2358 | dev_err(adapter->dev, | ||
2359 | "Failed to create file %s\n", filename); | ||
2360 | } | ||
2361 | vfree(entry->mem_ptr); | ||
2362 | entry->mem_ptr = NULL; | ||
2363 | break; | ||
2364 | } while (true); | ||
2365 | } | ||
2366 | do_gettimeofday(&t); | ||
2367 | dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n", | ||
2368 | (u32)t.tv_sec, (u32)t.tv_usec); | ||
2369 | |||
2370 | done: | ||
2371 | for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { | ||
2372 | struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; | ||
2373 | |||
2374 | if (entry->mem_ptr) { | ||
2375 | vfree(entry->mem_ptr); | ||
2376 | entry->mem_ptr = NULL; | ||
2377 | } | ||
2378 | } | ||
2379 | |||
2380 | return; | ||
2381 | } | ||
2382 | |||
2383 | static void mwifiex_pcie_work(struct work_struct *work) | ||
2384 | { | ||
2385 | if (test_and_clear_bit(MWIFIEX_PCIE_WORK_FW_DUMP, &pcie_work_flags)) | ||
2386 | mwifiex_pcie_fw_dump_work(work); | ||
2387 | } | ||
2388 | |||
2389 | /* This function dumps FW information */ | ||
2390 | static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) | ||
2391 | { | ||
2392 | if (test_bit(MWIFIEX_PCIE_WORK_FW_DUMP, &pcie_work_flags)) | ||
2393 | return; | ||
2394 | |||
2395 | set_bit(MWIFIEX_PCIE_WORK_FW_DUMP, &pcie_work_flags); | ||
2396 | |||
2397 | schedule_work(&adapter->iface_work); | ||
2398 | } | ||
2399 | |||
2175 | /* | 2400 | /* |
2176 | * This function initializes the PCI-E host memory space, WCB rings, etc. | 2401 | * This function initializes the PCI-E host memory space, WCB rings, etc. |
2177 | * | 2402 | * |
@@ -2393,6 +2618,8 @@ static struct mwifiex_if_ops pcie_ops = { | |||
2393 | .cleanup_mpa_buf = NULL, | 2618 | .cleanup_mpa_buf = NULL, |
2394 | .init_fw_port = mwifiex_pcie_init_fw_port, | 2619 | .init_fw_port = mwifiex_pcie_init_fw_port, |
2395 | .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, | 2620 | .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, |
2621 | .fw_dump = mwifiex_pcie_fw_dump, | ||
2622 | .iface_work = mwifiex_pcie_work, | ||
2396 | }; | 2623 | }; |
2397 | 2624 | ||
2398 | /* | 2625 | /* |
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index e8ec561f8a64..3abba32e9448 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h | |||
@@ -100,6 +100,28 @@ | |||
100 | #define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF | 100 | #define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF |
101 | #define MWIFIEX_MAX_DELAY_COUNT 5 | 101 | #define MWIFIEX_MAX_DELAY_COUNT 5 |
102 | 102 | ||
103 | #define DEBUG_DUMP_CTRL_REG 0xCF4 | ||
104 | #define DEBUG_DUMP_START_REG 0xCF8 | ||
105 | #define DEBUG_DUMP_END_REG 0xCFF | ||
106 | #define DEBUG_HOST_READY 0xEE | ||
107 | #define DEBUG_FW_DONE 0xFF | ||
108 | |||
109 | #define MAX_NAME_LEN 8 | ||
110 | #define MAX_FULL_NAME_LEN 32 | ||
111 | |||
112 | struct memory_type_mapping { | ||
113 | u8 mem_name[MAX_NAME_LEN]; | ||
114 | u8 *mem_ptr; | ||
115 | struct file *file_mem; | ||
116 | u8 done_flag; | ||
117 | }; | ||
118 | |||
119 | enum rdwr_status { | ||
120 | RDWR_STATUS_SUCCESS = 0, | ||
121 | RDWR_STATUS_FAILURE = 1, | ||
122 | RDWR_STATUS_DONE = 2 | ||
123 | }; | ||
124 | |||
103 | struct mwifiex_pcie_card_reg { | 125 | struct mwifiex_pcie_card_reg { |
104 | u16 cmd_addr_lo; | 126 | u16 cmd_addr_lo; |
105 | u16 cmd_addr_hi; | 127 | u16 cmd_addr_hi; |
@@ -322,4 +344,9 @@ mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) | |||
322 | 344 | ||
323 | return 0; | 345 | return 0; |
324 | } | 346 | } |
347 | |||
348 | enum mwifiex_pcie_work_flags { | ||
349 | MWIFIEX_PCIE_WORK_FW_DUMP, | ||
350 | }; | ||
351 | |||
325 | #endif /* _MWIFIEX_PCIE_H */ | 352 | #endif /* _MWIFIEX_PCIE_H */ |