185630469SLior David // SPDX-License-Identifier: ISC
27dc47258SVladimir Kondratiev /*
3af3db60aSLazar Alexei * Copyright (c) 2015,2017 Qualcomm Atheros, Inc.
4a0618945SAhmad Masri * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
57dc47258SVladimir Kondratiev */
67dc47258SVladimir Kondratiev
77dc47258SVladimir Kondratiev #include "wil6210.h"
87dc47258SVladimir Kondratiev #include <linux/devcoredump.h>
97dc47258SVladimir Kondratiev
wil_fw_get_crash_dump_bounds(struct wil6210_priv * wil,u32 * out_dump_size,u32 * out_host_min)107dc47258SVladimir Kondratiev static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,
117dc47258SVladimir Kondratiev u32 *out_dump_size, u32 *out_host_min)
127dc47258SVladimir Kondratiev {
137dc47258SVladimir Kondratiev int i;
147dc47258SVladimir Kondratiev const struct fw_map *map;
157dc47258SVladimir Kondratiev u32 host_min, host_max, tmp_max;
167dc47258SVladimir Kondratiev
177dc47258SVladimir Kondratiev if (!out_dump_size)
187dc47258SVladimir Kondratiev return -EINVAL;
197dc47258SVladimir Kondratiev
207dc47258SVladimir Kondratiev /* calculate the total size of the unpacked crash dump */
217dc47258SVladimir Kondratiev BUILD_BUG_ON(ARRAY_SIZE(fw_mapping) == 0);
227dc47258SVladimir Kondratiev map = &fw_mapping[0];
237dc47258SVladimir Kondratiev host_min = map->host;
247dc47258SVladimir Kondratiev host_max = map->host + (map->to - map->from);
257dc47258SVladimir Kondratiev
267dc47258SVladimir Kondratiev for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) {
277dc47258SVladimir Kondratiev map = &fw_mapping[i];
287dc47258SVladimir Kondratiev
299a53d0b6SMaya Erez if (!map->crash_dump)
3061578820SLior David continue;
3161578820SLior David
327dc47258SVladimir Kondratiev if (map->host < host_min)
337dc47258SVladimir Kondratiev host_min = map->host;
347dc47258SVladimir Kondratiev
357dc47258SVladimir Kondratiev tmp_max = map->host + (map->to - map->from);
367dc47258SVladimir Kondratiev if (tmp_max > host_max)
377dc47258SVladimir Kondratiev host_max = tmp_max;
387dc47258SVladimir Kondratiev }
397dc47258SVladimir Kondratiev
407dc47258SVladimir Kondratiev *out_dump_size = host_max - host_min;
417dc47258SVladimir Kondratiev if (out_host_min)
427dc47258SVladimir Kondratiev *out_host_min = host_min;
437dc47258SVladimir Kondratiev
447dc47258SVladimir Kondratiev return 0;
457dc47258SVladimir Kondratiev }
467dc47258SVladimir Kondratiev
wil_fw_copy_crash_dump(struct wil6210_priv * wil,void * dest,u32 size)47ea3ade75SLior David int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
487dc47258SVladimir Kondratiev {
49c715b384SAlexei Avshalom Lazar int i;
507dc47258SVladimir Kondratiev const struct fw_map *map;
517dc47258SVladimir Kondratiev void *data;
527dc47258SVladimir Kondratiev u32 host_min, dump_size, offset, len;
537dc47258SVladimir Kondratiev
547dc47258SVladimir Kondratiev if (wil_fw_get_crash_dump_bounds(wil, &dump_size, &host_min)) {
55af3db60aSLazar Alexei wil_err(wil, "fail to obtain crash dump size\n");
567dc47258SVladimir Kondratiev return -EINVAL;
577dc47258SVladimir Kondratiev }
587dc47258SVladimir Kondratiev
597dc47258SVladimir Kondratiev if (dump_size > size) {
60af3db60aSLazar Alexei wil_err(wil, "not enough space for dump. Need %d have %d\n",
61af3db60aSLazar Alexei dump_size, size);
627dc47258SVladimir Kondratiev return -EINVAL;
637dc47258SVladimir Kondratiev }
647dc47258SVladimir Kondratiev
65c715b384SAlexei Avshalom Lazar down_write(&wil->mem_lock);
66c715b384SAlexei Avshalom Lazar
67c715b384SAlexei Avshalom Lazar if (test_bit(wil_status_suspending, wil->status) ||
68c715b384SAlexei Avshalom Lazar test_bit(wil_status_suspended, wil->status)) {
69c715b384SAlexei Avshalom Lazar wil_err(wil,
70c715b384SAlexei Avshalom Lazar "suspend/resume in progress. cannot copy crash dump\n");
71c715b384SAlexei Avshalom Lazar up_write(&wil->mem_lock);
72c715b384SAlexei Avshalom Lazar return -EBUSY;
73c715b384SAlexei Avshalom Lazar }
74a8fd16d7SMaya Erez
757dc47258SVladimir Kondratiev /* copy to crash dump area */
767dc47258SVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
777dc47258SVladimir Kondratiev map = &fw_mapping[i];
787dc47258SVladimir Kondratiev
799a53d0b6SMaya Erez if (!map->crash_dump)
8061578820SLior David continue;
8161578820SLior David
827dc47258SVladimir Kondratiev data = (void * __force)wil->csr + HOSTADDR(map->host);
837dc47258SVladimir Kondratiev len = map->to - map->from;
847dc47258SVladimir Kondratiev offset = map->host - host_min;
857dc47258SVladimir Kondratiev
86af3db60aSLazar Alexei wil_dbg_misc(wil,
87af3db60aSLazar Alexei "fw_copy_crash_dump: - dump %s, size %d, offset %d\n",
88af3db60aSLazar Alexei fw_mapping[i].name, len, offset);
897dc47258SVladimir Kondratiev
907dc47258SVladimir Kondratiev wil_memcpy_fromio_32((void * __force)(dest + offset),
917dc47258SVladimir Kondratiev (const void __iomem * __force)data, len);
927dc47258SVladimir Kondratiev }
93c715b384SAlexei Avshalom Lazar
94c715b384SAlexei Avshalom Lazar up_write(&wil->mem_lock);
95a8fd16d7SMaya Erez
967dc47258SVladimir Kondratiev return 0;
977dc47258SVladimir Kondratiev }
987dc47258SVladimir Kondratiev
wil_fw_core_dump(struct wil6210_priv * wil)997dc47258SVladimir Kondratiev void wil_fw_core_dump(struct wil6210_priv *wil)
1007dc47258SVladimir Kondratiev {
1017dc47258SVladimir Kondratiev void *fw_dump_data;
1027dc47258SVladimir Kondratiev u32 fw_dump_size;
1037dc47258SVladimir Kondratiev
1047dc47258SVladimir Kondratiev if (wil_fw_get_crash_dump_bounds(wil, &fw_dump_size, NULL)) {
105af3db60aSLazar Alexei wil_err(wil, "fail to get fw dump size\n");
1067dc47258SVladimir Kondratiev return;
1077dc47258SVladimir Kondratiev }
1087dc47258SVladimir Kondratiev
1097dc47258SVladimir Kondratiev fw_dump_data = vzalloc(fw_dump_size);
1107dc47258SVladimir Kondratiev if (!fw_dump_data)
1117dc47258SVladimir Kondratiev return;
1127dc47258SVladimir Kondratiev
1137dc47258SVladimir Kondratiev if (wil_fw_copy_crash_dump(wil, fw_dump_data, fw_dump_size)) {
1147dc47258SVladimir Kondratiev vfree(fw_dump_data);
1157dc47258SVladimir Kondratiev return;
1167dc47258SVladimir Kondratiev }
1177dc47258SVladimir Kondratiev /* fw_dump_data will be free in device coredump release function
1187dc47258SVladimir Kondratiev * after 5 min
1197dc47258SVladimir Kondratiev */
120*38a523a2SGreg Kroah-Hartman dev_coredumpv(wil_to_dev(wil), fw_dump_data, fw_dump_size, GFP_KERNEL);
121af3db60aSLazar Alexei wil_info(wil, "fw core dumped, size %d bytes\n", fw_dump_size);
1227dc47258SVladimir Kondratiev }
123