diff options
| author | Yue Ma <yuem@qca.qualcomm.com> | 2013-11-06 10:56:45 -0800 |
|---|---|---|
| committer | Prakash Dhavali <pdhavali@qca.qualcomm.com> | 2013-12-03 14:39:34 -0800 |
| commit | 7703f7c7fa0207f1120f5864cb974e746e3dec78 (patch) | |
| tree | d31c6f8e12c6a878c8e6892309c70f0f51942381 | |
| parent | 3752c6f0d68ab75425e8d216e7442294ff05c17e (diff) | |
wlan: Add SSR support to CLD host driver
This change is for chip level reset (SSR phase 1). When target crashes,
host driver will collect the RAM dump by dumping them to a dedicated
memory location.
Change-Id: I458b50913057ee80158d82d228396b27c5b7e7db
CRs-Fixed: 562329
| -rw-r--r-- | CORE/HDD/inc/wlan_hdd_main.h | 9 | ||||
| -rw-r--r-- | CORE/HDD/src/wlan_hdd_main.c | 122 | ||||
| -rw-r--r-- | CORE/HDD/src/wlan_hdd_wext.c | 19 | ||||
| -rw-r--r-- | CORE/SERVICES/BMI/ol_fw.c | 156 | ||||
| -rw-r--r-- | CORE/SERVICES/BMI/ol_fw.h | 19 | ||||
| -rw-r--r-- | CORE/SERVICES/COMMON/wma_api.h | 1 | ||||
| -rw-r--r-- | CORE/SERVICES/WMA/wma.c | 33 |
7 files changed, 359 insertions, 0 deletions
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h index 3e8cc9f2caf6..74d8f1a25292 100644 --- a/CORE/HDD/inc/wlan_hdd_main.h +++ b/CORE/HDD/inc/wlan_hdd_main.h @@ -61,6 +61,11 @@ #ifdef FEATURE_WLAN_TDLS #include "wlan_hdd_tdls.h" #endif +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +#include <linux/device.h> +#include <mach/ramdump.h> +#include <mach/subsystem_restart.h> +#endif /*--------------------------------------------------------------------------- Preprocessor definitions and constants -------------------------------------------------------------------------*/ @@ -1070,4 +1075,8 @@ VOS_STATUS wlan_hdd_restart_driver(hdd_context_t *pHddCtx); void hdd_exchange_version_and_caps(hdd_context_t *pHddCtx); void hdd_set_pwrparams(hdd_context_t *pHddCtx); void hdd_reset_pwrparams(hdd_context_t *pHddCtx); +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +int wlan_ssr_init(struct device* device); +void wlan_ssr_deinit(void); +#endif /* #if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) */ #endif // end #if !defined( WLAN_HDD_MAIN_H ) diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c index 2909dd5663b6..b3308e8a75c3 100644 --- a/CORE/HDD/src/wlan_hdd_main.c +++ b/CORE/HDD/src/wlan_hdd_main.c @@ -109,6 +109,11 @@ int wlan_hdd_ftm_start(hdd_context_t *pAdapter); #if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) #include "if_pci.h" +#include <linux/err.h> +#include <linux/kernel.h> +#include <net/cnss.h> + +struct dev_info *gDevInfo; #endif #ifdef MODULE @@ -5579,6 +5584,16 @@ int hdd_wlan_startup(struct device *dev, v_VOID_t *hif_sc) return -EIO; } +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) + if (wlan_ssr_init(dev)) + { + hddLog(VOS_TRACE_LEVEL_ERROR, "%s: SSR initialization failed!", __func__); + + wiphy_free(wiphy); + return -EIO; + } +#endif + pHddCtx = wiphy_priv(wiphy); //Initialize the adapter context to zeros. @@ -6477,6 +6492,9 @@ static void hdd_driver_exit(void) #else hif_unregister_driver(); +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) + wlan_ssr_deinit(); +#endif /* * ADF context cannot be freed in hdd_wlan_exit for discrete * as it is needed in PCI remove. So free it here. @@ -7022,6 +7040,110 @@ VOS_STATUS wlan_hdd_restart_driver(hdd_context_t *pHddCtx) return status; } +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +/**--------------------------------------------------------------------------- + * \brief wlan_discrete_powerup + * + * Function invoked by the SSR framework to power on the target + * + * \param - None + * + * \return - Success or Failure + * --------------------------------------------------------------------------*/ +static int wlan_discrete_powerup(void) +{ + /* TODO for SSR3*/ + return TRUE; +} + +/**--------------------------------------------------------------------------- + * \brief wlan_discrete_shutdown + * + * Function invoked by the SSR framework to shutdown the target + * + * \param - None + * + * \return - Success or Failure + * --------------------------------------------------------------------------*/ +static int wlan_discrete_shutdown(void) +{ + /* TODO for SSR3*/ + return TRUE; +} + +/**--------------------------------------------------------------------------- + * \brief wlan_discrete_crash_shutdown + * + * Function invoked by the SSR framework to crash shutdown the target + * + * \param - None + * + * \return - None + * --------------------------------------------------------------------------*/ +static void wlan_discrete_crash_shutdown(void) +{ + /* Do nothing for SSR1 */ + return; +} + +/**--------------------------------------------------------------------------- + * \brief wlan_ssr_init + * + * Function linking the platform driver to the host driver + * + * \param - struct device *dev + * + * \return - Success or Failure + * --------------------------------------------------------------------------*/ +int wlan_ssr_init(struct device *dev) +{ + int result = -EEXIST; + + if (!gDevInfo) { + /* Attempt to allocate the block of memory for *gDevInfo */ + gDevInfo = (struct dev_info*)kzalloc(sizeof(*gDevInfo), GFP_KERNEL); + if (gDevInfo) { + gDevInfo->dev = dev; + gDevInfo->dev_shutdown = wlan_discrete_shutdown; + gDevInfo->dev_powerup = wlan_discrete_powerup; + gDevInfo->dev_crashshutdown = wlan_discrete_crash_shutdown; + + result = cnss_config(gDevInfo); + if (result) { + kfree(gDevInfo); + gDevInfo = NULL; + } + } else { + result = -ENOMEM; + } + } + + return result; +} + +/**--------------------------------------------------------------------------- + * \brief wlan_ssr_deinit + * + * Function de-linking the platform driver to the host driver + * + * \param - None + * + * \return - None + * --------------------------------------------------------------------------*/ +void wlan_ssr_deinit() +{ + cnss_deinit(); + + if (gDevInfo) { + /* *gDevInfo has to be deallocated. */ + kfree(gDevInfo); + gDevInfo = NULL; + } + + return; +} +#endif /* #if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) */ + //Register the module init/exit functions module_init(hdd_module_init); module_exit(hdd_module_exit); diff --git a/CORE/HDD/src/wlan_hdd_wext.c b/CORE/HDD/src/wlan_hdd_wext.c index 0da54aa36fd8..331634fb22a0 100644 --- a/CORE/HDD/src/wlan_hdd_wext.c +++ b/CORE/HDD/src/wlan_hdd_wext.c @@ -292,6 +292,9 @@ static const hdd_freq_chan_map_t freq_chan_map[] = { {2412, 1}, {2417, 2}, #define WE_DUMP_CHANINFO_START 11 #define WE_DUMP_CHANINFO 12 #define WE_DUMP_WATCHDOG 13 +#ifdef DEBUG +#define WE_SET_FW_CRASH_INJECT 14 +#endif #endif /* Private ioctls and their sub-ioctls */ #define WLAN_PRIV_SET_VAR_INT_GET_NONE (SIOCIWFIRSTPRIV + 7) @@ -5584,6 +5587,16 @@ static int iw_setnone_getnone(struct net_device *dev, struct iw_request_info *in 0, GEN_CMD); break; } +#ifdef DEBUG + case WE_SET_FW_CRASH_INJECT: + { + hddLog(LOGE, "WE_FW_CRASH_INJECT"); + ret = process_wma_set_command((int) pAdapter->sessionId, + (int) GEN_PARAM_CRASH_INJECT, + 0, GEN_CMD); + break; + } +#endif #endif default: { @@ -8183,6 +8196,12 @@ static const struct iw_priv_args we_private_args[] = { 0, 0, "dump_watchdog" }, +#ifdef DEBUG + { WE_SET_FW_CRASH_INJECT, + 0, + 0, + "crash_inject" }, +#endif #endif /* handlers for main ioctl */ { WLAN_PRIV_SET_VAR_INT_GET_NONE, diff --git a/CORE/SERVICES/BMI/ol_fw.c b/CORE/SERVICES/BMI/ol_fw.c index 25f6728ba541..422a98884c41 100644 --- a/CORE/SERVICES/BMI/ol_fw.c +++ b/CORE/SERVICES/BMI/ol_fw.c @@ -40,6 +40,10 @@ #include "fw_one_bin.h" #include "bin_sig.h" +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +#include <net/cnss.h> +#endif + extern int dbglog_parse_debug_logs(ol_scn_t scn, u_int8_t *datap, u_int32_t len); @@ -456,6 +460,33 @@ u_int32_t host_interest_item_address(u_int32_t target_type, u_int32_t item_offse } } +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +static struct ol_softc *ramdump_scn; + +static void ramdump_work_handler(struct work_struct *ramdump) +{ + void __iomem *ramdump_base = ioremap(RAMDUMP_LOCATION, TOTAL_DUMP_SIZE); + + if (ramdump_scn) { + ol_target_coredump(ramdump_scn, ramdump_base, TOTAL_DUMP_SIZE); + + /* Add enough time to collect the RAM dump */ + msleep(1000); + } else { + printk("No RAM dump will be collected since ramdump_scn is NULL!\n"); + } + + iounmap(ramdump_base); + + /* Notify SSR framework the target has crashed. */ + cnss_device_crashed(); + + return; +} + +static DECLARE_WORK(ramdump_work, ramdump_work_handler); +#endif + #define REGISTER_DUMP_LEN_MAX 60 #define REG_DUMP_COUNT 60 @@ -522,6 +553,12 @@ void ol_target_failure(void *instance, A_STATUS status) return; } +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) + /* Collect the RAM dump through a workqueue */ + ramdump_scn = scn; + schedule_work(&ramdump_work); +#endif + dbglog_hdr.dbuf = (struct dbglog_buf_s *)dbglog_hdr_temp.dbuf; dbglog_hdr.dropped = dbglog_hdr_temp.dropped; @@ -815,3 +852,122 @@ int ol_download_firmware(struct ol_softc *scn) return EOK; } + +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +int ol_diag_read(struct ol_softc *scn, u_int8_t* buffer, + u_int32_t pos, size_t count) +{ + int result = 0; + + if ((count == 4) && ((pos & 3) == 0)) { + result = HIFDiagReadAccess(scn->hif_hdl, pos, + (u_int32_t*)buffer); + } else { + size_t amountRead = 0; + size_t readSize = PCIE_READ_LIMIT; + size_t remainder = 0; + if (count > PCIE_READ_LIMIT) { + while ((amountRead < count) && (0 == result)) { + result = HIFDiagReadMem(scn->hif_hdl, pos, + buffer, readSize); + if (0 == result) { + buffer += readSize; + pos += readSize; + amountRead += readSize; + remainder = count - amountRead; + if (remainder < PCIE_READ_LIMIT) + readSize = remainder; + } + } + } else { + result = HIFDiagReadMem(scn->hif_hdl, pos, + buffer, count); + } + } + + if (!result) { + return count; + } else { + return -EIO; + } +} + +/**--------------------------------------------------------------------------- + * \brief ol_target_coredump + * + * Function to perform core dump for the target + * + * \param: scn - ol_softc handler + * memoryBlock - non-NULL reserved memory location + * blockLength - size of the dump to collect + * + * \return: None + * --------------------------------------------------------------------------*/ +void ol_target_coredump(void *inst, void* memoryBlock, u_int32_t blockLength) +{ + struct ol_softc *scn = (struct ol_softc *)inst; + char* bufferLoc = memoryBlock; + int result = 0; + u_int32_t reg_dump_area = 0; + u_int32_t amountRead = 0; + u_int32_t sectionCount = 0; + u_int32_t pos = 0; + u_int32_t readLen = 0; + + /* + * SECTION = REGISTER + * START = 0x4000 + * LENGTH = 0x6c000 + * + * SECTION = DRAM + * START = 0x400000 + * LENGTH = 0x50000 + * + * SECTION = IRAM + * START = 0x980000 + * LENGTH = 0x38000 + * + */ + + if (HIFDiagReadMem(scn->hif_hdl, + host_interest_item_address(scn->target_type, + offsetof(struct host_interest_s, hi_failure_state)), + (A_UCHAR*) ®_dump_area, sizeof(u_int32_t)) != A_OK) { + return; + } + + while ((sectionCount < 3) && (amountRead < blockLength)) { + switch (sectionCount) { + case 0: + /* REGISTER SECTION */ + pos = reg_dump_area; + readLen = REGISTER_SIZE; + break; + case 1: + /* DRAM SECTION */ + pos = DRAM_LOCATION; + readLen = DRAM_SIZE; + break; + case 2: + /* IRAM SECTION */ + pos = IRAM_LOCATION; + readLen = IRAM_SIZE; + break; + } + + if ((blockLength - amountRead) >= readLen) { + result = ol_diag_read(scn, bufferLoc, pos, readLen); + if (result != EIO) { + amountRead += result; + bufferLoc += result; + sectionCount++; + } else { + break; /* Could not read the section */ + } + } else { + break; /* Insufficient room in buffer */ + } + } +} +#endif + diff --git a/CORE/SERVICES/BMI/ol_fw.h b/CORE/SERVICES/BMI/ol_fw.h index 9d0391cba052..72404328aba0 100644 --- a/CORE/SERVICES/BMI/ol_fw.h +++ b/CORE/SERVICES/BMI/ol_fw.h @@ -48,6 +48,25 @@ #define VDEV_DEFAULT_STATS_UPDATE_PERIOD 500 #define PEER_DEFAULT_STATS_UPDATE_PERIOD 500 +#if defined(QCA_WIFI_2_0) && !defined(QCA_WIFI_ISOC) +#define REGISTER_LOCATION 0x4000 +#define REGISTER_SIZE 0x6c000 + +#define DRAM_LOCATION 0x400000 +#define DRAM_SIZE 0x50000 + +#define IRAM_LOCATION 0x980000 +#define IRAM_SIZE 0x38000 + +#define TOTAL_DUMP_SIZE REGISTER_SIZE + DRAM_SIZE + IRAM_SIZE +#define PCIE_READ_LIMIT 0x5000 +#define RAMDUMP_LOCATION 0x0D400000 + +void ol_target_coredump(void *instance, void* memoryBlock, + u_int32_t blockLength); +int ol_diag_read(struct ol_softc *scn, u_int8_t* buffer, + u_int32_t pos, size_t count); +#endif int ol_download_firmware(struct ol_softc *scn); int ol_configure_target(struct ol_softc *scn); void ol_target_failure(void *instance, A_STATUS status); diff --git a/CORE/SERVICES/COMMON/wma_api.h b/CORE/SERVICES/COMMON/wma_api.h index f385f2adfb44..233017c499ab 100644 --- a/CORE/SERVICES/COMMON/wma_api.h +++ b/CORE/SERVICES/COMMON/wma_api.h @@ -74,6 +74,7 @@ typedef enum { GEN_PARAM_DUMP_CHANINFO_START, GEN_PARAM_DUMP_CHANINFO, GEN_PARAM_DUMP_WATCHDOG, + GEN_PARAM_CRASH_INJECT, } GEN_PARAM; #define VDEV_CMD 1 diff --git a/CORE/SERVICES/WMA/wma.c b/CORE/SERVICES/WMA/wma.c index 910860cd717e..2294d9d24952 100644 --- a/CORE/SERVICES/WMA/wma.c +++ b/CORE/SERVICES/WMA/wma.c @@ -3942,6 +3942,36 @@ static int32_t wma_set_priv_cfg(tp_wma_handle wma_handle, return ret; } +static int wmi_crash_inject(wmi_unified_t wmi_handle) +{ + int ret = 0; + WMI_FORCE_FW_HANG_CMD_fixed_param *cmd; + u_int16_t len = sizeof(*cmd); + wmi_buf_t buf; + + buf = wmi_buf_alloc(wmi_handle, len); + if (!buf) { + WMA_LOGE("%s: wmi_buf_alloc failed!", __func__); + return -ENOMEM; + } + + cmd = (WMI_FORCE_FW_HANG_CMD_fixed_param *) wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_WMI_FORCE_FW_HANG_CMD_fixed_param, + WMITLV_GET_STRUCT_TLVLEN(WMI_FORCE_FW_HANG_CMD_fixed_param)); + cmd->type = 1; + cmd->delay_time_ms = 0; + + ret = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_FORCE_FW_HANG_CMDID); + if (ret < 0) { + WMA_LOGE("%s: Failed to send set param command, ret = %d", + __func__, ret); + wmi_buf_free(buf); + } + + return ret; +} + static void wma_process_cli_set_cmd(tp_wma_handle wma, wda_cli_set_cmd_t *privcmd) { @@ -4025,6 +4055,9 @@ static void wma_process_cli_set_cmd(tp_wma_handle wma, case GEN_PARAM_DUMP_WATCHDOG: HTCDump(wma->htc_handle, WD_DUMP, false); break; + case GEN_PARAM_CRASH_INJECT: + ret = wmi_crash_inject(wma->wmi_handle); + break; default: WMA_LOGE("Invalid param id 0x%x", privcmd->param_id); break; |
