From a9d924743a75ae20a72a7210f0df16280768c272 Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Fri, 29 Jul 2022 02:36:26 +0530 Subject: [PATCH] cnss2: Fix boot time secure peripheral mode issues If Device boots up in secure mode, CNSS defers PCI enumeration, calibration and host driver registration. When secure mode exits host driver gets notified from CTRL_PARAM and calls PLD API to perform previously deferred operations and enable WLAN HW. This can take some seconds and by the time again Secure mode can enter, disable WLAN and access controlled by TZ. Now Calibration completes and CNSS receives CAL_DONE indication. As part of CAL_DONE when CNSS try to power off WLAN access WLAN_EN it results in NOC error. One more scenario, where CNSS can access WLAN_EN when WLAN HW is disabled by TZ, could be when QMI message times out. Consider secure mode is entering as part of CNSS will to IDLE shutdown. During IDLE shutdown while sending MODE_OFF QMI message to FW it might wait for 10 seconds if FW does not respond. But TZ has timeout of 5 sec if framework does not respond in 5 Sec TZ will disable WLAN_EN and access control it. Now when after QMI timedout when try to access WLAN_EN it results in NOC error. Check secure peripheral state before updating WLAN_EN and maintain correct driver state in such scenario. Change-Id: I4a0892f02edb42496b9633d2d33ded26cbc08707 CRs-Fixed: 3247847 --- cnss2/debug.c | 3 +++ cnss2/main.c | 7 ++++--- cnss2/main.h | 2 ++ cnss2/pci.c | 11 ++++++++--- cnss2/power.c | 24 +++++++++++++++++++++++- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/cnss2/debug.c b/cnss2/debug.c index beeadbe3a9..5833ccf2d9 100644 --- a/cnss2/debug.c +++ b/cnss2/debug.c @@ -145,6 +145,9 @@ static int cnss_stats_show_state(struct seq_file *s, case CNSS_FS_READY: seq_puts(s, "FS READY"); continue; + case CNSS_DRIVER_REGISTERED: + seq_puts(s, "DRIVER REGISTERED"); + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/cnss2/main.c b/cnss2/main.c index 29fef131eb..d28f5b3d55 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -4038,11 +4038,11 @@ int cnss_wlan_hw_enable(void) struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL); int ret = 0; - if (test_bit(CNSS_PCI_PROBE_DONE, &plat_priv->driver_state)) - return 0; - clear_bit(CNSS_WLAN_HW_DISABLED, &plat_priv->driver_state); + if (test_bit(CNSS_PCI_PROBE_DONE, &plat_priv->driver_state)) + goto register_driver; + ret = cnss_wlan_device_init(plat_priv); if (ret) { CNSS_ASSERT(0); @@ -4054,6 +4054,7 @@ int cnss_wlan_hw_enable(void) CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START, 0, NULL); +register_driver: if (plat_priv->driver_ops) ret = cnss_wlan_register_driver(plat_priv->driver_ops); diff --git a/cnss2/main.h b/cnss2/main.h index c4710eba6e..e8d4bdfa11 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -111,6 +111,7 @@ struct cnss_pinctrl_info { struct pinctrl_state *wlan_en_active; struct pinctrl_state *wlan_en_sleep; int bt_en_gpio; + int wlan_en_gpio; int xo_clk_gpio; /*qca6490 only */ int sw_ctrl_gpio; int wlan_sw_ctrl_gpio; @@ -320,6 +321,7 @@ enum cnss_driver_state { CNSS_DRIVER_REGISTER, CNSS_WLAN_HW_DISABLED, CNSS_FS_READY = 25, + CNSS_DRIVER_REGISTERED, }; struct cnss_recovery_data { diff --git a/cnss2/pci.c b/cnss2/pci.c index c791ff8ac6..91f49acf93 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -2945,6 +2945,9 @@ static void cnss_wlan_reg_driver_work(struct work_struct *work) struct cnss_cal_info *cal_info; unsigned int timeout; + if (test_bit(CNSS_WLAN_HW_DISABLED, &plat_priv->driver_state)) + return; + if (test_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state)) { goto reg_driver; } else { @@ -2993,7 +2996,7 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) { int ret = 0; struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); - struct cnss_pci_data *pci_priv; + struct cnss_pci_data *pci_priv = plat_priv->bus_priv; const struct pci_device_id *id_table = driver_ops->id_table; unsigned int timeout; @@ -3030,8 +3033,7 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) return -EAGAIN; } - pci_priv = plat_priv->bus_priv; - if (pci_priv->driver_ops) { + if (test_bit(CNSS_DRIVER_REGISTERED, &plat_priv->driver_state)) { cnss_pr_err("Driver has already registered\n"); return -EEXIST; } @@ -3162,6 +3164,8 @@ int cnss_pci_register_driver_hdlr(struct cnss_pci_data *pci_priv, if (ret) { clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); pci_priv->driver_ops = NULL; + } else { + set_bit(CNSS_DRIVER_REGISTERED, &plat_priv->driver_state); } return ret; @@ -3174,6 +3178,7 @@ int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv) set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); cnss_pci_dev_shutdown(pci_priv); pci_priv->driver_ops = NULL; + clear_bit(CNSS_DRIVER_REGISTERED, &plat_priv->driver_state); return 0; } diff --git a/cnss2/power.c b/cnss2/power.c index 9c8e4e899c..0d7fe691dc 100644 --- a/cnss2/power.c +++ b/cnss2/power.c @@ -761,6 +761,9 @@ int cnss_get_pinctrl(struct cnss_plat_data *plat_priv) } if (of_find_property(dev->of_node, WLAN_EN_GPIO, NULL)) { + pinctrl_info->wlan_en_gpio = of_get_named_gpio(dev->of_node, + WLAN_EN_GPIO, 0); + cnss_pr_dbg("WLAN_EN GPIO: %d\n", pinctrl_info->wlan_en_gpio); pinctrl_info->wlan_en_active = pinctrl_lookup_state(pinctrl_info->pinctrl, WLAN_EN_ACTIVE); @@ -780,6 +783,8 @@ int cnss_get_pinctrl(struct cnss_plat_data *plat_priv) ret); goto out; } + } else { + pinctrl_info->wlan_en_gpio = -EINVAL; } /* Added for QCA6490 PMU delayed WLAN_EN_GPIO */ @@ -923,10 +928,18 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, goto out; } udelay(WLAN_ENABLE_DELAY); + cnss_set_xo_clk_gpio_state(plat_priv, false); + } else { + cnss_set_xo_clk_gpio_state(plat_priv, false); + goto out; } - cnss_set_xo_clk_gpio_state(plat_priv, false); } else { if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { + cnss_wlan_hw_disable_check(plat_priv); + if (test_bit(CNSS_WLAN_HW_DISABLED, &plat_priv->driver_state)) { + cnss_pr_dbg("Avoid WLAN_EN low. WLAN HW Disbaled"); + goto out; + } ret = pinctrl_select_state(pinctrl_info->pinctrl, pinctrl_info->wlan_en_sleep); if (ret) { @@ -934,9 +947,12 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, ret); goto out; } + } else { + goto out; } } + cnss_pr_dbg("WLAN_EN Value: %d\n", gpio_get_value(pinctrl_info->wlan_en_gpio)); cnss_pr_dbg("%s WLAN_EN GPIO successfully\n", state ? "Assert" : "De-assert"); @@ -1012,6 +1028,12 @@ int cnss_power_on_device(struct cnss_plat_data *plat_priv) return 0; } + cnss_wlan_hw_disable_check(plat_priv); + if (test_bit(CNSS_WLAN_HW_DISABLED, &plat_priv->driver_state)) { + cnss_pr_dbg("Avoid WLAN Power On. WLAN HW Disbaled"); + return -EINVAL; + } + ret = cnss_vreg_on_type(plat_priv, CNSS_VREG_PRIM); if (ret) { cnss_pr_err("Failed to turn on vreg, err = %d\n", ret);