Commit 1e41213f authored by Igal Chernobelsky's avatar Igal Chernobelsky Committed by Luciano Coelho

wlcore: read FW logs from FW memory on watchdog recovery

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: default avatarIgal Chernobelsky <igalc@ti.com>
Signed-off-by: default avatarLuciano Coelho <coelho@ti.com>
parent add779a0
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment