Procházet zdrojové kódy

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
Naman Padhiar před 2 roky
rodič
revize
a9d924743a
5 změnil soubory, kde provedl 40 přidání a 7 odebrání
  1. 3 0
      cnss2/debug.c
  2. 4 3
      cnss2/main.c
  3. 2 0
      cnss2/main.h
  4. 8 3
      cnss2/pci.c
  5. 23 1
      cnss2/power.c

+ 3 - 0
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);

+ 4 - 3
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);
 

+ 2 - 0
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 {

+ 8 - 3
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;
 }

+ 23 - 1
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);