Loading drivers/net/wireless/cnss2/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -4,4 +4,6 @@ cnss2-y := main.o cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o cnss2-y += qmi.o cnss2-y += utils.o cnss2-y += wlan_firmware_service_v01.o drivers/net/wireless/cnss2/main.c +403 −2 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ #define CNSS_DUMP_NAME "CNSS_WLAN" #define WLAN_RECOVERY_DELAY 1000 #define FILE_SYSTEM_READY 1 #define FW_READY_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 static struct cnss_plat_data *plat_env; Loading @@ -41,6 +43,18 @@ MODULE_PARM_DESC(fbc_bypass, "Bypass firmware download when loading WLAN driver"); #endif static bool qmi_bypass; #ifdef CONFIG_CNSS2_DEBUG module_param(qmi_bypass, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(qmi_bypass, "Bypass QMI from platform driver"); #endif static bool enable_waltest; #ifdef CONFIG_CNSS2_DEBUG module_param(enable_waltest, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); #endif static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = { "qwlan30.bin", "bdwlan30.bin", "otp30.bin", "utf30.bin", "utfbd30.bin", "epping30.bin", "evicted30.bin" Loading @@ -51,6 +65,15 @@ static struct cnss_fw_files FW_FILES_DEFAULT = { "utfbd.bin", "epping.bin", "evicted.bin" }; struct cnss_driver_event { struct list_head list; enum cnss_driver_event_type type; bool sync; struct completion complete; int ret; void *data; }; static enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) { if (!dev) Loading Loading @@ -287,6 +310,314 @@ out: } EXPORT_SYMBOL(cnss_common_get_wlan_mac_address); int cnss_wlan_enable(struct device *dev, struct cnss_wlan_enable_cfg *config, enum cnss_driver_mode mode, const char *host_version) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct wlfw_wlan_cfg_req_msg_v01 req; u32 i; int ret = 0; if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; if (qmi_bypass) return 0; if (!config || !host_version) { cnss_pr_err("Invalid config or host_version pointer\n"); return -EINVAL; } cnss_pr_dbg("Mode: %d, config: %pK, host_version: %s\n", mode, config, host_version); if (mode == CNSS_WALTEST || mode == CNSS_CCPM) goto skip_cfg; memset(&req, 0, sizeof(req)); req.host_version_valid = 1; strlcpy(req.host_version, host_version, QMI_WLFW_MAX_STR_LEN_V01 + 1); req.tgt_cfg_valid = 1; if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01) req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; else req.tgt_cfg_len = config->num_ce_tgt_cfg; for (i = 0; i < req.tgt_cfg_len; i++) { req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num; req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir; req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries; req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max; req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags; } req.svc_cfg_valid = 1; if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01) req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01; else req.svc_cfg_len = config->num_ce_svc_pipe_cfg; for (i = 0; i < req.svc_cfg_len; i++) { req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id; req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir; req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; } req.shadow_reg_valid = 1; if (config->num_shadow_reg_cfg > QMI_WLFW_MAX_NUM_SHADOW_REG_V01) req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01; else req.shadow_reg_len = config->num_shadow_reg_cfg; memcpy(req.shadow_reg, config->shadow_reg_cfg, sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len); ret = cnss_wlfw_wlan_cfg_send_sync(plat_priv, &req); if (ret) goto out; skip_cfg: ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, mode); out: return ret; } EXPORT_SYMBOL(cnss_wlan_enable); int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; if (qmi_bypass) return 0; return cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); } EXPORT_SYMBOL(cnss_wlan_disable); static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; if (!plat_priv) return -ENODEV; set_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); if (ret) goto out; ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv); if (ret) goto out; return 0; out: return ret; } static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) { struct cnss_pci_data *pci_priv; int ret = 0; if (!plat_priv) return -ENODEV; set_bit(CNSS_FW_READY, &plat_priv->driver_state); pci_priv = plat_priv->bus_priv; if (!pci_priv) return -ENODEV; if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) complete(&plat_priv->fw_ready_event); else ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_CALIBRATION_V01); return ret; } static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) { struct cnss_pci_data *pci_priv; if (!plat_priv) return -ENODEV; set_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state); pci_priv = plat_priv->bus_priv; if (!pci_priv) return -ENODEV; cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); cnss_pci_stop_mhi(pci_priv); cnss_suspend_pci_link(pci_priv); cnss_power_off_device(plat_priv); return 0; } static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) { switch (type) { case CNSS_DRIVER_EVENT_SERVER_ARRIVE: return "SERVER_ARRIVE"; case CNSS_DRIVER_EVENT_SERVER_EXIT: return "SERVER_EXIT"; case CNSS_DRIVER_EVENT_REQUEST_MEM: return "REQUEST_MEM"; case CNSS_DRIVER_EVENT_FW_MEM_READY: return "FW_MEM_READY"; case CNSS_DRIVER_EVENT_FW_READY: return "FW_READY"; case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: return "COLD_BOOT_CAL_DONE"; case CNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } return "UNKNOWN"; }; static void cnss_driver_event_work(struct work_struct *work) { struct cnss_plat_data *plat_priv = container_of(work, struct cnss_plat_data, event_work); struct cnss_driver_event *event; unsigned long flags; int ret = 0; if (!plat_priv) return; spin_lock_irqsave(&plat_priv->event_lock, flags); while (!list_empty(&plat_priv->event_list)) { event = list_first_entry(&plat_priv->event_list, struct cnss_driver_event, list); list_del(&event->list); spin_unlock_irqrestore(&plat_priv->event_lock, flags); cnss_pr_dbg("Processing driver event: %s%s(%d), state: 0x%lx\n", cnss_driver_event_to_str(event->type), event->sync ? "-sync" : "", event->type, plat_priv->driver_state); switch (event->type) { case CNSS_DRIVER_EVENT_SERVER_ARRIVE: ret = cnss_wlfw_server_arrive(plat_priv); break; case CNSS_DRIVER_EVENT_SERVER_EXIT: ret = cnss_wlfw_server_exit(plat_priv); break; case CNSS_DRIVER_EVENT_REQUEST_MEM: ret = cnss_wlfw_respond_mem_send_sync(plat_priv); break; case CNSS_DRIVER_EVENT_FW_MEM_READY: ret = cnss_fw_mem_ready_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_FW_READY: ret = cnss_fw_ready_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: ret = cnss_cold_boot_cal_done_hdlr(plat_priv); break; default: cnss_pr_err("Invalid driver event type: %d", event->type); kfree(event); continue; } spin_lock_irqsave(&plat_priv->event_lock, flags); if (event->sync) { event->ret = ret; complete(&event->complete); continue; } spin_unlock_irqrestore(&plat_priv->event_lock, flags); kfree(event); spin_lock_irqsave(&plat_priv->event_lock, flags); } spin_unlock_irqrestore(&plat_priv->event_lock, flags); } int cnss_driver_event_post(struct cnss_plat_data *plat_priv, enum cnss_driver_event_type type, bool sync, void *data) { struct cnss_driver_event *event; unsigned long flags; int gfp = GFP_KERNEL; int ret = 0; if (!plat_priv) return -ENODEV; cnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n", cnss_driver_event_to_str(type), type, sync ? "-sync" : "", plat_priv->driver_state); if (type >= CNSS_DRIVER_EVENT_MAX) { cnss_pr_err("Invalid Event type: %d, can't post", type); return -EINVAL; } if (in_interrupt() || irqs_disabled()) gfp = GFP_ATOMIC; event = kzalloc(sizeof(*event), gfp); if (!event) return -ENOMEM; event->type = type; event->data = data; init_completion(&event->complete); event->ret = CNSS_EVENT_PENDING; event->sync = sync; spin_lock_irqsave(&plat_priv->event_lock, flags); list_add_tail(&event->list, &plat_priv->event_list); spin_unlock_irqrestore(&plat_priv->event_lock, flags); queue_work(plat_priv->event_wq, &plat_priv->event_work); if (!sync) return ret; ret = wait_for_completion_interruptible(&event->complete); cnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", cnss_driver_event_to_str(type), type, plat_priv->driver_state, ret, event->ret); spin_lock_irqsave(&plat_priv->event_lock, flags); if (ret == -ERESTARTSYS && event->ret == CNSS_EVENT_PENDING) { event->sync = false; spin_unlock_irqrestore(&plat_priv->event_lock, flags); return ret; } spin_unlock_irqrestore(&plat_priv->event_lock, flags); ret = event->ret; kfree(event); return ret; } int cnss_power_up(struct device *dev) { int ret = 0; Loading Loading @@ -664,6 +995,26 @@ static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv) } bypass_fbc: if (qmi_bypass) goto skip_fw_ready; reinit_completion(&plat_priv->fw_ready_event); ret = wait_for_completion_timeout(&plat_priv->fw_ready_event, msecs_to_jiffies(FW_READY_TIMEOUT)); if (ret == 0) { cnss_pr_err("Timeout for FW ready event!\n"); ret = -EBUSY; goto stop_mhi; } skip_fw_ready: if (enable_waltest) { cnss_pr_dbg("Firmware waltest is enabled.\n"); ret = 0; goto out; } if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) { ret = plat_priv->driver_ops->probe(pci_priv->pci_dev, pci_priv->pci_device_id); Loading @@ -687,6 +1038,8 @@ bypass_fbc: goto stop_mhi; } set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); return 0; stop_mhi: Loading @@ -711,6 +1064,9 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) if (!plat_priv->driver_ops) return -EINVAL; if (enable_waltest) goto bypass_driver_remove; if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) { cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); plat_priv->driver_ops->remove(pci_priv->pci_dev); Loading @@ -721,6 +1077,9 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) plat_priv->driver_ops->shutdown(pci_priv->pci_dev); } clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); bypass_driver_remove: if (!fbc_bypass) cnss_pci_stop_mhi(pci_priv); Loading Loading @@ -1015,7 +1374,7 @@ static int cnss_register_ramdump(struct cnss_plat_data *plat_priv) ramdump_info->ramdump_size = ramdump_size; } cnss_pr_dbg("ramdump va: %p, pa: %pa\n", cnss_pr_dbg("ramdump va: %pK, pa: %pa\n", ramdump_info->ramdump_va, &ramdump_info->ramdump_pa); if (ramdump_info->ramdump_size == 0) { Loading Loading @@ -1111,6 +1470,11 @@ static ssize_t cnss_fs_ready_store(struct device *dev, cnss_pr_dbg("File system is ready, fs_ready is %d, count is %zu\n", fs_ready, count); if (qmi_bypass) { cnss_pr_dbg("QMI is bypassed.\n"); return count; } if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); return count; Loading Loading @@ -1153,6 +1517,29 @@ static void cnss_remove_sysfs(struct cnss_plat_data *plat_priv) device_remove_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready); } static int cnss_event_work_init(struct cnss_plat_data *plat_priv) { spin_lock_init(&plat_priv->event_lock); plat_priv->event_wq = alloc_workqueue("cnss_driver_event", WQ_UNBOUND, 1); if (!plat_priv->event_wq) { cnss_pr_err("Failed to create event workqueue!\n"); return -EFAULT; } INIT_WORK(&plat_priv->event_work, cnss_driver_event_work); INIT_LIST_HEAD(&plat_priv->event_list); init_completion(&plat_priv->fw_ready_event); return 0; } static void cnss_event_work_deinit(struct cnss_plat_data *plat_priv) { complete_all(&plat_priv->fw_ready_event); destroy_workqueue(plat_priv->event_wq); } static const struct platform_device_id cnss_platform_id_table[] = { { .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, }, { .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, }, Loading Loading @@ -1235,12 +1622,24 @@ static int cnss_probe(struct platform_device *plat_dev) if (ret) goto unreg_bus_scale; ret = cnss_event_work_init(plat_priv); if (ret) goto remove_sysfs; ret = cnss_qmi_init(plat_priv); if (ret) goto deinit_event_work; register_pm_notifier(&cnss_pm_notifier); cnss_pr_info("Platform driver probed successfully.\n"); return 0; deinit_event_work: cnss_event_work_deinit(plat_priv); remove_sysfs: cnss_remove_sysfs(plat_priv); unreg_bus_scale: cnss_unregister_bus_scale(plat_priv); unreg_ramdump: Loading @@ -1267,13 +1666,15 @@ static int cnss_remove(struct platform_device *plat_dev) struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev); unregister_pm_notifier(&cnss_pm_notifier); cnss_qmi_deinit(plat_priv); cnss_event_work_deinit(plat_priv); cnss_remove_sysfs(plat_priv); cnss_unregister_bus_scale(plat_priv); cnss_unregister_ramdump(plat_priv); cnss_unregister_subsys(plat_priv); cnss_unregister_esoc(plat_priv); cnss_put_resources(plat_priv); cnss_pci_deinit(plat_priv); cnss_put_resources(plat_priv); platform_set_drvdata(plat_dev, NULL); plat_env = NULL; Loading drivers/net/wireless/cnss2/main.h +44 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <soc/qcom/memory_dump.h> #include <soc/qcom/subsystem_restart.h> #include "qmi.h" #define MAX_NO_OF_MAC_ADDR 4 enum cnss_dev_bus_type { Loading Loading @@ -81,6 +83,31 @@ struct cnss_wlan_mac_info { bool is_wlan_mac_set; }; struct cnss_fw_mem { size_t size; void *va; phys_addr_t pa; bool valid; }; enum cnss_driver_event_type { CNSS_DRIVER_EVENT_SERVER_ARRIVE, CNSS_DRIVER_EVENT_SERVER_EXIT, CNSS_DRIVER_EVENT_REQUEST_MEM, CNSS_DRIVER_EVENT_FW_MEM_READY, CNSS_DRIVER_EVENT_FW_READY, CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE, CNSS_DRIVER_EVENT_MAX, }; enum cnss_driver_state { CNSS_QMI_WLFW_CONNECTED, CNSS_FW_MEM_READY, CNSS_FW_READY, CNSS_COLD_BOOT_CAL_DONE, CNSS_DRIVER_PROBED, }; struct cnss_plat_data { struct platform_device *plat_dev; void *bus_priv; Loading @@ -98,10 +125,27 @@ struct cnss_plat_data { enum cnss_driver_status driver_status; uint32_t recovery_count; struct cnss_wlan_mac_info wlan_mac_info; unsigned long driver_state; struct completion fw_ready_event; 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 qmi_handle *qmi_wlfw_clnt; struct work_struct qmi_recv_msg_work; struct notifier_block qmi_wlfw_clnt_nb; struct wlfw_rf_chip_info_s_v01 chip_info; struct wlfw_rf_board_info_s_v01 board_info; struct wlfw_soc_info_s_v01 soc_info; struct wlfw_fw_version_info_s_v01 fw_version_info; struct cnss_fw_mem fw_mem; }; void *cnss_bus_dev_to_bus_priv(struct device *dev); struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev); int cnss_driver_event_post(struct cnss_plat_data *plat_priv, enum cnss_driver_event_type type, bool sync, void *data); int cnss_get_vreg(struct cnss_plat_data *plat_priv); int cnss_get_pinctrl(struct cnss_plat_data *plat_priv); int cnss_power_on_device(struct cnss_plat_data *plat_priv); Loading Loading
drivers/net/wireless/cnss2/Makefile +2 −0 Original line number Diff line number Diff line Loading @@ -4,4 +4,6 @@ cnss2-y := main.o cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o cnss2-y += qmi.o cnss2-y += utils.o cnss2-y += wlan_firmware_service_v01.o
drivers/net/wireless/cnss2/main.c +403 −2 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ #define CNSS_DUMP_NAME "CNSS_WLAN" #define WLAN_RECOVERY_DELAY 1000 #define FILE_SYSTEM_READY 1 #define FW_READY_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 static struct cnss_plat_data *plat_env; Loading @@ -41,6 +43,18 @@ MODULE_PARM_DESC(fbc_bypass, "Bypass firmware download when loading WLAN driver"); #endif static bool qmi_bypass; #ifdef CONFIG_CNSS2_DEBUG module_param(qmi_bypass, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(qmi_bypass, "Bypass QMI from platform driver"); #endif static bool enable_waltest; #ifdef CONFIG_CNSS2_DEBUG module_param(enable_waltest, bool, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); #endif static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = { "qwlan30.bin", "bdwlan30.bin", "otp30.bin", "utf30.bin", "utfbd30.bin", "epping30.bin", "evicted30.bin" Loading @@ -51,6 +65,15 @@ static struct cnss_fw_files FW_FILES_DEFAULT = { "utfbd.bin", "epping.bin", "evicted.bin" }; struct cnss_driver_event { struct list_head list; enum cnss_driver_event_type type; bool sync; struct completion complete; int ret; void *data; }; static enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) { if (!dev) Loading Loading @@ -287,6 +310,314 @@ out: } EXPORT_SYMBOL(cnss_common_get_wlan_mac_address); int cnss_wlan_enable(struct device *dev, struct cnss_wlan_enable_cfg *config, enum cnss_driver_mode mode, const char *host_version) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct wlfw_wlan_cfg_req_msg_v01 req; u32 i; int ret = 0; if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; if (qmi_bypass) return 0; if (!config || !host_version) { cnss_pr_err("Invalid config or host_version pointer\n"); return -EINVAL; } cnss_pr_dbg("Mode: %d, config: %pK, host_version: %s\n", mode, config, host_version); if (mode == CNSS_WALTEST || mode == CNSS_CCPM) goto skip_cfg; memset(&req, 0, sizeof(req)); req.host_version_valid = 1; strlcpy(req.host_version, host_version, QMI_WLFW_MAX_STR_LEN_V01 + 1); req.tgt_cfg_valid = 1; if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01) req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; else req.tgt_cfg_len = config->num_ce_tgt_cfg; for (i = 0; i < req.tgt_cfg_len; i++) { req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num; req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir; req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries; req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max; req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags; } req.svc_cfg_valid = 1; if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01) req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01; else req.svc_cfg_len = config->num_ce_svc_pipe_cfg; for (i = 0; i < req.svc_cfg_len; i++) { req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id; req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir; req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; } req.shadow_reg_valid = 1; if (config->num_shadow_reg_cfg > QMI_WLFW_MAX_NUM_SHADOW_REG_V01) req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01; else req.shadow_reg_len = config->num_shadow_reg_cfg; memcpy(req.shadow_reg, config->shadow_reg_cfg, sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len); ret = cnss_wlfw_wlan_cfg_send_sync(plat_priv, &req); if (ret) goto out; skip_cfg: ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, mode); out: return ret; } EXPORT_SYMBOL(cnss_wlan_enable); int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; if (qmi_bypass) return 0; return cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); } EXPORT_SYMBOL(cnss_wlan_disable); static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; if (!plat_priv) return -ENODEV; set_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); if (ret) goto out; ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv); if (ret) goto out; return 0; out: return ret; } static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) { struct cnss_pci_data *pci_priv; int ret = 0; if (!plat_priv) return -ENODEV; set_bit(CNSS_FW_READY, &plat_priv->driver_state); pci_priv = plat_priv->bus_priv; if (!pci_priv) return -ENODEV; if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) complete(&plat_priv->fw_ready_event); else ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_CALIBRATION_V01); return ret; } static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) { struct cnss_pci_data *pci_priv; if (!plat_priv) return -ENODEV; set_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state); pci_priv = plat_priv->bus_priv; if (!pci_priv) return -ENODEV; cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); cnss_pci_stop_mhi(pci_priv); cnss_suspend_pci_link(pci_priv); cnss_power_off_device(plat_priv); return 0; } static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) { switch (type) { case CNSS_DRIVER_EVENT_SERVER_ARRIVE: return "SERVER_ARRIVE"; case CNSS_DRIVER_EVENT_SERVER_EXIT: return "SERVER_EXIT"; case CNSS_DRIVER_EVENT_REQUEST_MEM: return "REQUEST_MEM"; case CNSS_DRIVER_EVENT_FW_MEM_READY: return "FW_MEM_READY"; case CNSS_DRIVER_EVENT_FW_READY: return "FW_READY"; case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: return "COLD_BOOT_CAL_DONE"; case CNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } return "UNKNOWN"; }; static void cnss_driver_event_work(struct work_struct *work) { struct cnss_plat_data *plat_priv = container_of(work, struct cnss_plat_data, event_work); struct cnss_driver_event *event; unsigned long flags; int ret = 0; if (!plat_priv) return; spin_lock_irqsave(&plat_priv->event_lock, flags); while (!list_empty(&plat_priv->event_list)) { event = list_first_entry(&plat_priv->event_list, struct cnss_driver_event, list); list_del(&event->list); spin_unlock_irqrestore(&plat_priv->event_lock, flags); cnss_pr_dbg("Processing driver event: %s%s(%d), state: 0x%lx\n", cnss_driver_event_to_str(event->type), event->sync ? "-sync" : "", event->type, plat_priv->driver_state); switch (event->type) { case CNSS_DRIVER_EVENT_SERVER_ARRIVE: ret = cnss_wlfw_server_arrive(plat_priv); break; case CNSS_DRIVER_EVENT_SERVER_EXIT: ret = cnss_wlfw_server_exit(plat_priv); break; case CNSS_DRIVER_EVENT_REQUEST_MEM: ret = cnss_wlfw_respond_mem_send_sync(plat_priv); break; case CNSS_DRIVER_EVENT_FW_MEM_READY: ret = cnss_fw_mem_ready_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_FW_READY: ret = cnss_fw_ready_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: ret = cnss_cold_boot_cal_done_hdlr(plat_priv); break; default: cnss_pr_err("Invalid driver event type: %d", event->type); kfree(event); continue; } spin_lock_irqsave(&plat_priv->event_lock, flags); if (event->sync) { event->ret = ret; complete(&event->complete); continue; } spin_unlock_irqrestore(&plat_priv->event_lock, flags); kfree(event); spin_lock_irqsave(&plat_priv->event_lock, flags); } spin_unlock_irqrestore(&plat_priv->event_lock, flags); } int cnss_driver_event_post(struct cnss_plat_data *plat_priv, enum cnss_driver_event_type type, bool sync, void *data) { struct cnss_driver_event *event; unsigned long flags; int gfp = GFP_KERNEL; int ret = 0; if (!plat_priv) return -ENODEV; cnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n", cnss_driver_event_to_str(type), type, sync ? "-sync" : "", plat_priv->driver_state); if (type >= CNSS_DRIVER_EVENT_MAX) { cnss_pr_err("Invalid Event type: %d, can't post", type); return -EINVAL; } if (in_interrupt() || irqs_disabled()) gfp = GFP_ATOMIC; event = kzalloc(sizeof(*event), gfp); if (!event) return -ENOMEM; event->type = type; event->data = data; init_completion(&event->complete); event->ret = CNSS_EVENT_PENDING; event->sync = sync; spin_lock_irqsave(&plat_priv->event_lock, flags); list_add_tail(&event->list, &plat_priv->event_list); spin_unlock_irqrestore(&plat_priv->event_lock, flags); queue_work(plat_priv->event_wq, &plat_priv->event_work); if (!sync) return ret; ret = wait_for_completion_interruptible(&event->complete); cnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", cnss_driver_event_to_str(type), type, plat_priv->driver_state, ret, event->ret); spin_lock_irqsave(&plat_priv->event_lock, flags); if (ret == -ERESTARTSYS && event->ret == CNSS_EVENT_PENDING) { event->sync = false; spin_unlock_irqrestore(&plat_priv->event_lock, flags); return ret; } spin_unlock_irqrestore(&plat_priv->event_lock, flags); ret = event->ret; kfree(event); return ret; } int cnss_power_up(struct device *dev) { int ret = 0; Loading Loading @@ -664,6 +995,26 @@ static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv) } bypass_fbc: if (qmi_bypass) goto skip_fw_ready; reinit_completion(&plat_priv->fw_ready_event); ret = wait_for_completion_timeout(&plat_priv->fw_ready_event, msecs_to_jiffies(FW_READY_TIMEOUT)); if (ret == 0) { cnss_pr_err("Timeout for FW ready event!\n"); ret = -EBUSY; goto stop_mhi; } skip_fw_ready: if (enable_waltest) { cnss_pr_dbg("Firmware waltest is enabled.\n"); ret = 0; goto out; } if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) { ret = plat_priv->driver_ops->probe(pci_priv->pci_dev, pci_priv->pci_device_id); Loading @@ -687,6 +1038,8 @@ bypass_fbc: goto stop_mhi; } set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); return 0; stop_mhi: Loading @@ -711,6 +1064,9 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) if (!plat_priv->driver_ops) return -EINVAL; if (enable_waltest) goto bypass_driver_remove; if (plat_priv->driver_status == CNSS_LOAD_UNLOAD) { cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); plat_priv->driver_ops->remove(pci_priv->pci_dev); Loading @@ -721,6 +1077,9 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) plat_priv->driver_ops->shutdown(pci_priv->pci_dev); } clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); bypass_driver_remove: if (!fbc_bypass) cnss_pci_stop_mhi(pci_priv); Loading Loading @@ -1015,7 +1374,7 @@ static int cnss_register_ramdump(struct cnss_plat_data *plat_priv) ramdump_info->ramdump_size = ramdump_size; } cnss_pr_dbg("ramdump va: %p, pa: %pa\n", cnss_pr_dbg("ramdump va: %pK, pa: %pa\n", ramdump_info->ramdump_va, &ramdump_info->ramdump_pa); if (ramdump_info->ramdump_size == 0) { Loading Loading @@ -1111,6 +1470,11 @@ static ssize_t cnss_fs_ready_store(struct device *dev, cnss_pr_dbg("File system is ready, fs_ready is %d, count is %zu\n", fs_ready, count); if (qmi_bypass) { cnss_pr_dbg("QMI is bypassed.\n"); return count; } if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); return count; Loading Loading @@ -1153,6 +1517,29 @@ static void cnss_remove_sysfs(struct cnss_plat_data *plat_priv) device_remove_file(&plat_priv->plat_dev->dev, &dev_attr_fs_ready); } static int cnss_event_work_init(struct cnss_plat_data *plat_priv) { spin_lock_init(&plat_priv->event_lock); plat_priv->event_wq = alloc_workqueue("cnss_driver_event", WQ_UNBOUND, 1); if (!plat_priv->event_wq) { cnss_pr_err("Failed to create event workqueue!\n"); return -EFAULT; } INIT_WORK(&plat_priv->event_work, cnss_driver_event_work); INIT_LIST_HEAD(&plat_priv->event_list); init_completion(&plat_priv->fw_ready_event); return 0; } static void cnss_event_work_deinit(struct cnss_plat_data *plat_priv) { complete_all(&plat_priv->fw_ready_event); destroy_workqueue(plat_priv->event_wq); } static const struct platform_device_id cnss_platform_id_table[] = { { .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, }, { .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, }, Loading Loading @@ -1235,12 +1622,24 @@ static int cnss_probe(struct platform_device *plat_dev) if (ret) goto unreg_bus_scale; ret = cnss_event_work_init(plat_priv); if (ret) goto remove_sysfs; ret = cnss_qmi_init(plat_priv); if (ret) goto deinit_event_work; register_pm_notifier(&cnss_pm_notifier); cnss_pr_info("Platform driver probed successfully.\n"); return 0; deinit_event_work: cnss_event_work_deinit(plat_priv); remove_sysfs: cnss_remove_sysfs(plat_priv); unreg_bus_scale: cnss_unregister_bus_scale(plat_priv); unreg_ramdump: Loading @@ -1267,13 +1666,15 @@ static int cnss_remove(struct platform_device *plat_dev) struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev); unregister_pm_notifier(&cnss_pm_notifier); cnss_qmi_deinit(plat_priv); cnss_event_work_deinit(plat_priv); cnss_remove_sysfs(plat_priv); cnss_unregister_bus_scale(plat_priv); cnss_unregister_ramdump(plat_priv); cnss_unregister_subsys(plat_priv); cnss_unregister_esoc(plat_priv); cnss_put_resources(plat_priv); cnss_pci_deinit(plat_priv); cnss_put_resources(plat_priv); platform_set_drvdata(plat_dev, NULL); plat_env = NULL; Loading
drivers/net/wireless/cnss2/main.h +44 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <soc/qcom/memory_dump.h> #include <soc/qcom/subsystem_restart.h> #include "qmi.h" #define MAX_NO_OF_MAC_ADDR 4 enum cnss_dev_bus_type { Loading Loading @@ -81,6 +83,31 @@ struct cnss_wlan_mac_info { bool is_wlan_mac_set; }; struct cnss_fw_mem { size_t size; void *va; phys_addr_t pa; bool valid; }; enum cnss_driver_event_type { CNSS_DRIVER_EVENT_SERVER_ARRIVE, CNSS_DRIVER_EVENT_SERVER_EXIT, CNSS_DRIVER_EVENT_REQUEST_MEM, CNSS_DRIVER_EVENT_FW_MEM_READY, CNSS_DRIVER_EVENT_FW_READY, CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE, CNSS_DRIVER_EVENT_MAX, }; enum cnss_driver_state { CNSS_QMI_WLFW_CONNECTED, CNSS_FW_MEM_READY, CNSS_FW_READY, CNSS_COLD_BOOT_CAL_DONE, CNSS_DRIVER_PROBED, }; struct cnss_plat_data { struct platform_device *plat_dev; void *bus_priv; Loading @@ -98,10 +125,27 @@ struct cnss_plat_data { enum cnss_driver_status driver_status; uint32_t recovery_count; struct cnss_wlan_mac_info wlan_mac_info; unsigned long driver_state; struct completion fw_ready_event; 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 qmi_handle *qmi_wlfw_clnt; struct work_struct qmi_recv_msg_work; struct notifier_block qmi_wlfw_clnt_nb; struct wlfw_rf_chip_info_s_v01 chip_info; struct wlfw_rf_board_info_s_v01 board_info; struct wlfw_soc_info_s_v01 soc_info; struct wlfw_fw_version_info_s_v01 fw_version_info; struct cnss_fw_mem fw_mem; }; void *cnss_bus_dev_to_bus_priv(struct device *dev); struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev); int cnss_driver_event_post(struct cnss_plat_data *plat_priv, enum cnss_driver_event_type type, bool sync, void *data); int cnss_get_vreg(struct cnss_plat_data *plat_priv); int cnss_get_pinctrl(struct cnss_plat_data *plat_priv); int cnss_power_on_device(struct cnss_plat_data *plat_priv); Loading