Loading drivers/net/wireless/ath/ath10k/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o ath10k_core-$(CONFIG_THERMAL) += thermal.o ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o ath10k_core-$(CONFIG_PM) += wow.o ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o ath10k_pci-y += pci.o \ Loading drivers/net/wireless/ath/ath10k/core.c +117 −77 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include "htt.h" #include "testmode.h" #include "wmi-ops.h" #include "coredump.h" unsigned int ath10k_debug_mask; static unsigned int ath10k_cryptmode_param; Loading Loading @@ -471,6 +472,7 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast", [ATH10K_FW_FEATURE_NO_PS] = "no-ps", [ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference", [ATH10K_FW_FEATURE_NON_BMI] = "non-bmi", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, Loading Loading @@ -1550,8 +1552,8 @@ int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name, data += ie_len; } if (!fw_file->firmware_data || !fw_file->firmware_len) { if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, fw_file->fw_features) && (!fw_file->firmware_data || !fw_file->firmware_len)) { ath10k_warn(ar, "No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n", ar->hw_params.fw.dir, name); ret = -ENOMEDIUM; Loading @@ -1577,6 +1579,7 @@ static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name, break; case ATH10K_BUS_PCI: case ATH10K_BUS_AHB: case ATH10K_BUS_SNOC: scnprintf(fw_name, fw_name_len, "%s-%d.bin", ATH10K_FW_FILE_BASE, fw_api); break; Loading Loading @@ -1862,7 +1865,7 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); ret = ath10k_debug_fw_devcoredump(ar); ret = ath10k_coredump_submit(ar); if (ret) ath10k_warn(ar, "failed to send firmware crash dump via devcoredump: %d", ret); Loading Loading @@ -2104,6 +2107,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, ar->running_fw = fw; if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->running_fw->fw_file.fw_features)) { ath10k_bmi_start(ar); if (ath10k_init_configure_target(ar)) { Loading @@ -2118,8 +2123,9 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, /* Some of of qca988x solutions are having global reset issue * during target initialization. Bypassing PLL setting before * downloading firmware and letting the SoC run on REF_CLK is * fixing the problem. Corresponding firmware change is also needed * to set the clock source once the target is initialized. * fixing the problem. Corresponding firmware change is also * needed to set the clock source once the target is * initialized. */ if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT, ar->running_fw->fw_file.fw_features)) { Loading @@ -2141,6 +2147,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, if (ar->hif.bus == ATH10K_BUS_SDIO) ath10k_init_sdio(ar); } ar->htc.htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; Loading @@ -2151,9 +2158,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err; } if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->running_fw->fw_file.fw_features)) { status = ath10k_bmi_done(ar); if (status) goto err; } status = ath10k_wmi_attach(ar); if (status) { Loading Loading @@ -2396,18 +2406,33 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return ret; } switch (ar->hif.bus) { case ATH10K_BUS_SDIO: memset(&target_info, 0, sizeof(target_info)); if (ar->hif.bus == ATH10K_BUS_SDIO) ret = ath10k_bmi_get_target_info_sdio(ar, &target_info); else if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down; } ar->target_version = target_info.version; ar->hw->wiphy->hw_version = target_info.version; break; case ATH10K_BUS_PCI: case ATH10K_BUS_AHB: memset(&target_info, 0, sizeof(target_info)); ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down; } ar->target_version = target_info.version; ar->hw->wiphy->hw_version = target_info.version; break; case ATH10K_BUS_SNOC: break; default: ath10k_err(ar, "incorrect hif bus type: %d\n", ar->hif.bus); } ret = ath10k_init_hw_params(ar); if (ret) { Loading @@ -2428,6 +2453,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ath10k_debug_print_hwfw_info(ar); if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->normal_mode_fw.fw_file.fw_features)) { ret = ath10k_core_pre_cal_download(ar); if (ret) { /* pre calibration data download is not necessary Loading Loading @@ -2459,6 +2486,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) } ath10k_debug_print_board_info(ar); } ret = ath10k_core_init_firmware_features(ar); if (ret) { Loading @@ -2467,12 +2495,16 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_free_firmware_files; } ret = ath10k_swap_code_seg_init(ar, &ar->normal_mode_fw.fw_file); if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->normal_mode_fw.fw_file.fw_features)) { ret = ath10k_swap_code_seg_init(ar, &ar->normal_mode_fw.fw_file); if (ret) { ath10k_err(ar, "failed to initialize code swap segment: %d\n", ret); goto err_free_firmware_files; } } mutex_lock(&ar->conf_mutex); Loading Loading @@ -2703,12 +2735,19 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_dummy_netdev(&ar->napi_dev); ret = ath10k_debug_create(ar); ret = ath10k_coredump_create(ar); if (ret) goto err_free_aux_wq; ret = ath10k_debug_create(ar); if (ret) goto err_free_coredump; return ar; err_free_coredump: ath10k_coredump_destroy(ar); err_free_aux_wq: destroy_workqueue(ar->workqueue_aux); err_free_wq: Loading @@ -2730,6 +2769,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_aux); ath10k_debug_destroy(ar); ath10k_coredump_destroy(ar); ath10k_htt_tx_destroy(&ar->htt); ath10k_wmi_free_host_mem(ar); ath10k_mac_destroy(ar); Loading drivers/net/wireless/ath/ath10k/core.h +13 −4 Original line number Diff line number Diff line Loading @@ -92,6 +92,7 @@ enum ath10k_bus { ATH10K_BUS_AHB, ATH10K_BUS_SDIO, ATH10K_BUS_USB, ATH10K_BUS_SNOC, }; static inline const char *ath10k_bus_str(enum ath10k_bus bus) Loading @@ -105,6 +106,8 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) return "sdio"; case ATH10K_BUS_USB: return "usb"; case ATH10K_BUS_SNOC: return "snoc"; } return "unknown"; Loading Loading @@ -459,8 +462,6 @@ struct ath10k_ce_crash_hdr { /* used for crash-dump storage, protected by data-lock */ struct ath10k_fw_crash_data { bool crashed_since_read; guid_t guid; struct timespec64 timestamp; __le32 registers[REG_DUMP_COUNT_QCA988X]; Loading Loading @@ -490,8 +491,6 @@ struct ath10k_debug { u32 reg_addr; u32 nf_cal_period; void *cal_data; struct ath10k_fw_crash_data *fw_crash_data; }; enum ath10k_state { Loading Loading @@ -616,6 +615,9 @@ enum ath10k_fw_features { /* Firmware allows management tx by reference instead of by value. */ ATH10K_FW_FEATURE_MGMT_TX_BY_REF = 18, /* Firmware load is done externally, not by bmi */ ATH10K_FW_FEATURE_NON_BMI = 19, /* keep last */ ATH10K_FW_FEATURE_COUNT, }; Loading Loading @@ -965,6 +967,13 @@ struct ath10k { #endif u32 pktlog_filter; #ifdef CONFIG_DEV_COREDUMP struct { struct ath10k_fw_crash_data *fw_crash_data; } coredump; #endif struct { /* protected by conf_mutex */ struct ath10k_fw_components utf_mode_fw; Loading drivers/net/wireless/ath/ath10k/coredump.c 0 → 100644 +148 −0 Original line number Diff line number Diff line /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "coredump.h" #include <linux/devcoredump.h> #include <linux/utsname.h> #include "debug.h" struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar) { struct ath10k_fw_crash_data *crash_data = ar->coredump.fw_crash_data; lockdep_assert_held(&ar->data_lock); guid_gen(&crash_data->guid); ktime_get_real_ts64(&crash_data->timestamp); return crash_data; } EXPORT_SYMBOL(ath10k_coredump_new); static struct ath10k_dump_file_data *ath10k_coredump_build(struct ath10k *ar) { struct ath10k_fw_crash_data *crash_data = ar->coredump.fw_crash_data; struct ath10k_ce_crash_hdr *ce_hdr; struct ath10k_dump_file_data *dump_data; struct ath10k_tlv_dump_data *dump_tlv; size_t hdr_len = sizeof(*dump_data); size_t len, sofar = 0; unsigned char *buf; len = hdr_len; len += sizeof(*dump_tlv) + sizeof(crash_data->registers); len += sizeof(*dump_tlv) + sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0]); sofar += hdr_len; /* This is going to get big when we start dumping FW RAM and such, * so go ahead and use vmalloc. */ buf = vzalloc(len); if (!buf) return NULL; spin_lock_bh(&ar->data_lock); dump_data = (struct ath10k_dump_file_data *)(buf); strlcpy(dump_data->df_magic, "ATH10K-FW-DUMP", sizeof(dump_data->df_magic)); dump_data->len = cpu_to_le32(len); dump_data->version = cpu_to_le32(ATH10K_FW_CRASH_DUMP_VERSION); guid_copy(&dump_data->guid, &crash_data->guid); dump_data->chip_id = cpu_to_le32(ar->chip_id); dump_data->bus_type = cpu_to_le32(0); dump_data->target_version = cpu_to_le32(ar->target_version); dump_data->fw_version_major = cpu_to_le32(ar->fw_version_major); dump_data->fw_version_minor = cpu_to_le32(ar->fw_version_minor); dump_data->fw_version_release = cpu_to_le32(ar->fw_version_release); dump_data->fw_version_build = cpu_to_le32(ar->fw_version_build); dump_data->phy_capability = cpu_to_le32(ar->phy_capability); dump_data->hw_min_tx_power = cpu_to_le32(ar->hw_min_tx_power); dump_data->hw_max_tx_power = cpu_to_le32(ar->hw_max_tx_power); dump_data->ht_cap_info = cpu_to_le32(ar->ht_cap_info); dump_data->vht_cap_info = cpu_to_le32(ar->vht_cap_info); dump_data->num_rf_chains = cpu_to_le32(ar->num_rf_chains); strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version, sizeof(dump_data->fw_ver)); dump_data->kernel_ver_code = 0; strlcpy(dump_data->kernel_ver, init_utsname()->release, sizeof(dump_data->kernel_ver)); dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec); dump_data->tv_nsec = cpu_to_le64(crash_data->timestamp.tv_nsec); /* Gather crash-dump */ dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar); dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_REGISTERS); dump_tlv->tlv_len = cpu_to_le32(sizeof(crash_data->registers)); memcpy(dump_tlv->tlv_data, &crash_data->registers, sizeof(crash_data->registers)); sofar += sizeof(*dump_tlv) + sizeof(crash_data->registers); dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar); dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_CE_DATA); dump_tlv->tlv_len = cpu_to_le32(sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0])); ce_hdr = (struct ath10k_ce_crash_hdr *)(dump_tlv->tlv_data); ce_hdr->ce_count = cpu_to_le32(CE_COUNT); memset(ce_hdr->reserved, 0, sizeof(ce_hdr->reserved)); memcpy(ce_hdr->entries, crash_data->ce_crash_data, CE_COUNT * sizeof(ce_hdr->entries[0])); sofar += sizeof(*dump_tlv) + sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0]); spin_unlock_bh(&ar->data_lock); return dump_data; } int ath10k_coredump_submit(struct ath10k *ar) { struct ath10k_dump_file_data *dump; dump = ath10k_coredump_build(ar); if (!dump) { ath10k_warn(ar, "no crash dump data found for devcoredump"); return -ENODATA; } dev_coredumpv(ar->dev, dump, le32_to_cpu(dump->len), GFP_KERNEL); return 0; } int ath10k_coredump_create(struct ath10k *ar) { ar->coredump.fw_crash_data = vzalloc(sizeof(*ar->coredump.fw_crash_data)); if (!ar->coredump.fw_crash_data) return -ENOMEM; return 0; } void ath10k_coredump_destroy(struct ath10k *ar) { vfree(ar->coredump.fw_crash_data); ar->coredump.fw_crash_data = NULL; } drivers/net/wireless/ath/ath10k/coredump.h 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _COREDUMP_H_ #define _COREDUMP_H_ #include "core.h" #define ATH10K_FW_CRASH_DUMP_VERSION 1 /** * enum ath10k_fw_crash_dump_type - types of data in the dump file * @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format */ enum ath10k_fw_crash_dump_type { ATH10K_FW_CRASH_DUMP_REGISTERS = 0, ATH10K_FW_CRASH_DUMP_CE_DATA = 1, ATH10K_FW_CRASH_DUMP_MAX, }; struct ath10k_tlv_dump_data { /* see ath10k_fw_crash_dump_type above */ __le32 type; /* in bytes */ __le32 tlv_len; /* pad to 32-bit boundaries as needed */ u8 tlv_data[]; } __packed; struct ath10k_dump_file_data { /* dump file information */ /* "ATH10K-FW-DUMP" */ char df_magic[16]; __le32 len; /* file dump version */ __le32 version; /* some info we can get from ath10k struct that might help */ guid_t guid; __le32 chip_id; /* 0 for now, in place for later hardware */ __le32 bus_type; __le32 target_version; __le32 fw_version_major; __le32 fw_version_minor; __le32 fw_version_release; __le32 fw_version_build; __le32 phy_capability; __le32 hw_min_tx_power; __le32 hw_max_tx_power; __le32 ht_cap_info; __le32 vht_cap_info; __le32 num_rf_chains; /* firmware version string */ char fw_ver[ETHTOOL_FWVERS_LEN]; /* Kernel related information */ /* time-of-day stamp */ __le64 tv_sec; /* time-of-day stamp, nano-seconds */ __le64 tv_nsec; /* LINUX_VERSION_CODE */ __le32 kernel_ver_code; /* VERMAGIC_STRING */ char kernel_ver[64]; /* room for growth w/out changing binary format */ u8 unused[128]; /* struct ath10k_tlv_dump_data + more */ u8 data[0]; } __packed; #ifdef CONFIG_DEV_COREDUMP int ath10k_coredump_submit(struct ath10k *ar); struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar); int ath10k_coredump_create(struct ath10k *ar); void ath10k_coredump_destroy(struct ath10k *ar); #else /* CONFIG_DEV_COREDUMP */ static inline int ath10k_coredump_submit(struct ath10k *ar) { return 0; } static inline struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar) { return NULL; } static inline int ath10k_coredump_create(struct ath10k *ar) { return 0; } static inline void ath10k_coredump_destroy(struct ath10k *ar) { } #endif /* CONFIG_DEV_COREDUMP */ #endif /* _COREDUMP_H_ */ Loading
drivers/net/wireless/ath/ath10k/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o ath10k_core-$(CONFIG_THERMAL) += thermal.o ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o ath10k_core-$(CONFIG_PM) += wow.o ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o ath10k_pci-y += pci.o \ Loading
drivers/net/wireless/ath/ath10k/core.c +117 −77 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include "htt.h" #include "testmode.h" #include "wmi-ops.h" #include "coredump.h" unsigned int ath10k_debug_mask; static unsigned int ath10k_cryptmode_param; Loading Loading @@ -471,6 +472,7 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast", [ATH10K_FW_FEATURE_NO_PS] = "no-ps", [ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference", [ATH10K_FW_FEATURE_NON_BMI] = "non-bmi", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, Loading Loading @@ -1550,8 +1552,8 @@ int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name, data += ie_len; } if (!fw_file->firmware_data || !fw_file->firmware_len) { if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, fw_file->fw_features) && (!fw_file->firmware_data || !fw_file->firmware_len)) { ath10k_warn(ar, "No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n", ar->hw_params.fw.dir, name); ret = -ENOMEDIUM; Loading @@ -1577,6 +1579,7 @@ static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name, break; case ATH10K_BUS_PCI: case ATH10K_BUS_AHB: case ATH10K_BUS_SNOC: scnprintf(fw_name, fw_name_len, "%s-%d.bin", ATH10K_FW_FILE_BASE, fw_api); break; Loading Loading @@ -1862,7 +1865,7 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); ret = ath10k_debug_fw_devcoredump(ar); ret = ath10k_coredump_submit(ar); if (ret) ath10k_warn(ar, "failed to send firmware crash dump via devcoredump: %d", ret); Loading Loading @@ -2104,6 +2107,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, ar->running_fw = fw; if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->running_fw->fw_file.fw_features)) { ath10k_bmi_start(ar); if (ath10k_init_configure_target(ar)) { Loading @@ -2118,8 +2123,9 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, /* Some of of qca988x solutions are having global reset issue * during target initialization. Bypassing PLL setting before * downloading firmware and letting the SoC run on REF_CLK is * fixing the problem. Corresponding firmware change is also needed * to set the clock source once the target is initialized. * fixing the problem. Corresponding firmware change is also * needed to set the clock source once the target is * initialized. */ if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT, ar->running_fw->fw_file.fw_features)) { Loading @@ -2141,6 +2147,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, if (ar->hif.bus == ATH10K_BUS_SDIO) ath10k_init_sdio(ar); } ar->htc.htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; Loading @@ -2151,9 +2158,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, goto err; } if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->running_fw->fw_file.fw_features)) { status = ath10k_bmi_done(ar); if (status) goto err; } status = ath10k_wmi_attach(ar); if (status) { Loading Loading @@ -2396,18 +2406,33 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return ret; } switch (ar->hif.bus) { case ATH10K_BUS_SDIO: memset(&target_info, 0, sizeof(target_info)); if (ar->hif.bus == ATH10K_BUS_SDIO) ret = ath10k_bmi_get_target_info_sdio(ar, &target_info); else if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down; } ar->target_version = target_info.version; ar->hw->wiphy->hw_version = target_info.version; break; case ATH10K_BUS_PCI: case ATH10K_BUS_AHB: memset(&target_info, 0, sizeof(target_info)); ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down; } ar->target_version = target_info.version; ar->hw->wiphy->hw_version = target_info.version; break; case ATH10K_BUS_SNOC: break; default: ath10k_err(ar, "incorrect hif bus type: %d\n", ar->hif.bus); } ret = ath10k_init_hw_params(ar); if (ret) { Loading @@ -2428,6 +2453,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ath10k_debug_print_hwfw_info(ar); if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->normal_mode_fw.fw_file.fw_features)) { ret = ath10k_core_pre_cal_download(ar); if (ret) { /* pre calibration data download is not necessary Loading Loading @@ -2459,6 +2486,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) } ath10k_debug_print_board_info(ar); } ret = ath10k_core_init_firmware_features(ar); if (ret) { Loading @@ -2467,12 +2495,16 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_free_firmware_files; } ret = ath10k_swap_code_seg_init(ar, &ar->normal_mode_fw.fw_file); if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, ar->normal_mode_fw.fw_file.fw_features)) { ret = ath10k_swap_code_seg_init(ar, &ar->normal_mode_fw.fw_file); if (ret) { ath10k_err(ar, "failed to initialize code swap segment: %d\n", ret); goto err_free_firmware_files; } } mutex_lock(&ar->conf_mutex); Loading Loading @@ -2703,12 +2735,19 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_dummy_netdev(&ar->napi_dev); ret = ath10k_debug_create(ar); ret = ath10k_coredump_create(ar); if (ret) goto err_free_aux_wq; ret = ath10k_debug_create(ar); if (ret) goto err_free_coredump; return ar; err_free_coredump: ath10k_coredump_destroy(ar); err_free_aux_wq: destroy_workqueue(ar->workqueue_aux); err_free_wq: Loading @@ -2730,6 +2769,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_aux); ath10k_debug_destroy(ar); ath10k_coredump_destroy(ar); ath10k_htt_tx_destroy(&ar->htt); ath10k_wmi_free_host_mem(ar); ath10k_mac_destroy(ar); Loading
drivers/net/wireless/ath/ath10k/core.h +13 −4 Original line number Diff line number Diff line Loading @@ -92,6 +92,7 @@ enum ath10k_bus { ATH10K_BUS_AHB, ATH10K_BUS_SDIO, ATH10K_BUS_USB, ATH10K_BUS_SNOC, }; static inline const char *ath10k_bus_str(enum ath10k_bus bus) Loading @@ -105,6 +106,8 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) return "sdio"; case ATH10K_BUS_USB: return "usb"; case ATH10K_BUS_SNOC: return "snoc"; } return "unknown"; Loading Loading @@ -459,8 +462,6 @@ struct ath10k_ce_crash_hdr { /* used for crash-dump storage, protected by data-lock */ struct ath10k_fw_crash_data { bool crashed_since_read; guid_t guid; struct timespec64 timestamp; __le32 registers[REG_DUMP_COUNT_QCA988X]; Loading Loading @@ -490,8 +491,6 @@ struct ath10k_debug { u32 reg_addr; u32 nf_cal_period; void *cal_data; struct ath10k_fw_crash_data *fw_crash_data; }; enum ath10k_state { Loading Loading @@ -616,6 +615,9 @@ enum ath10k_fw_features { /* Firmware allows management tx by reference instead of by value. */ ATH10K_FW_FEATURE_MGMT_TX_BY_REF = 18, /* Firmware load is done externally, not by bmi */ ATH10K_FW_FEATURE_NON_BMI = 19, /* keep last */ ATH10K_FW_FEATURE_COUNT, }; Loading Loading @@ -965,6 +967,13 @@ struct ath10k { #endif u32 pktlog_filter; #ifdef CONFIG_DEV_COREDUMP struct { struct ath10k_fw_crash_data *fw_crash_data; } coredump; #endif struct { /* protected by conf_mutex */ struct ath10k_fw_components utf_mode_fw; Loading
drivers/net/wireless/ath/ath10k/coredump.c 0 → 100644 +148 −0 Original line number Diff line number Diff line /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "coredump.h" #include <linux/devcoredump.h> #include <linux/utsname.h> #include "debug.h" struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar) { struct ath10k_fw_crash_data *crash_data = ar->coredump.fw_crash_data; lockdep_assert_held(&ar->data_lock); guid_gen(&crash_data->guid); ktime_get_real_ts64(&crash_data->timestamp); return crash_data; } EXPORT_SYMBOL(ath10k_coredump_new); static struct ath10k_dump_file_data *ath10k_coredump_build(struct ath10k *ar) { struct ath10k_fw_crash_data *crash_data = ar->coredump.fw_crash_data; struct ath10k_ce_crash_hdr *ce_hdr; struct ath10k_dump_file_data *dump_data; struct ath10k_tlv_dump_data *dump_tlv; size_t hdr_len = sizeof(*dump_data); size_t len, sofar = 0; unsigned char *buf; len = hdr_len; len += sizeof(*dump_tlv) + sizeof(crash_data->registers); len += sizeof(*dump_tlv) + sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0]); sofar += hdr_len; /* This is going to get big when we start dumping FW RAM and such, * so go ahead and use vmalloc. */ buf = vzalloc(len); if (!buf) return NULL; spin_lock_bh(&ar->data_lock); dump_data = (struct ath10k_dump_file_data *)(buf); strlcpy(dump_data->df_magic, "ATH10K-FW-DUMP", sizeof(dump_data->df_magic)); dump_data->len = cpu_to_le32(len); dump_data->version = cpu_to_le32(ATH10K_FW_CRASH_DUMP_VERSION); guid_copy(&dump_data->guid, &crash_data->guid); dump_data->chip_id = cpu_to_le32(ar->chip_id); dump_data->bus_type = cpu_to_le32(0); dump_data->target_version = cpu_to_le32(ar->target_version); dump_data->fw_version_major = cpu_to_le32(ar->fw_version_major); dump_data->fw_version_minor = cpu_to_le32(ar->fw_version_minor); dump_data->fw_version_release = cpu_to_le32(ar->fw_version_release); dump_data->fw_version_build = cpu_to_le32(ar->fw_version_build); dump_data->phy_capability = cpu_to_le32(ar->phy_capability); dump_data->hw_min_tx_power = cpu_to_le32(ar->hw_min_tx_power); dump_data->hw_max_tx_power = cpu_to_le32(ar->hw_max_tx_power); dump_data->ht_cap_info = cpu_to_le32(ar->ht_cap_info); dump_data->vht_cap_info = cpu_to_le32(ar->vht_cap_info); dump_data->num_rf_chains = cpu_to_le32(ar->num_rf_chains); strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version, sizeof(dump_data->fw_ver)); dump_data->kernel_ver_code = 0; strlcpy(dump_data->kernel_ver, init_utsname()->release, sizeof(dump_data->kernel_ver)); dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec); dump_data->tv_nsec = cpu_to_le64(crash_data->timestamp.tv_nsec); /* Gather crash-dump */ dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar); dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_REGISTERS); dump_tlv->tlv_len = cpu_to_le32(sizeof(crash_data->registers)); memcpy(dump_tlv->tlv_data, &crash_data->registers, sizeof(crash_data->registers)); sofar += sizeof(*dump_tlv) + sizeof(crash_data->registers); dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar); dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_CE_DATA); dump_tlv->tlv_len = cpu_to_le32(sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0])); ce_hdr = (struct ath10k_ce_crash_hdr *)(dump_tlv->tlv_data); ce_hdr->ce_count = cpu_to_le32(CE_COUNT); memset(ce_hdr->reserved, 0, sizeof(ce_hdr->reserved)); memcpy(ce_hdr->entries, crash_data->ce_crash_data, CE_COUNT * sizeof(ce_hdr->entries[0])); sofar += sizeof(*dump_tlv) + sizeof(*ce_hdr) + CE_COUNT * sizeof(ce_hdr->entries[0]); spin_unlock_bh(&ar->data_lock); return dump_data; } int ath10k_coredump_submit(struct ath10k *ar) { struct ath10k_dump_file_data *dump; dump = ath10k_coredump_build(ar); if (!dump) { ath10k_warn(ar, "no crash dump data found for devcoredump"); return -ENODATA; } dev_coredumpv(ar->dev, dump, le32_to_cpu(dump->len), GFP_KERNEL); return 0; } int ath10k_coredump_create(struct ath10k *ar) { ar->coredump.fw_crash_data = vzalloc(sizeof(*ar->coredump.fw_crash_data)); if (!ar->coredump.fw_crash_data) return -ENOMEM; return 0; } void ath10k_coredump_destroy(struct ath10k *ar) { vfree(ar->coredump.fw_crash_data); ar->coredump.fw_crash_data = NULL; }
drivers/net/wireless/ath/ath10k/coredump.h 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _COREDUMP_H_ #define _COREDUMP_H_ #include "core.h" #define ATH10K_FW_CRASH_DUMP_VERSION 1 /** * enum ath10k_fw_crash_dump_type - types of data in the dump file * @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format */ enum ath10k_fw_crash_dump_type { ATH10K_FW_CRASH_DUMP_REGISTERS = 0, ATH10K_FW_CRASH_DUMP_CE_DATA = 1, ATH10K_FW_CRASH_DUMP_MAX, }; struct ath10k_tlv_dump_data { /* see ath10k_fw_crash_dump_type above */ __le32 type; /* in bytes */ __le32 tlv_len; /* pad to 32-bit boundaries as needed */ u8 tlv_data[]; } __packed; struct ath10k_dump_file_data { /* dump file information */ /* "ATH10K-FW-DUMP" */ char df_magic[16]; __le32 len; /* file dump version */ __le32 version; /* some info we can get from ath10k struct that might help */ guid_t guid; __le32 chip_id; /* 0 for now, in place for later hardware */ __le32 bus_type; __le32 target_version; __le32 fw_version_major; __le32 fw_version_minor; __le32 fw_version_release; __le32 fw_version_build; __le32 phy_capability; __le32 hw_min_tx_power; __le32 hw_max_tx_power; __le32 ht_cap_info; __le32 vht_cap_info; __le32 num_rf_chains; /* firmware version string */ char fw_ver[ETHTOOL_FWVERS_LEN]; /* Kernel related information */ /* time-of-day stamp */ __le64 tv_sec; /* time-of-day stamp, nano-seconds */ __le64 tv_nsec; /* LINUX_VERSION_CODE */ __le32 kernel_ver_code; /* VERMAGIC_STRING */ char kernel_ver[64]; /* room for growth w/out changing binary format */ u8 unused[128]; /* struct ath10k_tlv_dump_data + more */ u8 data[0]; } __packed; #ifdef CONFIG_DEV_COREDUMP int ath10k_coredump_submit(struct ath10k *ar); struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar); int ath10k_coredump_create(struct ath10k *ar); void ath10k_coredump_destroy(struct ath10k *ar); #else /* CONFIG_DEV_COREDUMP */ static inline int ath10k_coredump_submit(struct ath10k *ar) { return 0; } static inline struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar) { return NULL; } static inline int ath10k_coredump_create(struct ath10k *ar) { return 0; } static inline void ath10k_coredump_destroy(struct ath10k *ar) { } #endif /* CONFIG_DEV_COREDUMP */ #endif /* _COREDUMP_H_ */