wlcore: read FW logs from FW memory on watchdog recovery
authorIgal Chernobelsky <igalc@ti.com>
Mon, 18 Jun 2012 08:05:39 +0000 (11:05 +0300)
committerLuciano Coelho <coelho@ti.com>
Thu, 21 Jun 2012 13:48:19 +0000 (16:48 +0300)
FW uses a few memory blocks as a buffer to accumulate FW logs before
transmitting them to the host over SDIO. When FW WatchDog recovery
occurs, the last FW traces are still pending in the buffer. Driver is
to read these FW traces whether log mode is continuous or on demand.

FW memory blocks allocated for the log buffer are handled as a link list:
the first 4 bytes in each memory block contain FW address to the next block.
The end of list condition depends on FW log mode:
- on demand: the list is cyclic, the next address is equal to the first address
- continuous: the address is  equal to 0x2000000

Log data resides inside FW memory block with offset depending on
logger mode:
- on demand:  4 bytes (address of the next memory block)
- continuous: 4 bytes and Rx Descriptor structure size

Described FW logger API is backward compatible with previous FW versions.

Signed-off-by: Igal Chernobelsky <igalc@ti.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/ti/wlcore/main.c

index a7c5e32e90dbc20ddde927cdc320989950dd0441..78edc58da2102af113e51261a6b336451e9e0430 100644 (file)
@@ -770,14 +770,16 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
        return len;
 }
 
+#define WLCORE_FW_LOG_END 0x2000000
+
 static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 {
        u32 addr;
-       u32 first_addr;
+       u32 offset;
+       u32 end_of_log;
        u8 *block;
 
        if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
-           (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
            (wl->conf.fwlog.mem_blocks == 0))
                return;
 
@@ -791,19 +793,26 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
         * Make sure the chip is awake and the logger isn't active.
         * Do not send a stop fwlog command if the fw is hanged.
         */
-       if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery)
-               wl12xx_cmd_stop_fwlog(wl);
-       else
+       if (wl1271_ps_elp_wakeup(wl))
                goto out;
+       if (!wl->watchdog_recovery)
+               wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
        wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
-       first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
-       if (!first_addr)
+       addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
+       if (!addr)
                goto out;
 
+       if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
+               offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
+               end_of_log = WLCORE_FW_LOG_END;
+       } else {
+               offset = sizeof(addr);
+               end_of_log = addr;
+       }
+
        /* Traverse the memory blocks linked list */
-       addr = first_addr;
        do {
                memset(block, 0, WL12XX_HW_BLOCK_SIZE);
                wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
@@ -812,13 +821,14 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                /*
                 * Memory blocks are linked to one another. The first 4 bytes
                 * of each memory block hold the hardware address of the next
-                * one. The last memory block points to the first one.
+                * one. The last memory block points to the first one in
+                * on demand mode and is equal to 0x2000000 in continuous mode.
                 */
                addr = le32_to_cpup((__le32 *)block);
-               if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
-                                      WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
+               if (!wl12xx_copy_fwlog(wl, block + offset,
+                                      WL12XX_HW_BLOCK_SIZE - offset))
                        break;
-       } while (addr && (addr != first_addr));
+       } while (addr && (addr != end_of_log));
 
        wake_up_interruptible(&wl->fwlog_waitq);