linux/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015,2017 Qualcomm Atheros, Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include "wil6210.h"
  18#include <linux/devcoredump.h>
  19
  20static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,
  21                                        u32 *out_dump_size, u32 *out_host_min)
  22{
  23        int i;
  24        const struct fw_map *map;
  25        u32 host_min, host_max, tmp_max;
  26
  27        if (!out_dump_size)
  28                return -EINVAL;
  29
  30        /* calculate the total size of the unpacked crash dump */
  31        BUILD_BUG_ON(ARRAY_SIZE(fw_mapping) == 0);
  32        map = &fw_mapping[0];
  33        host_min = map->host;
  34        host_max = map->host + (map->to - map->from);
  35
  36        for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) {
  37                map = &fw_mapping[i];
  38
  39                if (!map->fw)
  40                        continue;
  41
  42                if (map->host < host_min)
  43                        host_min = map->host;
  44
  45                tmp_max = map->host + (map->to - map->from);
  46                if (tmp_max > host_max)
  47                        host_max = tmp_max;
  48        }
  49
  50        *out_dump_size = host_max - host_min;
  51        if (out_host_min)
  52                *out_host_min = host_min;
  53
  54        return 0;
  55}
  56
  57int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
  58{
  59        int i;
  60        const struct fw_map *map;
  61        void *data;
  62        u32 host_min, dump_size, offset, len;
  63
  64        if (wil_fw_get_crash_dump_bounds(wil, &dump_size, &host_min)) {
  65                wil_err(wil, "fail to obtain crash dump size\n");
  66                return -EINVAL;
  67        }
  68
  69        if (dump_size > size) {
  70                wil_err(wil, "not enough space for dump. Need %d have %d\n",
  71                        dump_size, size);
  72                return -EINVAL;
  73        }
  74
  75        set_bit(wil_status_collecting_dumps, wil->status);
  76        if (test_bit(wil_status_suspending, wil->status) ||
  77            test_bit(wil_status_suspended, wil->status) ||
  78            test_bit(wil_status_resetting, wil->status)) {
  79                wil_err(wil, "cannot collect fw dump during suspend/reset\n");
  80                clear_bit(wil_status_collecting_dumps, wil->status);
  81                return -EINVAL;
  82        }
  83
  84        /* copy to crash dump area */
  85        for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
  86                map = &fw_mapping[i];
  87
  88                if (!map->fw)
  89                        continue;
  90
  91                data = (void * __force)wil->csr + HOSTADDR(map->host);
  92                len = map->to - map->from;
  93                offset = map->host - host_min;
  94
  95                wil_dbg_misc(wil,
  96                             "fw_copy_crash_dump: - dump %s, size %d, offset %d\n",
  97                             fw_mapping[i].name, len, offset);
  98
  99                wil_memcpy_fromio_32((void * __force)(dest + offset),
 100                                     (const void __iomem * __force)data, len);
 101        }
 102
 103        clear_bit(wil_status_collecting_dumps, wil->status);
 104
 105        return 0;
 106}
 107
 108void wil_fw_core_dump(struct wil6210_priv *wil)
 109{
 110        void *fw_dump_data;
 111        u32 fw_dump_size;
 112
 113        if (wil_fw_get_crash_dump_bounds(wil, &fw_dump_size, NULL)) {
 114                wil_err(wil, "fail to get fw dump size\n");
 115                return;
 116        }
 117
 118        fw_dump_data = vzalloc(fw_dump_size);
 119        if (!fw_dump_data)
 120                return;
 121
 122        if (wil_fw_copy_crash_dump(wil, fw_dump_data, fw_dump_size)) {
 123                vfree(fw_dump_data);
 124                return;
 125        }
 126        /* fw_dump_data will be free in device coredump release function
 127         * after 5 min
 128         */
 129        dev_coredumpv(wil_to_dev(wil), fw_dump_data, fw_dump_size, GFP_KERNEL);
 130        wil_info(wil, "fw core dumped, size %d bytes\n", fw_dump_size);
 131}
 132