tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / staging / csr / csr_wifi_hip_dump.c
1 /*****************************************************************************
2
3             (c) Cambridge Silicon Radio Limited 2012
4             All rights reserved and confidential information of CSR
5
6             Refer to LICENSE.txt included with this source for details
7             on the license terms.
8
9 *****************************************************************************/
10
11 /*
12  * ---------------------------------------------------------------------------
13  * FILE: csr_wifi_hip_dump.c
14  *
15  * PURPOSE:
16  *      Routines for retrieving and buffering core status from the UniFi
17  *
18  * ---------------------------------------------------------------------------
19  */
20 #include <linux/slab.h>
21 #include "csr_wifi_hip_unifi.h"
22 #include "csr_wifi_hip_unifiversion.h"
23 #include "csr_wifi_hip_card.h"
24
25 /* Locations to capture in dump (XAP words) */
26 #define HIP_CDUMP_FIRST_CPUREG      (0xFFE0) /* First CPU register */
27 #define HIP_CDUMP_FIRST_LO          (0)      /* Start of low address range */
28 #define HIP_CDUMP_FIRST_HI_MAC      (0x3C00) /* Start of MAC high area */
29 #define HIP_CDUMP_FIRST_HI_PHY      (0x1C00) /* Start of PHY high area */
30 #define HIP_CDUMP_FIRST_SH          (0)      /* Start of shared memory area */
31
32 #define HIP_CDUMP_NCPUREGS    (10)           /* No. of 16-bit XAP registers */
33 #define HIP_CDUMP_NWORDS_LO   (0x0100)       /* Low area size in 16-bit words */
34 #define HIP_CDUMP_NWORDS_HI   (0x0400)       /* High area size in 16-bit words */
35 #define HIP_CDUMP_NWORDS_SH   (0x0500)       /* Shared memory area size, 16-bit words */
36
37 #define HIP_CDUMP_NUM_ZONES 7                /* Number of UniFi memory areas to capture */
38
39 /* Mini-coredump state */
40 typedef struct coredump_buf
41 {
42     u16  count;                       /* serial number of dump */
43     u32  timestamp;                   /* host's system time at capture */
44     s16   requestor;                   /* request: 0=auto dump, 1=manual */
45     u16  chip_ver;
46     u32  fw_ver;
47     u16 *zone[HIP_CDUMP_NUM_ZONES];
48
49     struct coredump_buf *next;              /* circular list */
50     struct coredump_buf *prev;              /* circular list */
51 } coredump_buffer;
52
53 /* Structure used to describe a zone of chip memory captured by mini-coredump */
54 struct coredump_zone
55 {
56     unifi_coredump_space_t           space;  /* XAP memory space this zone covers */
57     enum unifi_dbg_processors_select cpu;    /* XAP CPU core selector */
58     u32                        gp;     /* Generic Pointer to memory zone on XAP */
59     u16                        offset; /* 16-bit XAP word offset of zone in memory space */
60     u16                        length; /* Length of zone in XAP words */
61 };
62
63 static CsrResult unifi_coredump_from_sdio(card_t *card, coredump_buffer *dump_buf);
64 static CsrResult unifi_coredump_read_zones(card_t *card, coredump_buffer *dump_buf);
65 static CsrResult unifi_coredump_read_zone(card_t *card, u16 *zone,
66                                           const struct coredump_zone *def);
67 static s32 get_value_from_coredump(const coredump_buffer *dump,
68                                         const unifi_coredump_space_t space, const u16 offset);
69
70 /* Table of chip memory zones we capture on mini-coredump */
71 static const struct coredump_zone zonedef_table[HIP_CDUMP_NUM_ZONES] = {
72     { UNIFI_COREDUMP_MAC_REG,  UNIFI_PROC_MAC, UNIFI_MAKE_GP(REGISTERS, HIP_CDUMP_FIRST_CPUREG * 2), HIP_CDUMP_FIRST_CPUREG, HIP_CDUMP_NCPUREGS },
73     { UNIFI_COREDUMP_PHY_REG,  UNIFI_PROC_PHY, UNIFI_MAKE_GP(REGISTERS, HIP_CDUMP_FIRST_CPUREG * 2), HIP_CDUMP_FIRST_CPUREG, HIP_CDUMP_NCPUREGS },
74     { 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 },
75     { 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 },
76     { 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 },
77     { 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 },
78     { 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 },
79 };
80
81 /*
82  * ---------------------------------------------------------------------------
83  *  unifi_coredump_request_at_next_reset
84  *
85  *      Request that a mini-coredump is performed when the driver has
86  *      completed resetting the UniFi device.
87  *
88  *  Arguments:
89  *      card            Pointer to card struct
90  *      enable          If non-zero, sets the request.
91  *                      If zero, cancels any pending request.
92  *
93  *  Returns:
94  *      CSR_RESULT_SUCCESS or CSR HIP error code
95  *
96  *  Notes:
97  *      This function is typically called once the driver has detected that
98  *      the UniFi device has become unresponsive due to crash, or internal
99  *      watchdog reset. The driver must reset it to regain communication and,
100  *      immediately after that, the mini-coredump can be captured.
101  * ---------------------------------------------------------------------------
102  */
103 CsrResult unifi_coredump_request_at_next_reset(card_t *card, s8 enable)
104 {
105     CsrResult r;
106
107     if (enable)
108     {
109         unifi_trace(card->ospriv, UDBG2, "Mini-coredump requested after reset\n");
110     }
111
112     if (card == NULL)
113     {
114         r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
115     }
116     else
117     {
118         card->request_coredump_on_reset = enable?1 : 0;
119         r = CSR_RESULT_SUCCESS;
120     }
121
122     return r;
123 }
124
125
126 /*
127  * ---------------------------------------------------------------------------
128  *  unifi_coredump_handle_request
129  *
130  *      Performs a coredump now, if one was requested, and clears the request.
131  *
132  *  Arguments:
133  *      card            Pointer to card struct
134  *
135  *  Returns:
136  *      CSR_RESULT_SUCCESS or CSR HIP error code
137  *
138  *  Notes:
139  * ---------------------------------------------------------------------------
140  */
141 CsrResult unifi_coredump_handle_request(card_t *card)
142 {
143     CsrResult r = CSR_RESULT_SUCCESS;
144
145     if (card == NULL)
146     {
147         r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
148     }
149     else
150     {
151         if (card->request_coredump_on_reset == 1)
152         {
153             card->request_coredump_on_reset = 0;
154             r = unifi_coredump_capture(card, NULL);
155         }
156     }
157
158     return r;
159 }
160
161
162 /*
163  * ---------------------------------------------------------------------------
164  *  unifi_coredump_capture
165  *
166  *      Capture the current status of the UniFi device.
167  *      Various registers are buffered for future offline inspection.
168  *
169  *  Arguments:
170  *      card            Pointer to card struct
171  *      req             Pointer to request struct, or NULL:
172  *                          A coredump requested manually by the user app
173  *                          will have a request struct pointer, an automatic
174  *                          coredump will have a NULL pointer.
175  *  Returns:
176  *      CSR_RESULT_SUCCESS  on success,
177  *      CSR_RESULT_FAILURE  SDIO error
178  *      CSR_WIFI_HIP_RESULT_INVALID_VALUE  Initialisation not complete
179  *
180  *  Notes:
181  *      The result is a filled entry in the circular buffer of core dumps,
182  *      values from which can be extracted to userland via an ioctl.
183  * ---------------------------------------------------------------------------
184  */
185 CsrResult unifi_coredump_capture(card_t *card, struct unifi_coredump_req *req)
186 {
187     CsrResult r = CSR_RESULT_SUCCESS;
188     static u16 dump_seq_no = 1;
189     u32 time_of_capture;
190
191     if (card->dump_next_write == NULL)
192     {
193         r = CSR_RESULT_SUCCESS;
194         goto done;
195     }
196
197     /* Reject forced capture before initialisation has happened */
198     if (card->helper == NULL)
199     {
200         r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
201         goto done;
202     }
203
204
205     /*
206      * Force a mini-coredump capture right now
207      */
208     time_of_capture = CsrTimeGet(NULL);
209     unifi_info(card->ospriv, "Mini-coredump capture at t=%u\n", time_of_capture);
210
211     /* Wake up the processors so we can talk to them */
212     r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
213     if (r != CSR_RESULT_SUCCESS)
214     {
215         unifi_error(card->ospriv, "Failed to wake UniFi\n");
216         goto done;
217     }
218     CsrThreadSleep(20);
219
220     /* Stop both XAPs */
221     unifi_trace(card->ospriv, UDBG4, "Stopping XAPs for coredump capture\n");
222     r = unifi_card_stop_processor(card, UNIFI_PROC_BOTH);
223     if (r != CSR_RESULT_SUCCESS)
224     {
225         unifi_error(card->ospriv, "Failed to stop UniFi XAPs\n");
226         goto done;
227     }
228
229     /* Dump core into the next available slot in the circular list */
230     r = unifi_coredump_from_sdio(card, card->dump_next_write);
231     if (r == CSR_RESULT_SUCCESS)
232     {
233         /* Record whether the dump was manual or automatic */
234         card->dump_next_write->requestor = (req?1 : 0);
235         card->dump_next_write->timestamp = time_of_capture;
236         /* Advance to the next buffer */
237         card->dump_next_write->count = dump_seq_no++;
238         card->dump_cur_read = card->dump_next_write;
239         card->dump_next_write = card->dump_next_write->next;
240
241         /* Sequence no. of zero indicates slot not in use, so handle wrap */
242         if (dump_seq_no == 0)
243         {
244             dump_seq_no = 1;
245         }
246
247         unifi_trace(card->ospriv, UDBG3,
248                     "Coredump (%p), SeqNo=%d, cur_read=%p, next_write=%p\n",
249                     req,
250                     card->dump_cur_read->count,
251                     card->dump_cur_read, card->dump_next_write);
252     }
253
254     /* Start both XAPs */
255     unifi_trace(card->ospriv, UDBG4, "Restart XAPs after coredump\n");
256     r = card_start_processor(card, UNIFI_PROC_BOTH);
257     if (r != CSR_RESULT_SUCCESS)
258     {
259         unifi_error(card->ospriv, "Failed to start UniFi XAPs\n");
260         goto done;
261     }
262
263 done:
264     return r;
265 } /* unifi_coredump_capture() */
266
267
268 /*
269  * ---------------------------------------------------------------------------
270  *  get_value_from_coredump
271  *
272  *
273  *
274  *  Arguments:
275  *      dump                Pointer to buffered coredump data
276  *      offset_in_space     XAP memory space to retrieve from the buffer (there
277  *                          may be more than one zone covering the same memory
278  *                          space, but starting from different offsets).
279  *      offset              Offset within the XAP memory space to be retrieved
280  *
281  *  Returns:
282  *      >=0                  Register value on success
283  *      <0                   Register out of range of any captured zones
284  *
285  *  Notes:
286  * ---------------------------------------------------------------------------
287  */
288 static s32 get_value_from_coredump(const coredump_buffer       *coreDump,
289                                         const unifi_coredump_space_t space,
290                                         const u16              offset_in_space)
291 {
292     s32 r = -1;
293     u16 offset_in_zone;
294     u32 zone_end_offset;
295     s32 i;
296     const struct coredump_zone *def = &zonedef_table[0];
297
298     /* Search zone def table for a match with the requested memory space */
299     for (i = 0; i < HIP_CDUMP_NUM_ZONES; i++, def++)
300     {
301         if (space == def->space)
302         {
303             zone_end_offset = def->offset + def->length;
304
305             /* Is the space offset contained in this zone? */
306             if (offset_in_space < zone_end_offset &&
307                 offset_in_space >= def->offset)
308             {
309                 /* Calculate the offset of data within the zone buffer */
310                 offset_in_zone = offset_in_space - def->offset;
311                 r = (s32) * (coreDump->zone[i] + offset_in_zone);
312
313                 unifi_trace(NULL, UDBG6,
314                             "sp %d, offs 0x%04x = 0x%04x (in z%d 0x%04x->0x%04x)\n",
315                             space, offset_in_space, r,
316                             i, def->offset, zone_end_offset - 1);
317                 break;
318             }
319         }
320     }
321     return r;
322 }
323
324
325 /*
326  * ---------------------------------------------------------------------------
327  *  unifi_coredump_get_value
328  *
329  *      Retrieve the value of a register buffered from a previous core dump,
330  *      so that it may be reported back to application code.
331  *
332  *  Arguments:
333  *      card            Pointer to card struct
334  *      req_reg         Pointer to request parameter partially filled. This
335  *                      function puts in the values retrieved from the dump.
336  *
337  *  Returns:
338  *      CSR_RESULT_SUCCESS on success, or:
339  *      CSR_WIFI_HIP_RESULT_INVALID_VALUE         Null parameter error
340  *      CSR_WIFI_HIP_RESULT_RANGE                 Register out of range
341  *      CSR_WIFI_HIP_RESULT_NOT_FOUND             Dump index not (yet) captured
342  *
343  *  Notes:
344  * ---------------------------------------------------------------------------
345  */
346 CsrResult unifi_coredump_get_value(card_t *card, struct unifi_coredump_req *req)
347 {
348     CsrResult r;
349     s32 i = 0;
350     coredump_buffer *find_dump = NULL;
351
352     if (req == NULL || card == NULL)
353     {
354         r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
355         goto done;
356     }
357     req->value = -1;
358     if (card->dump_buf == NULL)
359     {
360         unifi_trace(card->ospriv, UDBG2, "No coredump buffers\n");
361         r = CSR_WIFI_HIP_RESULT_NOT_FOUND;     /* Coredumping disabled */
362         goto done;
363     }
364     if (card->dump_cur_read == NULL)
365     {
366         unifi_trace(card->ospriv, UDBG4, "No coredumps captured\n");
367         r = CSR_WIFI_HIP_RESULT_NOT_FOUND;     /* No coredump yet captured */
368         goto done;
369     }
370
371     /* Find the requested dump buffer */
372     switch (req->index)
373     {
374         case 0:     /* Newest */
375             find_dump = card->dump_cur_read;
376             break;
377         case -1:    /* Oldest: The next used slot forward */
378             for (find_dump = card->dump_cur_read->next;
379                  (find_dump->count == 0) && (find_dump != card->dump_cur_read);
380                  find_dump = card->dump_cur_read->next)
381             {
382             }
383             break;
384         default:    /* Number of steps back from current read position */
385             for (i = 0, find_dump = card->dump_cur_read;
386                  i < req->index;
387                  i++, find_dump = find_dump->prev)
388             {
389                 /* Walk the list for the index'th entry, but
390                  * stop when about to wrap. */
391                 unifi_trace(card->ospriv, UDBG6,
392                             "%d: %d, @%p, p=%p, n=%p, cr=%p, h=%p\n",
393                             i, find_dump->count, find_dump, find_dump->prev,
394                             find_dump->next, card->dump_cur_read, card->dump_buf);
395                 if (find_dump->prev == card->dump_cur_read)
396                 {
397                     /* Wrapped but still not found, index out of range */
398                     if (i != req->index)
399                     {
400                         unifi_trace(card->ospriv, UDBG6,
401                                     "Dump index %d not found %d\n", req->index, i);
402                         r = CSR_WIFI_HIP_RESULT_NOT_FOUND;
403                         goto done;
404                     }
405                     break;
406                 }
407             }
408             break;
409     }
410
411     /* Check if the slot is actually filled with a core dump */
412     if (find_dump->count == 0)
413     {
414         unifi_trace(card->ospriv, UDBG4, "Not captured %d\n", req->index);
415         r = CSR_WIFI_HIP_RESULT_NOT_FOUND;
416         goto done;
417     }
418
419     unifi_trace(card->ospriv, UDBG6, "Req index %d, found seq %d at step %d\n",
420                 req->index, find_dump->count, i);
421
422     /* Find the appropriate entry in the buffer */
423     req->value = get_value_from_coredump(find_dump, req->space, (u16)req->offset);
424     if (req->value < 0)
425     {
426         r = CSR_WIFI_HIP_RESULT_RANGE;     /* Un-captured register */
427         unifi_trace(card->ospriv, UDBG4,
428                     "Can't read space %d, reg 0x%x from coredump buffer %d\n",
429                     req->space, req->offset, req->index);
430     }
431     else
432     {
433         r = CSR_RESULT_SUCCESS;
434     }
435
436     /* Update the private request structure with the found values */
437     req->chip_ver = find_dump->chip_ver;
438     req->fw_ver = find_dump->fw_ver;
439     req->timestamp = find_dump->timestamp;
440     req->requestor = find_dump->requestor;
441     req->serial = find_dump->count;
442
443 done:
444     return r;
445 } /* unifi_coredump_get_value() */
446
447
448 /*
449  * ---------------------------------------------------------------------------
450  *  unifi_coredump_read_zone
451  *
452  *      Captures a UniFi memory zone into a buffer on the host
453  *
454  *  Arguments:
455  *      card          Pointer to card struct
456  *      zonebuf       Pointer to on-host buffer to dump the memory zone into
457  *      def           Pointer to description of the memory zone to read from UniFi.
458  *
459  *  Returns:
460  *      CSR_RESULT_SUCCESS                   on success, or:
461  *      CSR_RESULT_FAILURE                   SDIO error
462  *      CSR_WIFI_HIP_RESULT_INVALID_VALUE         Parameter error
463  *
464  *  Notes:
465  *      It is assumed that the caller has already stopped the XAPs
466  * ---------------------------------------------------------------------------
467  */
468 static CsrResult unifi_coredump_read_zone(card_t *card, u16 *zonebuf, const struct coredump_zone *def)
469 {
470     CsrResult r;
471
472     if (zonebuf == NULL || def == NULL)
473     {
474         r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
475         goto done;
476     }
477
478     /* Select XAP CPU if necessary */
479     if (def->cpu != UNIFI_PROC_INVALID)
480     {
481         if (def->cpu != UNIFI_PROC_MAC && def->cpu != UNIFI_PROC_PHY)
482         {
483             r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
484             goto done;
485         }
486         r = unifi_set_proc_select(card, def->cpu);
487         if (r != CSR_RESULT_SUCCESS)
488         {
489             goto done;
490         }
491     }
492
493     unifi_trace(card->ospriv, UDBG4,
494                 "Dump sp %d, offs 0x%04x, 0x%04x words @GP=%08x CPU %d\n",
495                 def->space, def->offset, def->length, def->gp, def->cpu);
496
497     /* Read on-chip RAM (byte-wise) */
498     r = unifi_card_readn(card, def->gp, zonebuf, (u16)(def->length * 2));
499     if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
500     {
501         goto done;
502     }
503     if (r != CSR_RESULT_SUCCESS)
504     {
505         unifi_error(card->ospriv, "Can't read UniFi shared data area\n");
506         goto done;
507     }
508
509 done:
510     return r;
511 }
512
513
514 /*
515  * ---------------------------------------------------------------------------
516  *  unifi_coredump_read_zones
517  *
518  *      Walks through the table of on-chip memory zones defined in zonedef_table,
519  *      and reads each of them from the UniFi chip
520  *
521  *  Arguments:
522  *      card          Pointer to card struct
523  *      dump_buf      Buffer into which register values will be dumped
524  *
525  *  Returns:
526  *      CSR_RESULT_SUCCESS                   on success, or:
527  *      CSR_RESULT_FAILURE                   SDIO error
528  *      CSR_WIFI_HIP_RESULT_INVALID_VALUE         Parameter error
529  *
530  *  Notes:
531  *      It is assumed that the caller has already stopped the XAPs
532  * ---------------------------------------------------------------------------
533  */
534 static CsrResult unifi_coredump_read_zones(card_t *card, coredump_buffer *dump_buf)
535 {
536     CsrResult r = CSR_RESULT_SUCCESS;
537     s32 i;
538
539     /* Walk the table of coredump zone definitions and read them from the chip */
540     for (i = 0;
541          (i < HIP_CDUMP_NUM_ZONES) && (r == 0);
542          i++)
543     {
544         r = unifi_coredump_read_zone(card, dump_buf->zone[i], &zonedef_table[i]);
545     }
546
547     return r;
548 }
549
550
551 /*
552  * ---------------------------------------------------------------------------
553  *  unifi_coredump_from_sdio
554  *
555  *      Capture the status of the UniFi processors, over SDIO
556  *
557  *  Arguments:
558  *      card            Pointer to card struct
559  *      reg_buffer      Buffer into which register values will be dumped
560  *
561  *  Returns:
562  *      CSR_RESULT_SUCCESS                   on success, or:
563  *      CSR_RESULT_FAILURE                   SDIO error
564  *      CSR_WIFI_HIP_RESULT_INVALID_VALUE         Parameter error
565  *
566  *  Notes:
567  * ---------------------------------------------------------------------------
568  */
569 static CsrResult unifi_coredump_from_sdio(card_t *card, coredump_buffer *dump_buf)
570 {
571     u16 val;
572     CsrResult r;
573     u32 sdio_addr;
574
575     if (dump_buf == NULL)
576     {
577         r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
578         goto done;
579     }
580
581
582     /* Chip and firmware version */
583     unifi_trace(card->ospriv, UDBG4, "Get chip version\n");
584     sdio_addr = 2 * ChipHelper_GBL_CHIP_VERSION(card->helper);
585     if (sdio_addr != 0)
586     {
587         r = unifi_read_direct16(card, sdio_addr, &val);
588         if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
589         {
590             goto done;
591         }
592         if (r != CSR_RESULT_SUCCESS)
593         {
594             unifi_error(card->ospriv, "Can't read GBL_CHIP_VERSION\n");
595             goto done;
596         }
597     }
598     dump_buf->chip_ver = val;
599     dump_buf->fw_ver = card->build_id;
600
601     unifi_trace(card->ospriv, UDBG4, "chip_ver 0x%04x, fw_ver %u\n",
602                 dump_buf->chip_ver, dump_buf->fw_ver);
603
604     /* Capture the memory zones required from UniFi */
605     r = unifi_coredump_read_zones(card, dump_buf);
606     if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
607     {
608         goto done;
609     }
610     if (r != CSR_RESULT_SUCCESS)
611     {
612         unifi_error(card->ospriv, "Can't read UniFi memory areas\n");
613         goto done;
614     }
615
616 done:
617     return r;
618 } /* unifi_coredump_from_sdio() */
619
620
621 #ifndef UNIFI_DISABLE_COREDUMP
622 /*
623  * ---------------------------------------------------------------------------
624  *  new_coredump_node
625  *
626  *      Allocates a coredump linked-list node, and links it to the previous.
627  *
628  *  Arguments:
629  *      ospriv          OS context
630  *      prevnode        Previous node to link into
631  *
632  *  Returns:
633  *      Pointer to valid coredump_buffer on success
634  *      NULL on memory allocation failure
635  *
636  *  Notes:
637  *      Allocates "all or nothing"
638  * ---------------------------------------------------------------------------
639  */
640 static
641 coredump_buffer* new_coredump_node(void *ospriv, coredump_buffer *prevnode)
642 {
643     coredump_buffer *newnode = NULL;
644     u16 *newzone = NULL;
645     s32 i;
646     u32 zone_size;
647
648     /* Allocate node header */
649     newnode = kzalloc(sizeof(coredump_buffer), GFP_KERNEL);
650     if (newnode == NULL)
651     {
652         return NULL;
653     }
654
655     /* Allocate chip memory zone capture buffers */
656     for (i = 0; i < HIP_CDUMP_NUM_ZONES; i++)
657     {
658         zone_size = sizeof(u16) * zonedef_table[i].length;
659         newzone = kzalloc(zone_size, GFP_KERNEL);
660         newnode->zone[i] = newzone;
661         if (newzone == NULL)
662         {
663             unifi_error(ospriv, "Out of memory on coredump zone %d (%d words)\n",
664                         i, zonedef_table[i].length);
665             break;
666         }
667     }
668
669     /* Clean up if any zone alloc failed */
670     if (newzone == NULL)
671     {
672         for (i = 0; newnode->zone[i] != NULL; i++)
673         {
674             kfree(newnode->zone[i]);
675             newnode->zone[i] = NULL;
676         }
677     }
678
679     /* Link to previous node */
680     newnode->prev = prevnode;
681     if (prevnode)
682     {
683         prevnode->next = newnode;
684     }
685     newnode->next = NULL;
686
687     return newnode;
688 }
689
690
691 #endif /* UNIFI_DISABLE_COREDUMP */
692
693 /*
694  * ---------------------------------------------------------------------------
695  *  unifi_coredump_init
696  *
697  *      Allocates buffers for the automatic SDIO core dump
698  *
699  *  Arguments:
700  *      card                Pointer to card struct
701  *      num_dump_buffers    Number of buffers to reserve for coredumps
702  *
703  *  Returns:
704  *      CSR_RESULT_SUCCESS               on success, or:
705  *      CSR_WIFI_HIP_RESULT_NO_MEMORY         memory allocation failed
706  *
707  *  Notes:
708  *      Allocates space in advance, to be used for the last n coredump buffers
709  *      the intention being that the size is sufficient for at least one dump,
710  *      probably several.
711  *      It's probably advisable to have at least 2 coredump buffers to allow
712  *      one to be enquired with the unifi_coredump tool, while leaving another
713  *      free for capturing.
714  * ---------------------------------------------------------------------------
715  */
716 CsrResult unifi_coredump_init(card_t *card, u16 num_dump_buffers)
717 {
718 #ifndef UNIFI_DISABLE_COREDUMP
719     void *ospriv = card->ospriv;
720     coredump_buffer *prev = NULL;
721     coredump_buffer *newnode = NULL;
722     u32 i = 0;
723 #endif
724
725     card->request_coredump_on_reset = 0;
726     card->dump_next_write = NULL;
727     card->dump_cur_read = NULL;
728     card->dump_buf = NULL;
729
730 #ifndef UNIFI_DISABLE_COREDUMP
731     unifi_trace(ospriv, UDBG1,
732                 "Allocate buffers for %d core dumps\n", num_dump_buffers);
733     if (num_dump_buffers == 0)
734     {
735         goto done;
736     }
737
738     /* Root node */
739     card->dump_buf = new_coredump_node(ospriv, NULL);
740     if (card->dump_buf == NULL)
741     {
742         goto fail;
743     }
744     prev = card->dump_buf;
745     newnode = card->dump_buf;
746
747     /* Add each subsequent node at tail */
748     for (i = 1; i < num_dump_buffers; i++)
749     {
750         newnode = new_coredump_node(ospriv, prev);
751         if (newnode == NULL)
752         {
753             goto fail;
754         }
755         prev = newnode;
756     }
757
758     /* Link the first and last nodes to make the list circular */
759     card->dump_buf->prev = newnode;
760     newnode->next = card->dump_buf;
761
762     /* Set initial r/w access pointers */
763     card->dump_next_write = card->dump_buf;
764     card->dump_cur_read = NULL;
765
766     unifi_trace(ospriv, UDBG2, "Core dump configured (%d dumps max)\n", i);
767
768 done:
769 #endif
770     return CSR_RESULT_SUCCESS;
771
772 #ifndef UNIFI_DISABLE_COREDUMP
773 fail:
774     /* Unwind what we allocated so far */
775     unifi_error(ospriv, "Out of memory allocating core dump node %d\n", i);
776     unifi_coredump_free(card);
777     return CSR_WIFI_HIP_RESULT_NO_MEMORY;
778 #endif
779 } /* unifi_coreump_init() */
780
781
782 /*
783  * ---------------------------------------------------------------------------
784  *  unifi_coredump_free
785  *
786  *      Free all memory dynamically allocated for core dump
787  *
788  *  Arguments:
789  *      card            Pointer to card struct
790  *
791  *  Returns:
792  *      None
793  *
794  *  Notes:
795  * ---------------------------------------------------------------------------
796  */
797 void unifi_coredump_free(card_t *card)
798 {
799     void *ospriv = card->ospriv;
800     coredump_buffer *node, *del_node;
801     s16 i = 0;
802     s16 j;
803
804     unifi_trace(ospriv, UDBG2, "Core dump de-configured\n");
805
806     if (card->dump_buf == NULL)
807     {
808         return;
809     }
810
811     node = card->dump_buf;
812     do
813     {
814         /* Free payload zones */
815         for (j = 0; j < HIP_CDUMP_NUM_ZONES; j++)
816         {
817             kfree(node->zone[j]);
818             node->zone[j] = NULL;
819         }
820
821         /* Detach */
822         del_node = node;
823         node = node->next;
824
825         /* Free header */
826         kfree(del_node);
827         i++;
828     } while ((node != NULL) && (node != card->dump_buf));
829
830     unifi_trace(ospriv, UDBG3, "Freed %d coredump buffers\n", i);
831
832     card->dump_buf = NULL;
833     card->dump_next_write = NULL;
834     card->dump_cur_read = NULL;
835 } /* unifi_coredump_free() */
836
837