diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 1e6ae66bad64..3814cee6b3be 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -77,6 +77,9 @@ static DEFINE_SPINLOCK(time_sync_lock); #define CNSS_DEBUG_DUMP_SRAM_START 0x1403D58 #define CNSS_DEBUG_DUMP_SRAM_SIZE 10 +#define HANG_DATA_LENGTH 384 +#define HANG_DATA_OFFSET ((3 * 1024 * 1024) - HANG_DATA_LENGTH) + static struct cnss_pci_reg ce_src[] = { { "SRC_RING_BASE_LSB", QCA6390_CE_SRC_RING_BASE_LSB_OFFSET }, { "SRC_RING_BASE_MSB", QCA6390_CE_SRC_RING_BASE_MSB_OFFSET }, @@ -3854,6 +3857,60 @@ static void cnss_pci_remove_dump_seg(struct cnss_pci_data *pci_priv, cnss_minidump_remove_region(plat_priv, type, seg_no, va, pa, size); } +int cnss_call_driver_uevent(struct cnss_pci_data *pci_priv, + enum cnss_driver_status status, void *data) +{ + struct cnss_uevent_data uevent_data; + struct cnss_wlan_driver *driver_ops; + + driver_ops = pci_priv->driver_ops; + if (!driver_ops || !driver_ops->update_event) { + cnss_pr_dbg("Hang event driver ops is NULL\n"); + return -EINVAL; + } + + cnss_pr_dbg("Calling driver uevent: %d\n", status); + + uevent_data.status = status; + uevent_data.data = data; + + return driver_ops->update_event(pci_priv->pci_dev, &uevent_data); +} + +static void cnss_pci_send_hang_event(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; + struct cnss_hang_event hang_event; + void *hang_data_va = NULL; + int i = 0; + + if (!fw_mem || !plat_priv->fw_mem_seg_len) + return; + + memset(&hang_event, 0, sizeof(hang_event)); + for (i = 0; i < plat_priv->fw_mem_seg_len; i++) { + if (fw_mem[i].type == QMI_WLFW_MEM_TYPE_DDR_V01 && + fw_mem[i].va) { + hang_data_va = fw_mem[i].va + HANG_DATA_OFFSET; + hang_event.hang_event_data = kmemdup(hang_data_va, + HANG_DATA_LENGTH, + GFP_ATOMIC); + if (!hang_event.hang_event_data) { + cnss_pr_dbg("Hang data memory alloc failed\n"); + return; + } + hang_event.hang_event_data_len = HANG_DATA_LENGTH; + break; + } + } + + cnss_call_driver_uevent(pci_priv, CNSS_HANG_EVENT, &hang_event); + + kfree(hang_event.hang_event_data); + hang_event.hang_event_data = NULL; +} + void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -3865,6 +3922,9 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; int ret, i, j; + if (test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + cnss_pci_send_hang_event(pci_priv); + if (test_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state)) { cnss_pr_dbg("RAM dump is already collected, skip\n"); return; diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index b04cb4ec6dc8..dd293b912a9d 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -199,6 +199,8 @@ void cnss_pci_pm_runtime_put_noidle(struct cnss_pci_data *pci_priv); void cnss_pci_pm_runtime_mark_last_busy(struct cnss_pci_data *pci_priv); int cnss_pci_update_status(struct cnss_pci_data *pci_priv, enum cnss_driver_status status); +int cnss_call_driver_uevent(struct cnss_pci_data *pci_priv, + enum cnss_driver_status status, void *data); int cnss_pcie_is_device_down(struct cnss_pci_data *pci_priv); int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv); int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv); diff --git a/include/net/cnss2.h b/include/net/cnss2.h index 78143ea5a701..eb37edc647f5 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -67,6 +67,25 @@ struct cnss_wlan_runtime_ops { int (*runtime_resume)(struct pci_dev *pdev); }; +enum cnss_driver_status { + CNSS_UNINITIALIZED, + CNSS_INITIALIZED, + CNSS_LOAD_UNLOAD, + CNSS_RECOVERY, + CNSS_FW_DOWN, + CNSS_HANG_EVENT, +}; + +struct cnss_hang_event { + void *hang_event_data; + u16 hang_event_data_len; +}; + +struct cnss_uevent_data { + enum cnss_driver_status status; + void *data; +}; + struct cnss_wlan_driver { char *name; int (*probe)(struct pci_dev *pdev, const struct pci_device_id *id); @@ -83,18 +102,12 @@ struct cnss_wlan_driver { int (*resume_noirq)(struct pci_dev *pdev); void (*modem_status)(struct pci_dev *pdev, int state); void (*update_status)(struct pci_dev *pdev, uint32_t status); + int (*update_event)(struct pci_dev *pdev, + struct cnss_uevent_data *uevent); struct cnss_wlan_runtime_ops *runtime_ops; const struct pci_device_id *id_table; }; -enum cnss_driver_status { - CNSS_UNINITIALIZED, - CNSS_INITIALIZED, - CNSS_LOAD_UNLOAD, - CNSS_RECOVERY, - CNSS_FW_DOWN, -}; - struct cnss_ce_tgt_pipe_cfg { u32 pipe_num; u32 pipe_dir;