From f6efedba754b5dd43e34947bb326ab5012adc35f Mon Sep 17 00:00:00 2001 From: Manikandan Mohan Date: Fri, 10 Apr 2020 15:03:58 -0700 Subject: [PATCH] cnss2: Allow WLAN driver register after cold boot calibration When cold boot calibration is enabled, it is the first step in WLAN initialization. Cold boot calibration is started post file system mount from init.target.rc and wlan driver is loaded after that. This update is to reject wlan driver register if it is loaded before cold boot calibration is complete. This ensures WLAN init sequence triggered from init.target.rc is followed and wlan driver load from early boot is rejected. Change-Id: Id2d7b6f9ceed0d6d648012ad6d0c24bba4d09854 Signed-off-by: Manikandan Mohan --- drivers/net/wireless/cnss2/debug.c | 7 ++++-- drivers/net/wireless/cnss2/main.c | 40 ++++++++++++++++++++++-------- drivers/net/wireless/cnss2/main.h | 15 +++++++---- drivers/net/wireless/cnss2/pci.c | 33 ++++++++++++++++-------- drivers/net/wireless/cnss2/qmi.c | 18 +++++++++++--- 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index 1d6c3748ff8c..4d10d1aa25a9 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -71,8 +71,8 @@ static int cnss_stats_show_state(struct seq_file *s, case CNSS_FW_READY: seq_puts(s, "FW_READY"); continue; - case CNSS_COLD_BOOT_CAL: - seq_puts(s, "COLD_BOOT_CAL"); + case CNSS_IN_COLD_BOOT_CAL: + seq_puts(s, "IN_COLD_BOOT_CAL"); continue; case CNSS_DRIVER_LOADING: seq_puts(s, "DRIVER_LOADING"); @@ -113,6 +113,9 @@ static int cnss_stats_show_state(struct seq_file *s, case CNSS_IN_REBOOT: seq_puts(s, "IN_REBOOT"); continue; + case CNSS_COLD_BOOT_CAL_DONE: + seq_puts(s, "COLD_BOOT_CAL_DONE"); + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 7a273f69e892..eef8058e4638 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -390,6 +390,7 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) if (!plat_priv) return -ENODEV; + cnss_pr_dbg("Processing FW Init Done..\n"); del_timer(&plat_priv->fw_boot_timer); set_bit(CNSS_FW_READY, &plat_priv->driver_state); clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); @@ -402,7 +403,7 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) if (test_bit(ENABLE_WALTEST, &plat_priv->ctrl_params.quirks)) { ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_WALTEST); - } else if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) { + } else if (test_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state)) { cnss_request_antenna_sharing(plat_priv); ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_CALIBRATION); @@ -1273,19 +1274,30 @@ static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; - if (test_bit(CNSS_FW_READY, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { - cnss_pr_dbg("Device is already active, ignore calibration\n"); + if (test_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state)) { + cnss_pr_dbg("Calibration complete. Ignore calibration req\n"); goto out; } - set_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); + if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state) || + test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("WLAN in mission mode before cold boot calibration\n"); + CNSS_ASSERT(0); + return -EINVAL; + } + + set_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state); reinit_completion(&plat_priv->cal_complete); ret = cnss_bus_dev_powerup(plat_priv); if (ret) { complete(&plat_priv->cal_complete); - clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); + clear_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state); + /* Set CBC done in driver state to mark attempt and note error + * since calibration cannot be retried at boot. + */ + plat_priv->cal_done = CNSS_CAL_FAILURE; + set_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state); } out: @@ -1297,7 +1309,7 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv, { struct cnss_cal_info *cal_info = data; - if (!test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) + if (!test_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state)) goto out; switch (cal_info->cal_status) { @@ -1306,7 +1318,9 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv, plat_priv->cal_done = true; break; case CNSS_CAL_TIMEOUT: - cnss_pr_dbg("Calibration timed out, force shutdown\n"); + case CNSS_CAL_FAILURE: + cnss_pr_dbg("Calibration failed. Status: %d, force shutdown\n", + cal_info->cal_status); break; default: cnss_pr_err("Unknown calibration status: %u\n", @@ -1320,7 +1334,8 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv, cnss_bus_dev_shutdown(plat_priv); msleep(COLD_BOOT_CAL_SHUTDOWN_DELAY_MS); complete(&plat_priv->cal_complete); - clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); + clear_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state); + set_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state); out: kfree(data); @@ -2022,7 +2037,7 @@ static ssize_t fs_ready_store(struct device *dev, return count; } - if (fs_ready == FILE_SYSTEM_READY) { + if (fs_ready == FILE_SYSTEM_READY && plat_priv->cbc_enabled) { cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START, 0, NULL); @@ -2179,6 +2194,9 @@ static void cnss_init_control_params(struct cnss_plat_data *plat_priv) "cnss-daemon-support")) plat_priv->ctrl_params.quirks |= BIT(ENABLE_DAEMON_SUPPORT); + plat_priv->cbc_enabled = + of_property_read_bool(plat_priv->plat_dev->dev.of_node, + "qcom,wlan-cbc-enabled"); plat_priv->ctrl_params.mhi_timeout = CNSS_MHI_TIMEOUT_DEFAULT; plat_priv->ctrl_params.mhi_m2_timeout = CNSS_MHI_M2_TIMEOUT_DEFAULT; plat_priv->ctrl_params.qmi_timeout = CNSS_QMI_TIMEOUT_DEFAULT; diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 0302ea83a7b7..8d0a94092b19 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -25,6 +25,8 @@ #define CNSS_RDDM_TIMEOUT_MS 20000 #define RECOVERY_TIMEOUT 60000 #define WLAN_WD_TIMEOUT_MS 60000 +#define WLAN_COLD_BOOT_CAL_TIMEOUT 60000 +#define WLAN_DRIVER_LOAD_TIMEOUT 90000 #define TIME_CLOCK_FREQ_HZ 19200000 #define CNSS_RAMDUMP_MAGIC 0x574C414E #define CNSS_RAMDUMP_VERSION 0 @@ -218,23 +220,24 @@ enum cnss_driver_event_type { }; enum cnss_driver_state { - CNSS_QMI_WLFW_CONNECTED, + CNSS_QMI_WLFW_CONNECTED = 0, CNSS_FW_MEM_READY, CNSS_FW_READY, - CNSS_COLD_BOOT_CAL, + CNSS_IN_COLD_BOOT_CAL, CNSS_DRIVER_LOADING, - CNSS_DRIVER_UNLOADING, + CNSS_DRIVER_UNLOADING = 5, CNSS_DRIVER_IDLE_RESTART, CNSS_DRIVER_IDLE_SHUTDOWN, CNSS_DRIVER_PROBED, CNSS_DRIVER_RECOVERY, - CNSS_FW_BOOT_RECOVERY, + CNSS_FW_BOOT_RECOVERY = 10, CNSS_DEV_ERR_NOTIFY, CNSS_DRIVER_DEBUG, CNSS_COEX_CONNECTED, CNSS_IMS_CONNECTED, - CNSS_IN_SUSPEND_RESUME, + CNSS_IN_SUSPEND_RESUME = 15, CNSS_IN_REBOOT, + CNSS_COLD_BOOT_CAL_DONE, }; struct cnss_recovery_data { @@ -285,6 +288,7 @@ enum cnss_bdf_type { enum cnss_cal_status { CNSS_CAL_DONE, CNSS_CAL_TIMEOUT, + CNSS_CAL_FAILURE, }; struct cnss_cal_info { @@ -393,6 +397,7 @@ struct cnss_plat_data { u64 dynamic_feature; void *get_info_cb_ctx; int (*get_info_cb)(void *ctx, void *event, int event_len); + bool cbc_enabled; u8 use_nv_mac; u8 set_wlaon_pwr_ctrl; struct kobject *shutdown_kobj; diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 7129d01f23a1..7f6df8df9536 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -1493,7 +1493,7 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) plat_priv = pci_priv->plat_priv; - if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || + if (test_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state) || test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { cnss_pr_dbg("Skip driver remove\n"); @@ -1839,10 +1839,12 @@ retry: if (ret) goto stop_mhi; } else if (timeout) { - if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) - timeout = timeout << 1; + if (test_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state)) + timeout += WLAN_COLD_BOOT_CAL_TIMEOUT; + else + timeout += WLAN_DRIVER_LOAD_TIMEOUT; mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(timeout << 1)); + jiffies + msecs_to_jiffies(timeout)); } return 0; @@ -2129,14 +2131,26 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) return -EEXIST; } - if (!test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) + if (!plat_priv->cbc_enabled || + test_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state)) goto register_driver; - cnss_pr_dbg("Start to wait for calibration to complete\n"); + /* If enabled Cold Boot Calibration is the 1st step in init sequence. + * CBC is done on file system_ready trigger. Qcacld should be loaded + * from init.target.rc after that. Reject qcacld load from + * vendor_modprobe.sh at early boot to satisfy this requirement. + */ + if (test_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state)) { + cnss_pr_dbg("Start to wait for calibration to complete\n"); + } else { + cnss_pr_err("Reject WLAN Driver insmod before CBC\n"); + return -EPERM; + } timeout = cnss_get_boot_timeout(&pci_priv->pci_dev->dev); ret = wait_for_completion_timeout(&plat_priv->cal_complete, - msecs_to_jiffies(timeout) << 2); + WLAN_COLD_BOOT_CAL_TIMEOUT + + msecs_to_jiffies(timeout)); if (!ret) { cnss_pr_err("Timeout waiting for calibration to complete\n"); if (!test_bit(CNSS_IN_REBOOT, &plat_priv->driver_state)) @@ -2152,12 +2166,11 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) 0, cal_info); } +register_driver: if (test_bit(CNSS_IN_REBOOT, &plat_priv->driver_state)) { cnss_pr_dbg("Reboot or shutdown is in progress, ignore register driver\n"); return -EINVAL; } - -register_driver: reinit_completion(&plat_priv->power_up_complete); ret = cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_REGISTER_DRIVER, @@ -3218,7 +3231,7 @@ void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) if (!plat_priv) return; - if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) { + if (test_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state)) { cnss_pr_dbg("Ignore FW ready timeout for calibration mode\n"); return; } diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index 4c26d37ffa26..c4f3f42a38ab 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -78,12 +78,11 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) req->client_id_valid = 1; req->client_id = WLFW_CLIENT_ID; - req->fw_ready_enable_valid = 1; - req->fw_ready_enable = 1; req->request_mem_enable_valid = 1; req->request_mem_enable = 1; req->fw_mem_ready_enable_valid = 1; req->fw_mem_ready_enable = 1; + /* fw_ready indication is replaced by fw_init_done in HST/HSP */ req->fw_init_done_enable_valid = 1; req->fw_init_done_enable = 1; req->pin_connect_result_enable_valid = 1; @@ -1776,6 +1775,12 @@ static void cnss_wlfw_fw_mem_ready_ind_cb(struct qmi_handle *qmi_wlfw, 0, NULL); } +/** + * cnss_wlfw_fw_ready_ind_cb: FW ready indication handler (Helium arch) + * + * This event is not required for HST/ HSP as FW calibration done is + * provided in QMI_WLFW_CAL_DONE_IND_V01 + */ static void cnss_wlfw_fw_ready_ind_cb(struct qmi_handle *qmi_wlfw, struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *data) @@ -1784,13 +1789,18 @@ static void cnss_wlfw_fw_ready_ind_cb(struct qmi_handle *qmi_wlfw, container_of(qmi_wlfw, struct cnss_plat_data, qmi_wlfw); struct cnss_cal_info *cal_info; - cnss_pr_dbg("Received QMI WLFW FW ready indication\n"); - if (!txn) { cnss_pr_err("Spurious indication\n"); return; } + if (plat_priv->device_id == QCA6390_DEVICE_ID || + plat_priv->device_id == QCA6490_DEVICE_ID) { + cnss_pr_dbg("Ignore FW Ready Indication for HST/HSP"); + return; + } + + cnss_pr_dbg("Received QMI WLFW FW ready indication.\n"); cal_info = kzalloc(sizeof(*cal_info), GFP_KERNEL); if (!cal_info) return;