diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index 94e0a4d4fed3..6525f8b0f307 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -250,6 +250,21 @@ void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv, bool in_panic) } } +void cnss_bus_device_crashed(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_device_crashed(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv) { if (!plat_priv) diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index 1e7cc0f3c1f2..f7cd54ddebb6 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -35,6 +35,7 @@ int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv); void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t); void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv, bool in_panic); +void cnss_bus_device_crashed(struct cnss_plat_data *plat_priv); int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv); int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv); int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv); diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index f22e95e3ebed..79aa1efa6001 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include "main.h" #include "bus.h" @@ -26,7 +24,7 @@ #define CNSS_DUMP_NAME "CNSS_WLAN" #define CNSS_DUMP_DESC_SIZE 0x1000 #define CNSS_DUMP_SEG_VER 0x1 -#define WLAN_RECOVERY_DELAY 1000 +#define RECOVERY_DELAY_MS 100 #define FILE_SYSTEM_READY 1 #define FW_READY_TIMEOUT 20000 #define FW_ASSERT_TIMEOUT 5000 @@ -765,7 +763,7 @@ static void cnss_put_resources(struct cnss_plat_data *plat_priv) cnss_put_vreg_type(plat_priv, CNSS_VREG_PRIM); } -#ifdef CONFIG_ESOC +#if IS_ENABLED(CONFIG_ESOC) && IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) static int cnss_modem_notifier_nb(struct notifier_block *nb, unsigned long code, void *ss_handle) @@ -873,6 +871,7 @@ static inline int cnss_register_esoc(struct cnss_plat_data *plat_priv) static inline void cnss_unregister_esoc(struct cnss_plat_data *plat_priv) {} #endif +#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc) { struct cnss_plat_data *plat_priv; @@ -965,6 +964,36 @@ static int cnss_subsys_ramdump(int enable, return cnss_bus_dev_ramdump(plat_priv); } +static void cnss_recovery_work_handler(struct work_struct *work) +{ +} +#else +static void cnss_recovery_work_handler(struct work_struct *work) +{ + struct cnss_plat_data *plat_priv = + container_of(work, struct cnss_plat_data, recovery_work); + + if (!plat_priv->recovery_enabled) + panic("subsys-restart: Resetting the SoC wlan crashed\n"); + + cnss_bus_dev_shutdown(plat_priv); + msleep(RECOVERY_DELAY_MS); + cnss_bus_dev_powerup(plat_priv); +} + +void cnss_device_crashed(struct device *dev) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!plat_priv) + return; + + set_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + schedule_work(&plat_priv->recovery_work); +} +EXPORT_SYMBOL(cnss_device_crashed); +#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ + void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); @@ -999,9 +1028,6 @@ static const char *cnss_recovery_reason_to_str(enum cnss_recovery_reason reason) static int cnss_do_recovery(struct cnss_plat_data *plat_priv, enum cnss_recovery_reason reason) { - struct cnss_subsys_info *subsys_info = - &plat_priv->subsys_info; - plat_priv->recovery_count++; if (plat_priv->device_id == QCA6174_DEVICE_ID) @@ -1034,11 +1060,7 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, break; } - if (!subsys_info->subsys_device) - return 0; - - subsys_set_crash_status(subsys_info->subsys_device, true); - subsystem_restart_dev(subsys_info->subsys_device); + cnss_bus_device_crashed(plat_priv); return 0; @@ -1609,6 +1631,7 @@ int cnss_va_to_pa(struct device *dev, size_t size, void *va, dma_addr_t dma, return 0; } +#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) int cnss_register_subsys(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -1660,6 +1683,70 @@ void cnss_unregister_subsys(struct cnss_plat_data *plat_priv) subsys_unregister(subsys_info->subsys_device); } +static void *cnss_create_ramdump_device(struct cnss_plat_data *plat_priv) +{ + struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; + + return create_ramdump_device(subsys_info->subsys_desc.name, + subsys_info->subsys_desc.dev); +} + +static void cnss_destroy_ramdump_device(struct cnss_plat_data *plat_priv, + void *ramdump_dev) +{ + destroy_ramdump_device(ramdump_dev); +} +#else +static int cnss_panic_handler(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct cnss_plat_data *plat_priv = + container_of(nb, struct cnss_plat_data, panic_nb); + + cnss_bus_dev_crash_shutdown(plat_priv); + + return NOTIFY_DONE; +} + +int cnss_register_subsys(struct cnss_plat_data *plat_priv) +{ + int ret; + + if (!plat_priv) + return -ENODEV; + + plat_priv->panic_nb.notifier_call = cnss_panic_handler; + ret = atomic_notifier_chain_register(&panic_notifier_list, + &plat_priv->panic_nb); + if (ret) { + cnss_pr_err("Failed to register panic handler\n"); + return -EINVAL; + } + + return 0; +} + +void cnss_unregister_subsys(struct cnss_plat_data *plat_priv) +{ + int ret; + + ret = atomic_notifier_chain_unregister(&panic_notifier_list, + &plat_priv->panic_nb); + if (ret) + cnss_pr_err("Failed to unregister panic handler\n"); +} + +static void *cnss_create_ramdump_device(struct cnss_plat_data *plat_priv) +{ + return &plat_priv->plat_dev->dev; +} + +static void cnss_destroy_ramdump_device(struct cnss_plat_data *plat_priv, + void *ramdump_dev) +{ +} +#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ + static int cnss_init_dump_entry(struct cnss_plat_data *plat_priv) { struct cnss_ramdump_info *ramdump_info; @@ -1683,12 +1770,10 @@ static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv) { int ret = 0; struct device *dev; - struct cnss_subsys_info *subsys_info; struct cnss_ramdump_info *ramdump_info; u32 ramdump_size = 0; dev = &plat_priv->plat_dev->dev; - subsys_info = &plat_priv->subsys_info; ramdump_info = &plat_priv->ramdump_info; if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic", @@ -1716,9 +1801,7 @@ static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv) goto free_ramdump; } - ramdump_info->ramdump_dev = - create_ramdump_device(subsys_info->subsys_desc.name, - subsys_info->subsys_desc.dev); + ramdump_info->ramdump_dev = cnss_create_ramdump_device(plat_priv); if (!ramdump_info->ramdump_dev) { cnss_pr_err("Failed to create ramdump device!"); ret = -ENOMEM; @@ -1742,7 +1825,8 @@ static void cnss_unregister_ramdump_v1(struct cnss_plat_data *plat_priv) ramdump_info = &plat_priv->ramdump_info; if (ramdump_info->ramdump_dev) - destroy_ramdump_device(ramdump_info->ramdump_dev); + cnss_destroy_ramdump_device(plat_priv, + ramdump_info->ramdump_dev); if (ramdump_info->ramdump_va) dma_free_coherent(dev, ramdump_info->ramdump_size, @@ -1774,14 +1858,12 @@ static int cnss_ignore_dump_data_reg_fail(int ret) static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv) { int ret = 0; - struct cnss_subsys_info *subsys_info; struct cnss_ramdump_info_v2 *info_v2; struct cnss_dump_data *dump_data; struct msm_dump_entry dump_entry; struct device *dev = &plat_priv->plat_dev->dev; u32 ramdump_size = 0; - subsys_info = &plat_priv->subsys_info; info_v2 = &plat_priv->ramdump_info_v2; dump_data = &info_v2->dump_data; @@ -1813,9 +1895,7 @@ static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv) goto free_ramdump; } - info_v2->ramdump_dev = - create_ramdump_device(subsys_info->subsys_desc.name, - subsys_info->subsys_desc.dev); + info_v2->ramdump_dev = cnss_create_ramdump_device(plat_priv); if (!info_v2->ramdump_dev) { cnss_pr_err("Failed to create ramdump device!\n"); ret = -ENOMEM; @@ -1837,7 +1917,7 @@ static void cnss_unregister_ramdump_v2(struct cnss_plat_data *plat_priv) info_v2 = &plat_priv->ramdump_info_v2; if (info_v2->ramdump_dev) - destroy_ramdump_device(info_v2->ramdump_dev); + cnss_destroy_ramdump_device(plat_priv, info_v2->ramdump_dev); kfree(info_v2->dump_data_vaddr); info_v2->dump_data_vaddr = NULL; @@ -2031,6 +2111,32 @@ static void cnss_unregister_bus_scale(struct cnss_plat_data *plat_priv) kfree(bus_bw_info->cfg_table); } +static ssize_t recovery_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cnss_plat_data *plat_priv = dev_get_drvdata(dev); + unsigned int recovery = 0; + + if (!plat_priv) + return -ENODEV; + + if (sscanf(buf, "%du", &recovery) != 1) { + cnss_pr_err("Invalid recovery sysfs command\n"); + return -EINVAL; + } + + if (recovery) + plat_priv->recovery_enabled = true; + else + plat_priv->recovery_enabled = false; + + cnss_pr_dbg("%s WLAN recovery, count is %zu\n", + plat_priv->recovery_enabled ? "Enable" : "Disable", count); + + return count; +} + static ssize_t shutdown_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -2094,10 +2200,12 @@ static ssize_t fs_ready_store(struct device *dev, static DEVICE_ATTR_WO(fs_ready); static DEVICE_ATTR_WO(shutdown); +static DEVICE_ATTR_WO(recovery); static struct attribute *cnss_attrs[] = { &dev_attr_fs_ready.attr, &dev_attr_shutdown.attr, + &dev_attr_recovery.attr, NULL, }; @@ -2223,6 +2331,7 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) cnss_pr_err("Failed to init platform device wakeup source, err = %d\n", ret); + INIT_WORK(&plat_priv->recovery_work, cnss_recovery_work_handler); init_completion(&plat_priv->power_up_complete); init_completion(&plat_priv->cal_complete); init_completion(&plat_priv->rddm_complete); diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 7b2db9149138..0401c69809e9 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -5,7 +5,7 @@ #define _CNSS_MAIN_H #include -#ifdef CONFIG_ESOC +#if IS_ENABLED(CONFIG_ESOC) #include #endif #include @@ -14,7 +14,11 @@ #include #include #include +#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) +#include +#include #include +#endif #include "qmi.h" @@ -83,14 +87,16 @@ struct cnss_pinctrl_info { struct pinctrl_state *wlan_en_sleep; }; +#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) struct cnss_subsys_info { struct subsys_device *subsys_device; struct subsys_desc subsys_desc; void *subsys_handle; }; +#endif struct cnss_ramdump_info { - struct ramdump_device *ramdump_dev; + void *ramdump_dev; unsigned long ramdump_size; void *ramdump_va; phys_addr_t ramdump_pa; @@ -114,14 +120,14 @@ struct cnss_dump_data { }; struct cnss_ramdump_info_v2 { - struct ramdump_device *ramdump_dev; + void *ramdump_dev; unsigned long ramdump_size; void *dump_data_vaddr; u8 dump_data_valid; struct cnss_dump_data dump_data; }; -#ifdef CONFIG_ESOC +#if IS_ENABLED(CONFIG_ESOC) struct cnss_esoc_info { struct esoc_desc *esoc_desc; u8 notify_modem_status; @@ -339,26 +345,31 @@ struct cnss_plat_data { struct list_head vreg_list; struct list_head clk_list; struct cnss_pinctrl_info pinctrl_info; +#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) struct cnss_subsys_info subsys_info; +#endif struct cnss_ramdump_info ramdump_info; struct cnss_ramdump_info_v2 ramdump_info_v2; -#ifdef CONFIG_ESOC +#if IS_ENABLED(CONFIG_ESOC) struct cnss_esoc_info esoc_info; #endif struct cnss_bus_bw_info bus_bw_info; struct notifier_block modem_nb; struct notifier_block reboot_nb; + struct notifier_block panic_nb; struct cnss_platform_cap cap; struct pm_qos_request qos_request; struct cnss_device_version device_version; unsigned long device_id; enum cnss_driver_status driver_status; u32 recovery_count; + u8 recovery_enabled; unsigned long driver_state; struct list_head event_list; spinlock_t event_lock; /* spinlock for driver work event handling */ struct work_struct event_work; struct workqueue_struct *event_wq; + struct work_struct recovery_work; struct qmi_handle qmi_wlfw; struct wlfw_rf_chip_info chip_info; struct wlfw_rf_board_info board_info; diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 93d0b4fe7748..522bd617f1df 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "main.h" #include "bus.h" @@ -4197,6 +4196,14 @@ void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv) plat_priv->ramdump_info_v2.dump_data_valid = false; } +void cnss_pci_device_crashed(struct cnss_pci_data *pci_priv) +{ + if (!pci_priv) + return; + + cnss_device_crashed(&pci_priv->pci_dev->dev); +} + static int cnss_mhi_pm_runtime_get(struct mhi_controller *mhi_ctrl, void *priv) { struct cnss_pci_data *pci_priv = priv; diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index dd293b912a9d..a1e1a67f9858 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -172,6 +172,7 @@ void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv); int cnss_pci_load_m3(struct cnss_pci_data *pci_priv); int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic); +void cnss_pci_device_crashed(struct cnss_pci_data *pci_priv); void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv); u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv); int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv);