summaryrefslogtreecommitdiff
path: root/drivers/staging/csr/csr_wifi_hip_dump.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-19 23:15:42 (GMT)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-19 23:37:01 (GMT)
commit635d2b00e5070378e7bf812acf47fb135c6ab928 (patch)
tree7048a0a511f3d221aa2dfe40aa3a401991f1b175 /drivers/staging/csr/csr_wifi_hip_dump.c
parent15a4bc17b7f4e85cb019e683f14e834078ec2208 (diff)
downloadlinux-fsl-qoriq-635d2b00e5070378e7bf812acf47fb135c6ab928.tar.xz
Staging: add CSR wifi module
This consists of two modules, the driver, and a "helper" module that is just a wrapper around common kernel functions. The wrapper module will be removed soon, but for now it's needed. These files were based on the csr-linux-wifi-5.0.3-oss.tar.gz package provided by CSR and Blue Giga, and is covered under the license specified in the LICENSE.txt file (basically dual BSD and GPLv2). The files were flattened out of the deep directory mess they were originally in, and a few EXPORT_SYMBOL_GPL() were added in order for everything to link properly with the helper module setup. Cc: Mikko Virkkilä <mikko.virkkila@bluegiga.com> Cc: Lauri Hintsala <Lauri.Hintsala@bluegiga.com> Cc: Riku Mettälä <riku.mettala@bluegiga.com> Cc: Veli-Pekka Peltola <veli-pekka.peltola@bluegiga.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/csr/csr_wifi_hip_dump.c')
-rw-r--r--drivers/staging/csr/csr_wifi_hip_dump.c872
1 files changed, 872 insertions, 0 deletions
diff --git a/drivers/staging/csr/csr_wifi_hip_dump.c b/drivers/staging/csr/csr_wifi_hip_dump.c
new file mode 100644
index 0000000..c191ea1
--- /dev/null
+++ b/drivers/staging/csr/csr_wifi_hip_dump.c
@@ -0,0 +1,872 @@
+/*****************************************************************************
+
+ (c) Cambridge Silicon Radio Limited 2011
+ All rights reserved and confidential information of CSR
+
+ Refer to LICENSE.txt included with this source for details
+ on the license terms.
+
+*****************************************************************************/
+
+/*
+ * ---------------------------------------------------------------------------
+ * FILE: csr_wifi_hip_dump.c
+ *
+ * PURPOSE:
+ * Routines for retrieving and buffering core status from the UniFi
+ *
+ * ---------------------------------------------------------------------------
+ */
+#include "csr_wifi_hip_unifi.h"
+#include "csr_wifi_hip_unifiversion.h"
+#include "csr_wifi_hip_card.h"
+
+/* Locations to capture in dump (XAP words) */
+#define HIP_CDUMP_FIRST_CPUREG (0xFFE0) /* First CPU register */
+#define HIP_CDUMP_FIRST_LO (0) /* Start of low address range */
+#define HIP_CDUMP_FIRST_HI_MAC (0x3C00) /* Start of MAC high area */
+#define HIP_CDUMP_FIRST_HI_PHY (0x1C00) /* Start of PHY high area */
+#define HIP_CDUMP_FIRST_SH (0) /* Start of shared memory area */
+
+#define HIP_CDUMP_NCPUREGS (10) /* No. of 16-bit XAP registers */
+#define HIP_CDUMP_NWORDS_LO (0x0100) /* Low area size in 16-bit words */
+#define HIP_CDUMP_NWORDS_HI (0x0400) /* High area size in 16-bit words */
+#define HIP_CDUMP_NWORDS_SH (0x0500) /* Shared memory area size, 16-bit words */
+
+#define HIP_CDUMP_NUM_ZONES 7 /* Number of UniFi memory areas to capture */
+
+/* Mini-coredump state */
+typedef struct coredump_buf
+{
+ CsrUint16 count; /* serial number of dump */
+ CsrTime timestamp; /* host's system time at capture */
+ CsrInt16 requestor; /* request: 0=auto dump, 1=manual */
+ CsrUint16 chip_ver;
+ CsrUint32 fw_ver;
+ CsrUint16 *zone[HIP_CDUMP_NUM_ZONES];
+
+ struct coredump_buf *next; /* circular list */
+ struct coredump_buf *prev; /* circular list */
+} coredump_buffer;
+
+/* Structure used to describe a zone of chip memory captured by mini-coredump */
+struct coredump_zone
+{
+ unifi_coredump_space_t space; /* XAP memory space this zone covers */
+ enum unifi_dbg_processors_select cpu; /* XAP CPU core selector */
+ CsrUint32 gp; /* Generic Pointer to memory zone on XAP */
+ CsrUint16 offset; /* 16-bit XAP word offset of zone in memory space */
+ CsrUint16 length; /* Length of zone in XAP words */
+};
+
+static CsrResult unifi_coredump_from_sdio(card_t *card, coredump_buffer *dump_buf);
+static CsrResult unifi_coredump_read_zones(card_t *card, coredump_buffer *dump_buf);
+static CsrResult unifi_coredump_read_zone(card_t *card, CsrUint16 *zone,
+ const struct coredump_zone *def);
+static CsrInt32 get_value_from_coredump(const coredump_buffer *dump,
+ const unifi_coredump_space_t space, const CsrUint16 offset);
+
+/* Table of chip memory zones we capture on mini-coredump */
+static const struct coredump_zone zonedef_table[HIP_CDUMP_NUM_ZONES] = {
+ { UNIFI_COREDUMP_MAC_REG, UNIFI_PROC_MAC, UNIFI_MAKE_GP(REGISTERS, HIP_CDUMP_FIRST_CPUREG * 2), HIP_CDUMP_FIRST_CPUREG, HIP_CDUMP_NCPUREGS },
+ { UNIFI_COREDUMP_PHY_REG, UNIFI_PROC_PHY, UNIFI_MAKE_GP(REGISTERS, HIP_CDUMP_FIRST_CPUREG * 2), HIP_CDUMP_FIRST_CPUREG, HIP_CDUMP_NCPUREGS },
+ { UNIFI_COREDUMP_SH_DMEM, UNIFI_PROC_INVALID, UNIFI_MAKE_GP(SH_DMEM, HIP_CDUMP_FIRST_SH * 2), HIP_CDUMP_FIRST_SH, HIP_CDUMP_NWORDS_SH },
+ { UNIFI_COREDUMP_MAC_DMEM, UNIFI_PROC_MAC, UNIFI_MAKE_GP(MAC_DMEM, HIP_CDUMP_FIRST_LO * 2), HIP_CDUMP_FIRST_LO, HIP_CDUMP_NWORDS_LO },
+ { UNIFI_COREDUMP_MAC_DMEM, UNIFI_PROC_MAC, UNIFI_MAKE_GP(MAC_DMEM, HIP_CDUMP_FIRST_HI_MAC * 2), HIP_CDUMP_FIRST_HI_MAC, HIP_CDUMP_NWORDS_HI },
+ { UNIFI_COREDUMP_PHY_DMEM, UNIFI_PROC_PHY, UNIFI_MAKE_GP(PHY_DMEM, HIP_CDUMP_FIRST_LO * 2), HIP_CDUMP_FIRST_LO, HIP_CDUMP_NWORDS_LO },
+ { UNIFI_COREDUMP_PHY_DMEM, UNIFI_PROC_PHY, UNIFI_MAKE_GP(PHY_DMEM, HIP_CDUMP_FIRST_HI_PHY * 2), HIP_CDUMP_FIRST_HI_PHY, HIP_CDUMP_NWORDS_HI },
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_request_at_next_reset
+ *
+ * Request that a mini-coredump is performed when the driver has
+ * completed resetting the UniFi device.
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * enable If non-zero, sets the request.
+ * If zero, cancels any pending request.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS or CSR HIP error code
+ *
+ * Notes:
+ * This function is typically called once the driver has detected that
+ * the UniFi device has become unresponsive due to crash, or internal
+ * watchdog reset. The driver must reset it to regain communication and,
+ * immediately after that, the mini-coredump can be captured.
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_coredump_request_at_next_reset(card_t *card, CsrInt8 enable)
+{
+ CsrResult r;
+
+ func_enter();
+
+ if (enable)
+ {
+ unifi_trace(card->ospriv, UDBG2, "Mini-coredump requested after reset\n");
+ }
+
+ if (card == NULL)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ }
+ else
+ {
+ card->request_coredump_on_reset = enable?1 : 0;
+ r = CSR_RESULT_SUCCESS;
+ }
+
+ func_exit_r(r);
+ return r;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_handle_request
+ *
+ * Performs a coredump now, if one was requested, and clears the request.
+ *
+ * Arguments:
+ * card Pointer to card struct
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS or CSR HIP error code
+ *
+ * Notes:
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_coredump_handle_request(card_t *card)
+{
+ CsrResult r = CSR_RESULT_SUCCESS;
+
+ func_enter();
+
+ if (card == NULL)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ }
+ else
+ {
+ if (card->request_coredump_on_reset == 1)
+ {
+ card->request_coredump_on_reset = 0;
+ r = unifi_coredump_capture(card, NULL);
+ }
+ }
+
+ func_exit_r(r);
+ return r;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_capture
+ *
+ * Capture the current status of the UniFi device.
+ * Various registers are buffered for future offline inspection.
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * req Pointer to request struct, or NULL:
+ * A coredump requested manually by the user app
+ * will have a request struct pointer, an automatic
+ * coredump will have a NULL pointer.
+ * Returns:
+ * CSR_RESULT_SUCCESS on success,
+ * CSR_RESULT_FAILURE SDIO error
+ * CSR_WIFI_HIP_RESULT_INVALID_VALUE Initialisation not complete
+ *
+ * Notes:
+ * The result is a filled entry in the circular buffer of core dumps,
+ * values from which can be extracted to userland via an ioctl.
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_coredump_capture(card_t *card, struct unifi_coredump_req *req)
+{
+ CsrResult r = CSR_RESULT_SUCCESS;
+ static CsrUint16 dump_seq_no = 1;
+ CsrTime time_of_capture;
+
+ func_enter();
+
+ if (card->dump_next_write == NULL)
+ {
+ r = CSR_RESULT_SUCCESS;
+ goto done;
+ }
+
+ /* Reject forced capture before initialisation has happened */
+ if (card->helper == NULL)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ goto done;
+ }
+
+
+ /*
+ * Force a mini-coredump capture right now
+ */
+ time_of_capture = CsrTimeGet(NULL);
+ unifi_info(card->ospriv, "Mini-coredump capture at t=%u\n", time_of_capture);
+
+ /* Wake up the processors so we can talk to them */
+ r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to wake UniFi\n");
+ goto done;
+ }
+ CsrThreadSleep(20);
+
+ /* Stop both XAPs */
+ unifi_trace(card->ospriv, UDBG4, "Stopping XAPs for coredump capture\n");
+ r = unifi_card_stop_processor(card, UNIFI_PROC_BOTH);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to stop UniFi XAPs\n");
+ goto done;
+ }
+
+ /* Dump core into the next available slot in the circular list */
+ r = unifi_coredump_from_sdio(card, card->dump_next_write);
+ if (r == CSR_RESULT_SUCCESS)
+ {
+ /* Record whether the dump was manual or automatic */
+ card->dump_next_write->requestor = (req?1 : 0);
+ card->dump_next_write->timestamp = time_of_capture;
+ /* Advance to the next buffer */
+ card->dump_next_write->count = dump_seq_no++;
+ card->dump_cur_read = card->dump_next_write;
+ card->dump_next_write = card->dump_next_write->next;
+
+ /* Sequence no. of zero indicates slot not in use, so handle wrap */
+ if (dump_seq_no == 0)
+ {
+ dump_seq_no = 1;
+ }
+
+ unifi_trace(card->ospriv, UDBG3,
+ "Coredump (%p), SeqNo=%d, cur_read=%p, next_write=%p\n",
+ req,
+ card->dump_cur_read->count,
+ card->dump_cur_read, card->dump_next_write);
+ }
+
+ /* Start both XAPs */
+ unifi_trace(card->ospriv, UDBG4, "Restart XAPs after coredump\n");
+ r = card_start_processor(card, UNIFI_PROC_BOTH);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Failed to start UniFi XAPs\n");
+ goto done;
+ }
+
+done:
+ func_exit_r(r);
+ return r;
+} /* unifi_coredump_capture() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * get_value_from_coredump
+ *
+ *
+ *
+ * Arguments:
+ * dump Pointer to buffered coredump data
+ * offset_in_space XAP memory space to retrieve from the buffer (there
+ * may be more than one zone covering the same memory
+ * space, but starting from different offsets).
+ * offset Offset within the XAP memory space to be retrieved
+ *
+ * Returns:
+ * >=0 Register value on success
+ * <0 Register out of range of any captured zones
+ *
+ * Notes:
+ * ---------------------------------------------------------------------------
+ */
+static CsrInt32 get_value_from_coredump(const coredump_buffer *dump,
+ const unifi_coredump_space_t space,
+ const CsrUint16 offset_in_space)
+{
+ CsrInt32 r = -1;
+ CsrUint16 offset_in_zone;
+ CsrUint32 zone_end_offset;
+ CsrInt32 i;
+ const struct coredump_zone *def = &zonedef_table[0];
+
+ /* Search zone def table for a match with the requested memory space */
+ for (i = 0; i < HIP_CDUMP_NUM_ZONES; i++, def++)
+ {
+ if (space == def->space)
+ {
+ zone_end_offset = def->offset + def->length;
+
+ /* Is the space offset contained in this zone? */
+ if (offset_in_space < zone_end_offset &&
+ offset_in_space >= def->offset)
+ {
+ /* Calculate the offset of data within the zone buffer */
+ offset_in_zone = offset_in_space - def->offset;
+ r = (CsrInt32) * (dump->zone[i] + offset_in_zone);
+
+ unifi_trace(NULL, UDBG6,
+ "sp %d, offs 0x%04x = 0x%04x (in z%d 0x%04x->0x%04x)\n",
+ space, offset_in_space, r,
+ i, def->offset, zone_end_offset - 1);
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_get_value
+ *
+ * Retrieve the value of a register buffered from a previous core dump,
+ * so that it may be reported back to application code.
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * req_reg Pointer to request parameter partially filled. This
+ * function puts in the values retrieved from the dump.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, or:
+ * CSR_WIFI_HIP_RESULT_INVALID_VALUE Null parameter error
+ * CSR_WIFI_HIP_RESULT_RANGE Register out of range
+ * CSR_WIFI_HIP_RESULT_NOT_FOUND Dump index not (yet) captured
+ *
+ * Notes:
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_coredump_get_value(card_t *card, struct unifi_coredump_req *req)
+{
+ CsrResult r;
+ CsrInt32 i = 0;
+ coredump_buffer *find_dump = NULL;
+
+ func_enter();
+
+ if (req == NULL || card == NULL)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ goto done;
+ }
+ req->value = -1;
+ if (card->dump_buf == NULL)
+ {
+ unifi_trace(card->ospriv, UDBG2, "No coredump buffers\n");
+ r = CSR_WIFI_HIP_RESULT_NOT_FOUND; /* Coredumping disabled */
+ goto done;
+ }
+ if (card->dump_cur_read == NULL)
+ {
+ unifi_trace(card->ospriv, UDBG4, "No coredumps captured\n");
+ r = CSR_WIFI_HIP_RESULT_NOT_FOUND; /* No coredump yet captured */
+ goto done;
+ }
+
+ /* Find the requested dump buffer */
+ switch (req->index)
+ {
+ case 0: /* Newest */
+ find_dump = card->dump_cur_read;
+ break;
+ case -1: /* Oldest: The next used slot forward */
+ for (find_dump = card->dump_cur_read->next;
+ (find_dump->count == 0) && (find_dump != card->dump_cur_read);
+ find_dump = card->dump_cur_read->next)
+ {
+ }
+ break;
+ default: /* Number of steps back from current read position */
+ for (i = 0, find_dump = card->dump_cur_read;
+ i < req->index;
+ i++, find_dump = find_dump->prev)
+ {
+ /* Walk the list for the index'th entry, but
+ * stop when about to wrap. */
+ unifi_trace(card->ospriv, UDBG6,
+ "%d: %d, @%p, p=%p, n=%p, cr=%p, h=%p\n",
+ i, find_dump->count, find_dump, find_dump->prev,
+ find_dump->next, card->dump_cur_read, card->dump_buf);
+ if (find_dump->prev == card->dump_cur_read)
+ {
+ /* Wrapped but still not found, index out of range */
+ if (i != req->index)
+ {
+ unifi_trace(card->ospriv, UDBG6,
+ "Dump index %d not found %d\n", req->index, i);
+ r = CSR_WIFI_HIP_RESULT_NOT_FOUND;
+ goto done;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Check if the slot is actually filled with a core dump */
+ if (find_dump->count == 0)
+ {
+ unifi_trace(card->ospriv, UDBG4, "Not captured %d\n", req->index);
+ r = CSR_WIFI_HIP_RESULT_NOT_FOUND;
+ goto done;
+ }
+
+ unifi_trace(card->ospriv, UDBG6, "Req index %d, found seq %d at step %d\n",
+ req->index, find_dump->count, i);
+
+ /* Find the appropriate entry in the buffer */
+ req->value = get_value_from_coredump(find_dump, req->space, (CsrUint16)req->offset);
+ if (req->value < 0)
+ {
+ r = CSR_WIFI_HIP_RESULT_RANGE; /* Un-captured register */
+ unifi_trace(card->ospriv, UDBG4,
+ "Can't read space %d, reg 0x%x from coredump buffer %d\n",
+ req->space, req->offset, req->index);
+ }
+ else
+ {
+ r = CSR_RESULT_SUCCESS;
+ }
+
+ /* Update the private request structure with the found values */
+ req->chip_ver = find_dump->chip_ver;
+ req->fw_ver = find_dump->fw_ver;
+ req->timestamp = find_dump->timestamp;
+ req->requestor = find_dump->requestor;
+ req->serial = find_dump->count;
+
+done:
+ func_exit_r(r);
+ return r;
+} /* unifi_coredump_get_value() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_read_zone
+ *
+ * Captures a UniFi memory zone into a buffer on the host
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * zonebuf Pointer to on-host buffer to dump the memory zone into
+ * def Pointer to description of the memory zone to read from UniFi.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, or:
+ * CSR_RESULT_FAILURE SDIO error
+ * CSR_WIFI_HIP_RESULT_INVALID_VALUE Parameter error
+ *
+ * Notes:
+ * It is assumed that the caller has already stopped the XAPs
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult unifi_coredump_read_zone(card_t *card, CsrUint16 *zonebuf, const struct coredump_zone *def)
+{
+ CsrResult r;
+
+ func_enter();
+
+ if (zonebuf == NULL || def == NULL)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ goto done;
+ }
+
+ /* Select XAP CPU if necessary */
+ if (def->cpu != UNIFI_PROC_INVALID)
+ {
+ if (def->cpu != UNIFI_PROC_MAC && def->cpu != UNIFI_PROC_PHY)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ goto done;
+ }
+ r = unifi_set_proc_select(card, def->cpu);
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ goto done;
+ }
+ }
+
+ unifi_trace(card->ospriv, UDBG4,
+ "Dump sp %d, offs 0x%04x, 0x%04x words @GP=%08x CPU %d\n",
+ def->space, def->offset, def->length, def->gp, def->cpu);
+
+ /* Read on-chip RAM (byte-wise) */
+ r = unifi_card_readn(card, def->gp, zonebuf, (CsrUint16)(def->length * 2));
+ if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
+ {
+ goto done;
+ }
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Can't read UniFi shared data area\n");
+ goto done;
+ }
+
+done:
+ func_exit_r(r);
+ return r;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_read_zones
+ *
+ * Walks through the table of on-chip memory zones defined in zonedef_table,
+ * and reads each of them from the UniFi chip
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * dump_buf Buffer into which register values will be dumped
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, or:
+ * CSR_RESULT_FAILURE SDIO error
+ * CSR_WIFI_HIP_RESULT_INVALID_VALUE Parameter error
+ *
+ * Notes:
+ * It is assumed that the caller has already stopped the XAPs
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult unifi_coredump_read_zones(card_t *card, coredump_buffer *dump_buf)
+{
+ CsrResult r = CSR_RESULT_SUCCESS;
+ CsrInt32 i;
+
+ func_enter();
+
+ /* Walk the table of coredump zone definitions and read them from the chip */
+ for (i = 0;
+ (i < HIP_CDUMP_NUM_ZONES) && (r == 0);
+ i++)
+ {
+ r = unifi_coredump_read_zone(card, dump_buf->zone[i], &zonedef_table[i]);
+ }
+
+ func_exit_r(r);
+ return r;
+}
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_from_sdio
+ *
+ * Capture the status of the UniFi processors, over SDIO
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * reg_buffer Buffer into which register values will be dumped
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, or:
+ * CSR_RESULT_FAILURE SDIO error
+ * CSR_WIFI_HIP_RESULT_INVALID_VALUE Parameter error
+ *
+ * Notes:
+ * ---------------------------------------------------------------------------
+ */
+static CsrResult unifi_coredump_from_sdio(card_t *card, coredump_buffer *dump_buf)
+{
+ CsrUint16 val;
+ CsrResult r;
+ CsrUint32 sdio_addr;
+
+ func_enter();
+
+ if (dump_buf == NULL)
+ {
+ r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
+ goto done;
+ }
+
+
+ /* Chip and firmware version */
+ unifi_trace(card->ospriv, UDBG4, "Get chip version\n");
+ sdio_addr = 2 * ChipHelper_GBL_CHIP_VERSION(card->helper);
+ if (sdio_addr != 0)
+ {
+ r = unifi_read_direct16(card, sdio_addr, &val);
+ if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
+ {
+ goto done;
+ }
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Can't read GBL_CHIP_VERSION\n");
+ goto done;
+ }
+ }
+ dump_buf->chip_ver = val;
+ dump_buf->fw_ver = card->build_id;
+
+ unifi_trace(card->ospriv, UDBG4, "chip_ver 0x%04x, fw_ver %u\n",
+ dump_buf->chip_ver, dump_buf->fw_ver);
+
+ /* Capture the memory zones required from UniFi */
+ r = unifi_coredump_read_zones(card, dump_buf);
+ if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
+ {
+ goto done;
+ }
+ if (r != CSR_RESULT_SUCCESS)
+ {
+ unifi_error(card->ospriv, "Can't read UniFi memory areas\n");
+ goto done;
+ }
+
+done:
+ func_exit_r(r);
+ return r;
+} /* unifi_coredump_from_sdio() */
+
+
+#ifndef UNIFI_DISABLE_COREDUMP
+/*
+ * ---------------------------------------------------------------------------
+ * new_coredump_node
+ *
+ * Allocates a coredump linked-list node, and links it to the previous.
+ *
+ * Arguments:
+ * ospriv OS context
+ * prevnode Previous node to link into
+ *
+ * Returns:
+ * Pointer to valid coredump_buffer on success
+ * NULL on memory allocation failure
+ *
+ * Notes:
+ * Allocates "all or nothing"
+ * ---------------------------------------------------------------------------
+ */
+static
+coredump_buffer* new_coredump_node(void *ospriv, coredump_buffer *prevnode)
+{
+ coredump_buffer *newnode = NULL;
+ CsrUint16 *newzone = NULL;
+ CsrInt32 i;
+ CsrUint32 zone_size;
+
+ /* Allocate node header */
+ newnode = (coredump_buffer *)CsrMemAlloc(sizeof(coredump_buffer));
+ if (newnode == NULL)
+ {
+ return NULL;
+ }
+ CsrMemSet(newnode, 0, sizeof(coredump_buffer));
+
+ /* Allocate chip memory zone capture buffers */
+ for (i = 0; i < HIP_CDUMP_NUM_ZONES; i++)
+ {
+ zone_size = sizeof(CsrUint16) * zonedef_table[i].length;
+ newzone = (CsrUint16 *)CsrMemAlloc(zone_size);
+ newnode->zone[i] = newzone;
+ if (newzone != NULL)
+ {
+ CsrMemSet(newzone, 0, zone_size);
+ }
+ else
+ {
+ unifi_error(ospriv, "Out of memory on coredump zone %d (%d words)\n",
+ i, zonedef_table[i].length);
+ break;
+ }
+ }
+
+ /* Clean up if any zone alloc failed */
+ if (newzone == NULL)
+ {
+ for (i = 0; newnode->zone[i] != NULL; i++)
+ {
+ CsrMemFree(newnode->zone[i]);
+ newnode->zone[i] = NULL;
+ }
+ }
+
+ /* Link to previous node */
+ newnode->prev = prevnode;
+ if (prevnode)
+ {
+ prevnode->next = newnode;
+ }
+ newnode->next = NULL;
+
+ return newnode;
+}
+
+
+#endif /* UNIFI_DISABLE_COREDUMP */
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_init
+ *
+ * Allocates buffers for the automatic SDIO core dump
+ *
+ * Arguments:
+ * card Pointer to card struct
+ * num_dump_buffers Number of buffers to reserve for coredumps
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, or:
+ * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed
+ *
+ * Notes:
+ * Allocates space in advance, to be used for the last n coredump buffers
+ * the intention being that the size is sufficient for at least one dump,
+ * probably several.
+ * It's probably advisable to have at least 2 coredump buffers to allow
+ * one to be enquired with the unifi_coredump tool, while leaving another
+ * free for capturing.
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_coredump_init(card_t *card, CsrUint16 num_dump_buffers)
+{
+#ifndef UNIFI_DISABLE_COREDUMP
+ void *ospriv = card->ospriv;
+ coredump_buffer *prev = NULL;
+ coredump_buffer *newnode = NULL;
+ CsrUint32 i = 0;
+#endif
+
+ func_enter();
+
+ card->request_coredump_on_reset = 0;
+ card->dump_next_write = NULL;
+ card->dump_cur_read = NULL;
+ card->dump_buf = NULL;
+
+#ifndef UNIFI_DISABLE_COREDUMP
+ unifi_trace(ospriv, UDBG1,
+ "Allocate buffers for %d core dumps\n", num_dump_buffers);
+ if (num_dump_buffers == 0)
+ {
+ goto done;
+ }
+
+ /* Root node */
+ card->dump_buf = new_coredump_node(ospriv, NULL);
+ if (card->dump_buf == NULL)
+ {
+ goto fail;
+ }
+ prev = card->dump_buf;
+ newnode = card->dump_buf;
+
+ /* Add each subsequent node at tail */
+ for (i = 1; i < num_dump_buffers; i++)
+ {
+ newnode = new_coredump_node(ospriv, prev);
+ if (newnode == NULL)
+ {
+ goto fail;
+ }
+ prev = newnode;
+ }
+
+ /* Link the first and last nodes to make the list circular */
+ card->dump_buf->prev = newnode;
+ newnode->next = card->dump_buf;
+
+ /* Set initial r/w access pointers */
+ card->dump_next_write = card->dump_buf;
+ card->dump_cur_read = NULL;
+
+ unifi_trace(ospriv, UDBG2, "Core dump configured (%d dumps max)\n", i);
+
+done:
+#endif
+ func_exit();
+ return CSR_RESULT_SUCCESS;
+
+#ifndef UNIFI_DISABLE_COREDUMP
+fail:
+ /* Unwind what we allocated so far */
+ unifi_error(ospriv, "Out of memory allocating core dump node %d\n", i);
+ unifi_coredump_free(card);
+ func_exit();
+ return CSR_WIFI_HIP_RESULT_NO_MEMORY;
+#endif
+} /* unifi_coreump_init() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * unifi_coredump_free
+ *
+ * Free all memory dynamically allocated for core dump
+ *
+ * Arguments:
+ * card Pointer to card struct
+ *
+ * Returns:
+ * None
+ *
+ * Notes:
+ * ---------------------------------------------------------------------------
+ */
+void unifi_coredump_free(card_t *card)
+{
+ void *ospriv = card->ospriv;
+ coredump_buffer *node, *del_node;
+ CsrInt16 i = 0;
+ CsrInt16 j;
+
+ func_enter();
+ unifi_trace(ospriv, UDBG2, "Core dump de-configured\n");
+
+ if (card->dump_buf == NULL)
+ {
+ return;
+ }
+
+ node = card->dump_buf;
+ do
+ {
+ /* Free payload zones */
+ for (j = 0; j < HIP_CDUMP_NUM_ZONES; j++)
+ {
+ if (node->zone[j] != NULL)
+ {
+ CsrMemFree(node->zone[j]);
+ node->zone[j] = NULL;
+ }
+ }
+
+ /* Detach */
+ del_node = node;
+ node = node->next;
+
+ /* Free header */
+ CsrMemFree(del_node);
+ i++;
+ } while ((node != NULL) && (node != card->dump_buf));
+
+ unifi_trace(ospriv, UDBG3, "Freed %d coredump buffers\n", i);
+
+ card->dump_buf = NULL;
+ card->dump_next_write = NULL;
+ card->dump_cur_read = NULL;
+
+ func_exit();
+} /* unifi_coredump_free() */
+
+