Just a few nits, this is a lot of code and I'll try to give it a second pass. On Wed, Aug 21, 2019 at 3:20 PM Wen Gong <wgong@xxxxxxxxxxxxxx> wrote: > > When firmware assert, it need coredump to analyze, this patch will > collect the register and memory info for sdio chip. > > The coredump configuration is different between PCIE and SDIO for > the same reversion, so this patch add bus type to distinguish PCIE > and SDIO chip for coredump. > > Tested with QCA6174 SDIO with firmware > WLAN.RMH.4.4.1-00007-QCARMSWP-1. > > Signed-off-by: Wen Gong <wgong@xxxxxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath10k/bmi.c | 1 + > drivers/net/wireless/ath/ath10k/core.c | 7 +- > drivers/net/wireless/ath/ath10k/core.h | 4 +- > drivers/net/wireless/ath/ath10k/coredump.c | 338 +++++++++++++++++++++++++++- > drivers/net/wireless/ath/ath10k/coredump.h | 1 + > drivers/net/wireless/ath/ath10k/hw.h | 1 + > drivers/net/wireless/ath/ath10k/sdio.c | 335 ++++++++++++++++++++++++++- > drivers/net/wireless/ath/ath10k/targaddrs.h | 10 + > 8 files changed, 692 insertions(+), 5 deletions(-) > > diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c > index 95dc4be..990fa4d 100644 > --- a/drivers/net/wireless/ath/ath10k/bmi.c > +++ b/drivers/net/wireless/ath/ath10k/bmi.c > @@ -197,6 +197,7 @@ int ath10k_bmi_read_memory(struct ath10k *ar, > > return 0; > } > +EXPORT_SYMBOL(ath10k_bmi_read_memory); > > int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val) > { > diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c > index dc45d16..0ea4c36 100644 > --- a/drivers/net/wireless/ath/ath10k/core.c > +++ b/drivers/net/wireless/ath/ath10k/core.c > @@ -33,7 +33,6 @@ > static bool skip_otp; > static bool rawmode; > static bool fw_diag_log; > - Don't do whitespace changes (unless you're changing code in the area anyway). > unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) | > BIT(ATH10K_FW_CRASH_DUMP_CE_DATA); > > @@ -708,6 +707,10 @@ static void ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode) > ath10k_bmi_read32(ar, hi_option_flag, ¶m); > param |= HI_OPTION_DISABLE_DBGLOG; > ath10k_bmi_write32(ar, hi_option_flag, param); > + > + ath10k_bmi_read32(ar, hi_option_flag2, ¶m); > + param |= HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_HOST; > + ath10k_bmi_write32(ar, hi_option_flag2, param); > } > > static int ath10k_init_configure_target(struct ath10k *ar) > @@ -1953,6 +1956,8 @@ static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name, > scnprintf(fw_name, fw_name_len, "%s-%d.bin", > ATH10K_FW_FILE_BASE, fw_api); > break; > + default: > + break; Why? > } > } > > diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h > index 4d7db07..1b52a3c 100644 > --- a/drivers/net/wireless/ath/ath10k/core.h > +++ b/drivers/net/wireless/ath/ath10k/core.h > @@ -97,7 +97,9 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) > return "usb"; > case ATH10K_BUS_SNOC: > return "snoc"; > - } > + default: > + return "unknown"; > +} This change does not look very useful? Also the indentation is broken. > > return "unknown"; > } > diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c > index b6d2932..b287509 100644 > --- a/drivers/net/wireless/ath/ath10k/coredump.c > +++ b/drivers/net/wireless/ath/ath10k/coredump.c > @@ -270,6 +270,277 @@ > {0x80010, 0x80020}, > }; > > +static const struct ath10k_mem_section qca6174_hw30_sdio_register_sections[] = { > + {0x800, 0x810}, > + {0x820, 0x82C}, > + {0x830, 0x8F4}, > + {0x90C, 0x91C}, > + {0xA14, 0xA18}, > + {0xA84, 0xA94}, > + {0xAA8, 0xAD4}, > + {0xADC, 0xB40}, > + {0x1000, 0x10A4}, > + {0x10BC, 0x111C}, > + {0x1134, 0x1138}, > + {0x1144, 0x114C}, > + {0x1150, 0x115C}, > + {0x1160, 0x1178}, > + {0x1240, 0x1260}, > + {0x2000, 0x207C}, > + {0x3000, 0x3014}, > + {0x4000, 0x4014}, > + {0x5000, 0x5124}, > + {0x6000, 0x6040}, > + {0x6080, 0x60CC}, > + {0x6100, 0x611C}, > + {0x6140, 0x61D8}, > + {0x6200, 0x6238}, > + {0x6240, 0x628C}, > + {0x62C0, 0x62EC}, > + {0x6380, 0x63E8}, > + {0x6400, 0x6440}, > + {0x6480, 0x64CC}, > + {0x6500, 0x651C}, > + {0x6540, 0x6580}, > + {0x6600, 0x6638}, > + {0x6640, 0x668C}, > + {0x66C0, 0x66EC}, > + {0x6780, 0x67E8}, > + {0x7080, 0x708C}, > + {0x70C0, 0x70C8}, > + {0x7400, 0x741C}, > + {0x7440, 0x7454}, > + {0x7800, 0x7818}, > + {0x8010, 0x8060}, > + {0x8080, 0x8084}, > + {0x80A0, 0x80A4}, > + {0x80C0, 0x80C4}, > + {0x80E0, 0x80ec}, > + {0x8110, 0x8128}, > + {0x9000, 0x9004}, > + {0xF000, 0xF0E0}, > + {0xF140, 0xF190}, > + {0xF250, 0xF25C}, > + {0xF260, 0xF268}, > + {0xF26C, 0xF2A8}, > + {0x10008, 0x1000C}, > + {0x10014, 0x10018}, > + {0x1001C, 0x10020}, > + {0x10024, 0x10028}, > + {0x10030, 0x10034}, > + {0x10040, 0x10054}, > + {0x10058, 0x1007C}, > + {0x10080, 0x100C4}, > + {0x100C8, 0x10114}, > + {0x1012C, 0x10130}, > + {0x10138, 0x10144}, > + {0x10200, 0x10220}, > + {0x10230, 0x10250}, > + {0x10260, 0x10280}, > + {0x10290, 0x102B0}, > + {0x102C0, 0x102DC}, > + {0x102E0, 0x102F4}, > + {0x102FC, 0x1037C}, > + {0x10380, 0x10390}, > + {0x10800, 0x10828}, > + {0x10840, 0x10844}, > + {0x10880, 0x10884}, > + {0x108C0, 0x108E8}, > + {0x10900, 0x10928}, > + {0x10940, 0x10944}, > + {0x10980, 0x10984}, > + {0x109C0, 0x109E8}, > + {0x10A00, 0x10A28}, > + {0x10A40, 0x10A50}, > + {0x11000, 0x11028}, > + {0x11030, 0x11034}, > + {0x11038, 0x11068}, > + {0x11070, 0x11074}, > + {0x11078, 0x110A8}, > + {0x110B0, 0x110B4}, > + {0x110B8, 0x110E8}, > + {0x110F0, 0x110F4}, > + {0x110F8, 0x11128}, > + {0x11138, 0x11144}, > + {0x11178, 0x11180}, > + {0x111B8, 0x111C0}, > + {0x111F8, 0x11200}, > + {0x11238, 0x1123C}, > + {0x11270, 0x11274}, > + {0x11278, 0x1127C}, > + {0x112B0, 0x112B4}, > + {0x112B8, 0x112BC}, > + {0x112F0, 0x112F4}, > + {0x112F8, 0x112FC}, > + {0x11338, 0x1133C}, > + {0x11378, 0x1137C}, > + {0x113B8, 0x113BC}, > + {0x113F8, 0x113FC}, > + {0x11438, 0x11440}, > + {0x11478, 0x11480}, > + {0x114B8, 0x114BC}, > + {0x114F8, 0x114FC}, > + {0x11538, 0x1153C}, > + {0x11578, 0x1157C}, > + {0x115B8, 0x115BC}, > + {0x115F8, 0x115FC}, > + {0x11638, 0x1163C}, > + {0x11678, 0x1167C}, > + {0x116B8, 0x116BC}, > + {0x116F8, 0x116FC}, > + {0x11738, 0x1173C}, > + {0x11778, 0x1177C}, > + {0x117B8, 0x117BC}, > + {0x117F8, 0x117FC}, > + {0x17000, 0x1701C}, > + {0x17020, 0x170AC}, > + {0x18000, 0x18050}, > + {0x18054, 0x18074}, > + {0x18080, 0x180D4}, > + {0x180DC, 0x18104}, > + {0x18108, 0x1813C}, > + {0x18144, 0x18148}, > + {0x18168, 0x18174}, > + {0x18178, 0x18180}, > + {0x181C8, 0x181E0}, > + {0x181E4, 0x181E8}, > + {0x181EC, 0x1820C}, > + {0x1825C, 0x18280}, > + {0x18284, 0x18290}, > + {0x18294, 0x182A0}, > + {0x18300, 0x18304}, > + {0x18314, 0x18320}, > + {0x18328, 0x18350}, > + {0x1835C, 0x1836C}, > + {0x18370, 0x18390}, > + {0x18398, 0x183AC}, > + {0x183BC, 0x183D8}, > + {0x183DC, 0x183F4}, > + {0x18400, 0x186F4}, > + {0x186F8, 0x1871C}, > + {0x18720, 0x18790}, > + {0x19800, 0x19830}, > + {0x19834, 0x19840}, > + {0x19880, 0x1989C}, > + {0x198A4, 0x198B0}, > + {0x198BC, 0x19900}, > + {0x19C00, 0x19C88}, > + {0x19D00, 0x19D20}, > + {0x19E00, 0x19E7C}, > + {0x19E80, 0x19E94}, > + {0x19E98, 0x19EAC}, > + {0x19EB0, 0x19EBC}, > + {0x19F70, 0x19F74}, > + {0x19F80, 0x19F8C}, > + {0x19FA0, 0x19FB4}, > + {0x19FC0, 0x19FD8}, > + {0x1A000, 0x1A200}, > + {0x1A204, 0x1A210}, > + {0x1A228, 0x1A22C}, > + {0x1A230, 0x1A248}, > + {0x1A250, 0x1A270}, > + {0x1A280, 0x1A290}, > + {0x1A2A0, 0x1A2A4}, > + {0x1A2C0, 0x1A2EC}, > + {0x1A300, 0x1A3BC}, > + {0x1A3F0, 0x1A3F4}, > + {0x1A3F8, 0x1A434}, > + {0x1A438, 0x1A444}, > + {0x1A448, 0x1A468}, > + {0x1A580, 0x1A58C}, > + {0x1A644, 0x1A654}, > + {0x1A670, 0x1A698}, > + {0x1A6AC, 0x1A6B0}, > + {0x1A6D0, 0x1A6D4}, > + {0x1A6EC, 0x1A70C}, > + {0x1A710, 0x1A738}, > + {0x1A7C0, 0x1A7D0}, > + {0x1A7D4, 0x1A7D8}, > + {0x1A7DC, 0x1A7E4}, > + {0x1A7F0, 0x1A7F8}, > + {0x1A888, 0x1A89C}, > + {0x1A8A8, 0x1A8AC}, > + {0x1A8C0, 0x1A8DC}, > + {0x1A8F0, 0x1A8FC}, > + {0x1AE04, 0x1AE08}, > + {0x1AE18, 0x1AE24}, > + {0x1AF80, 0x1AF8C}, > + {0x1AFA0, 0x1AFB4}, > + {0x1B000, 0x1B200}, > + {0x1B284, 0x1B288}, > + {0x1B2D0, 0x1B2D8}, > + {0x1B2DC, 0x1B2EC}, > + {0x1B300, 0x1B340}, > + {0x1B374, 0x1B378}, > + {0x1B380, 0x1B384}, > + {0x1B388, 0x1B38C}, > + {0x1B404, 0x1B408}, > + {0x1B420, 0x1B428}, > + {0x1B440, 0x1B444}, > + {0x1B448, 0x1B44C}, > + {0x1B450, 0x1B458}, > + {0x1B45C, 0x1B468}, > + {0x1B584, 0x1B58C}, > + {0x1B68C, 0x1B690}, > + {0x1B6AC, 0x1B6B0}, > + {0x1B7F0, 0x1B7F8}, > + {0x1C800, 0x1CC00}, > + {0x1CE00, 0x1CE04}, > + {0x1CF80, 0x1CF84}, > + {0x1D200, 0x1D800}, > + {0x1E000, 0x20014}, > + {0x20100, 0x20124}, > + {0x21400, 0x217A8}, > + {0x21800, 0x21BA8}, > + {0x21C00, 0x21FA8}, > + {0x22000, 0x223A8}, > + {0x22400, 0x227A8}, > + {0x22800, 0x22BA8}, > + {0x22C00, 0x22FA8}, > + {0x23000, 0x233A8}, > + {0x24000, 0x24034}, > + > + /* EFUSE0,1,2 is disabled here > + * because it's state may be reset its state > + * > + * {0x24800, 0x24804}, > + * {0x25000, 0x25004}, > + * {0x25800, 0x25804}, > + */ > + > + {0x26000, 0x26064}, > + {0x27000, 0x27024}, > + {0x34000, 0x3400C}, > + {0x34400, 0x3445C}, > + {0x34800, 0x3485C}, > + {0x34C00, 0x34C5C}, > + {0x35000, 0x3505C}, > + {0x35400, 0x3545C}, > + {0x35800, 0x3585C}, > + {0x35C00, 0x35C5C}, > + {0x36000, 0x3605C}, > + {0x38000, 0x38064}, > + {0x38070, 0x380E0}, > + {0x3A000, 0x3A074}, > + > + /* DBI windows is skipped here, it can be only accessed when pcie > + * is active (not in reset) and CORE_CTRL_PCIE_LTSSM_EN = 0 && > + * PCIE_CTRL_APP_LTSSM_ENALBE=0. > + * {0x3C000 , 0x3C004}, > + */ > + > + {0x40000, 0x400A4}, > + > + /* SI register is skiped here. > + * Because it will cause bus hang > + * > + * {0x50000, 0x50018}, > + */ > + > + {0x80000, 0x8000C}, > + {0x80010, 0x80020}, > +}; > + > static const struct ath10k_mem_section qca6174_hw30_register_sections[] = { > {0x800, 0x810}, > {0x820, 0x82C}, > @@ -602,6 +873,59 @@ > }, > }; > > +static const struct ath10k_mem_region qca6174_hw30_sdio_mem_regions[] = { > + { > + .type = ATH10K_MEM_REGION_TYPE_DRAM, > + .start = 0x400000, > + .len = 0xa8000, > + .name = "DRAM", > + .section_table = { > + .sections = NULL, > + .size = 0, Indentation. > + }, > + }, > + { > + .type = ATH10K_MEM_REGION_TYPE_AXI, > + .start = 0xa0000, > + .len = 0x18000, > + .name = "AXI", > + .section_table = { > + .sections = NULL, > + .size = 0, > + }, > + }, > + { > + .type = ATH10K_MEM_REGION_TYPE_IRAM1, > + .start = 0x00980000, > + .len = 0x00080000, > + .name = "IRAM1", > + .section_table = { > + .sections = NULL, > + .size = 0, Indentation > + }, > + }, > + { > + .type = ATH10K_MEM_REGION_TYPE_IRAM2, > + .start = 0x00a00000, > + .len = 0x00040000, > + .name = "IRAM2", > + .section_table = { > + .sections = NULL, > + .size = 0, Indentation > + }, > + }, > + { > + .type = ATH10K_MEM_REGION_TYPE_REG, > + .start = 0x800, > + .len = 0x80020 - 0x800, > + .name = "REG_TOTAL", > + .section_table = { > + .sections = qca6174_hw30_sdio_register_sections, > + .size = ARRAY_SIZE(qca6174_hw30_sdio_register_sections), > + }, > + }, > +}; > + > static const struct ath10k_mem_region qca6174_hw30_mem_regions[] = { > { > .type = ATH10K_MEM_REGION_TYPE_DRAM, > @@ -995,12 +1319,22 @@ > { > .hw_id = QCA6174_HW_3_2_VERSION, > .hw_rev = ATH10K_HW_QCA6174, > + .bus = ATH10K_BUS_PCI, > .region_table = { > .regions = qca6174_hw30_mem_regions, > .size = ARRAY_SIZE(qca6174_hw30_mem_regions), > }, > }, > { > + .hw_id = QCA6174_HW_3_2_VERSION, > + .hw_rev = ATH10K_HW_QCA6174, > + .bus = ATH10K_BUS_SDIO, > + .region_table = { > + .regions = qca6174_hw30_sdio_mem_regions, > + .size = ARRAY_SIZE(qca6174_hw30_sdio_mem_regions), > + }, > + }, > + { > .hw_id = QCA9377_HW_1_1_DEV_VERSION, > .hw_rev = ATH10K_HW_QCA9377, > .region_table = { > @@ -1090,7 +1424,9 @@ const struct ath10k_hw_mem_layout *ath10k_coredump_get_mem_layout(struct ath10k > > for (i = 0; i < ARRAY_SIZE(hw_mem_layouts); i++) { > if (ar->target_version == hw_mem_layouts[i].hw_id && > - ar->hw_rev == hw_mem_layouts[i].hw_rev) > + ar->hw_rev == hw_mem_layouts[i].hw_rev && > + (hw_mem_layouts[i].bus == ATH10K_BUS_UNDEF || > + hw_mem_layouts[i].bus == ar->hif.bus)) > return &hw_mem_layouts[i]; > } > > diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h > index 09de419..b191746 100644 > --- a/drivers/net/wireless/ath/ath10k/coredump.h > +++ b/drivers/net/wireless/ath/ath10k/coredump.h > @@ -155,6 +155,7 @@ struct ath10k_mem_region { > struct ath10k_hw_mem_layout { > u32 hw_id; > u32 hw_rev; > + enum ath10k_bus bus; > > struct { > const struct ath10k_mem_region *regions; > diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h > index 2ae57c1..d2cfd29 100644 > --- a/drivers/net/wireless/ath/ath10k/hw.h > +++ b/drivers/net/wireless/ath/ath10k/hw.h > @@ -11,6 +11,7 @@ > #include "targaddrs.h" > > enum ath10k_bus { > + ATH10K_BUS_UNDEF, Maybe call this "_ANY", given that you use it to match any bus? > ATH10K_BUS_PCI, > ATH10K_BUS_AHB, > ATH10K_BUS_SDIO, > diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c > index 8ed4fbd..cb75463 100644 > --- a/drivers/net/wireless/ath/ath10k/sdio.c > +++ b/drivers/net/wireless/ath/ath10k/sdio.c > @@ -23,6 +23,9 @@ > #include "targaddrs.h" > #include "trace.h" > #include "sdio.h" > +#include "coredump.h" > + > +void ath10k_sdio_fw_crashed_dump(struct ath10k *ar); > > /* inlined helper functions */ > > @@ -860,8 +863,7 @@ static int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k *ar) > out: > mutex_unlock(&irq_data->mtx); > if (cpu_int_status & MBOX_CPU_STATUS_ENABLE_ASSERT_MASK) { > - ath10k_err(ar, "firmware crashed!\n"); > - queue_work(ar->workqueue, &ar->restart_work); > + ath10k_sdio_fw_crashed_dump(ar); > } > return ret; > } > @@ -1967,6 +1969,335 @@ static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend, > > #endif /* CONFIG_PM_SLEEP */ > > +static int ath10k_sdio_read_host_interest_value(struct ath10k *ar, > + u32 item_offset, > + u32 *val) > +{ > + u32 addr; > + int ret; > + > + addr = host_interest_item_address(item_offset); > + > + ret = ath10k_sdio_hif_diag_read32(ar, addr, val); > + > + if (ret) > + ath10k_warn(ar, "unable to read host interest offset %d value\n", > + item_offset); > + > + return ret; > +} > + > +static int ath10k_sdio_read_mem(struct ath10k *ar, u32 address, void *buf, > + u32 buf_len) > +{ > + u32 val; > + int i, ret; > + > + for (i = 0; i < buf_len; i += 4) { > + ret = ath10k_sdio_hif_diag_read32(ar, address + i, &val); > + if (ret) { > + ath10k_warn(ar, "unable to read mem %d value\n", address + i); > + break; > + } > + memcpy(buf + i, &val, 4); > + } > + > + return ret; > +} > + > +void ath10k_sdio_check_fw_reg(struct ath10k *ar, u32 *fast_dump) > +{ > + int ret = 0; > + u32 param; > + > + ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_option_flag2), ¶m); > + > + *fast_dump = ((param & HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW) > + == HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW); > + > + ath10k_err(ar, "check fw reg : %x\n", param); > +} > + > +static void ath10k_sdio_dump_registers(struct ath10k *ar, > + struct ath10k_fw_crash_data *crash_data, > + u32 fast_dump) > +{ > + u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {}; > + int i, ret; > + u32 reg_dump_area; > + > + ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_failure_state), > + ®_dump_area); > + if (ret) { > + ath10k_err(ar, "failed to read firmware dump area: %d\n", ret); > + return; > + } > + > + if (fast_dump) > + ret = ath10k_bmi_read_memory(ar, reg_dump_area, reg_dump_values, > + sizeof(reg_dump_values)); > + else > + ret = ath10k_sdio_read_mem(ar, reg_dump_area, reg_dump_values, > + sizeof(reg_dump_values)); > + > + if (ret) { > + ath10k_err(ar, "failed to read firmware dump value: %d\n", ret); > + return; > + } > + > + ath10k_err(ar, "firmware register dump:\n"); > + for (i = 0; i < ARRAY_SIZE(reg_dump_values); i += 4) > + ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n", > + i, > + reg_dump_values[i], > + reg_dump_values[i + 1], > + reg_dump_values[i + 2], > + reg_dump_values[i + 3]); > + > + if (!crash_data) > + return; > + > + for (i = 0; i < ARRAY_SIZE(reg_dump_values); i++) > + crash_data->registers[i] = __cpu_to_le32(reg_dump_values[i]); > +} > + > +static int ath10k_sdio_dump_memory_section(struct ath10k *ar, > + const struct ath10k_mem_region *mem_region, > + u8 *buf, size_t buf_len) > +{ > + const struct ath10k_mem_section *cur_section, *next_section; > + unsigned int count, section_size, skip_size; > + int ret, i, j; > + > + if (!mem_region || !buf) > + return 0; > + > + cur_section = &mem_region->section_table.sections[0]; > + > + if (mem_region->start > cur_section->start) { > + ath10k_warn(ar, "incorrect memdump region 0x%x with section start address 0x%x.\n", > + mem_region->start, cur_section->start); > + return 0; > + } > + > + skip_size = cur_section->start - mem_region->start; > + > + /* fill the gap between the first register section and register > + * start address > + */ > + for (i = 0; i < skip_size; i++) { > + *buf = ATH10K_MAGIC_NOT_COPIED; > + buf++; > + } > + > + count = 0; > + > + for (i = 0; cur_section; i++) { > + section_size = cur_section->end - cur_section->start; > + > + if (section_size <= 0) { > + ath10k_warn(ar, "incorrect ramdump format with start address 0x%x and stop address 0x%x\n", > + cur_section->start, > + cur_section->end); > + break; > + } > + > + if ((i + 1) == mem_region->section_table.size) { > + /* last section */ > + next_section = NULL; > + skip_size = 0; > + } else { > + next_section = cur_section + 1; > + > + if (cur_section->end > next_section->start) { > + ath10k_warn(ar, "next ramdump section 0x%x is smaller than current end address 0x%x\n", > + next_section->start, > + cur_section->end); > + break; > + } > + > + skip_size = next_section->start - cur_section->end; > + } > + > + if (buf_len < (skip_size + section_size)) { > + ath10k_warn(ar, "ramdump buffer is too small: %zu\n", buf_len); > + break; > + } > + > + buf_len -= skip_size + section_size; > + > + /* read section to dest memory */ > + ret = ath10k_sdio_read_mem(ar, cur_section->start, > + buf, section_size); > + if (ret) { > + ath10k_warn(ar, "failed to read ramdump from section 0x%x: %d\n", > + cur_section->start, ret); > + break; > + } > + > + buf += section_size; > + count += section_size; > + > + /* fill in the gap between this section and the next */ > + for (j = 0; j < skip_size; j++) { > + *buf = ATH10K_MAGIC_NOT_COPIED; > + buf++; > + } > + > + count += skip_size; > + > + if (!next_section) > + /* this was the last section */ > + break; > + > + cur_section = next_section; > + } > + > + return count; > +} > + > +/* if an error happened returns < 0, otherwise the length */ > +static int ath10k_sdio_dump_memory_generic(struct ath10k *ar, > + const struct ath10k_mem_region *current_region, > + u8 *buf, > + u32 fast_dump) > +{ > + int ret; > + > + if (current_region->section_table.size > 0) > + /* Copy each section individually. */ > + return ath10k_sdio_dump_memory_section(ar, > + current_region, > + buf, > + current_region->len); > + > + /* No individiual memory sections defined so we can > + * copy the entire memory region. > + */ > + if (fast_dump) > + ret = ath10k_bmi_read_memory(ar, > + current_region->start, > + buf, > + current_region->len); > + else > + ret = ath10k_sdio_read_mem(ar, > + current_region->start, > + buf, > + current_region->len); > + > + if (ret) { > + ath10k_warn(ar, "failed to copy ramdump region %s: %d\n", > + current_region->name, ret); > + return ret; > + } > + > + return current_region->len; > +} > + > +static void ath10k_sdio_dump_memory(struct ath10k *ar, > + struct ath10k_fw_crash_data *crash_data, > + u32 fast_dump) > +{ > + const struct ath10k_hw_mem_layout *mem_layout; > + const struct ath10k_mem_region *current_region; > + struct ath10k_dump_ram_data_hdr *hdr; > + u32 count; > + size_t buf_len; > + int ret, i; > + u8 *buf; > + > + if (!crash_data) > + return; > + > + mem_layout = ath10k_coredump_get_mem_layout(ar); > + if (!mem_layout) > + return; > + > + current_region = &mem_layout->region_table.regions[0]; > + > + buf = crash_data->ramdump_buf; > + buf_len = crash_data->ramdump_buf_len; > + > + memset(buf, 0, buf_len); > + > + for (i = 0; i < mem_layout->region_table.size; i++) { > + count = 0; > + > + if (current_region->len > buf_len) { > + ath10k_warn(ar, "memory region %s size %d is larger that remaining ramdump buffer size %zu\n", > + current_region->name, > + current_region->len, > + buf_len); > + break; > + } > + > + /* Reserve space for the header. */ > + hdr = (void *)buf; > + buf += sizeof(*hdr); > + buf_len -= sizeof(*hdr); > + > + ret = ath10k_sdio_dump_memory_generic(ar, current_region, buf, fast_dump); > + > + ath10k_err(ar, "dump mem, name:%s, type:%d, start:0x%x, len:0x%x, size:%d, ret:0x%x\n", > + current_region->name, > + current_region->type, > + current_region->start, > + current_region->len, > + current_region->section_table.size, > + ret); > + > + if (ret >= 0) > + count = ret; > + > + hdr->region_type = cpu_to_le32(current_region->type); > + hdr->start = cpu_to_le32(current_region->start); > + hdr->length = cpu_to_le32(count); > + > + if (count == 0) > + /* Note: the header remains, just with zero length. */ > + break; > + > + buf += count; > + buf_len -= count; > + > + current_region++; > + } > +} > + > +void ath10k_sdio_fw_crashed_dump(struct ath10k *ar) > +{ > + struct ath10k_fw_crash_data *crash_data; > + char guid[UUID_STRING_LEN + 1]; > + u32 fast_dump = 0; > + > + ath10k_err(ar, "begin fw dump\n", guid); > + > + ath10k_sdio_check_fw_reg(ar, &fast_dump); > + > + if (fast_dump) > + ar->bmi.done_sent = false; > + > + ar->stats.fw_crash_counter++; > + > + ath10k_sdio_hif_disable_intrs(ar); > + > + crash_data = ath10k_coredump_new(ar); > + > + if (crash_data) > + scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid); > + else > + scnprintf(guid, sizeof(guid), "n/a"); > + > + ath10k_err(ar, "firmware crashed! (guid %s)\n", guid); > + ath10k_print_driver_info(ar); > + ath10k_sdio_dump_registers(ar, crash_data, fast_dump); > + ath10k_sdio_dump_memory(ar, crash_data, fast_dump); > + > + ath10k_sdio_hif_enable_intrs(ar); > + > + queue_work(ar->workqueue, &ar->restart_work); > +} > + > static int ath10k_sdio_probe(struct sdio_func *func, > const struct sdio_device_id *id) > { > diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h > index dff6c8a..c65045a 100644 > --- a/drivers/net/wireless/ath/ath10k/targaddrs.h > +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h > @@ -334,6 +334,16 @@ struct host_interest { > #define HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK (1 << 17) > > /* > + * If both SDIO_CRASH_DUMP_ENHANCEMENT_HOST and SDIO_CRASH_DUMP_ENHANCEMENT_FW > + * flags are set, then crashdump upload will be done using the BMI host/target > + * communication channel. > + */ > +/* HOST to support using BMI dump FW memory when hit assert */ > +#define HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_HOST 0x400 > +/* FW to support using BMI dump FW memory when hit assert */ > +#define HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW 0x800 > + > +/* > * CONSOLE FLAGS > * > * Bit Range Meaning > -- > 1.9.1 >