Merge "wcnss: Add support to auto detect Iris XO"
diff --git a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
index c130b26..6df1efe 100644
--- a/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
+++ b/Documentation/devicetree/bindings/wcnss/wcnss-wlan.txt
@@ -21,6 +21,10 @@
 - qcom,has_48mhz_xo: boolean flag to determine the usage of 24MHz XO from RF
 - qcom,has_pronto_hw: boolean flag to determine the revId of the WLAN subsystem
 
+Optional properties:
+- qcom,has_autodetect_xo: boolean flag to determine whether Iris XO auto detect
+should be performed during boot up.
+
 Example:
 
     qcom,wcnss-wlan@fb000000 {
diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c
index 59a6b68..c02daa4 100644
--- a/drivers/net/wireless/wcnss/wcnss_vreg.c
+++ b/drivers/net/wireless/wcnss/wcnss_vreg.c
@@ -31,6 +31,7 @@
 static LIST_HEAD(power_on_lock_list);
 static DEFINE_MUTEX(list_lock);
 static DEFINE_SEMAPHORE(wcnss_power_on_lock);
+static int auto_detect;
 
 #define MSM_RIVA_PHYS           0x03204000
 #define MSM_PRONTO_PHYS         0xfb21b000
@@ -42,11 +43,17 @@
 #define PRONTO_SPARE_OFFSET     0x1088
 #define NVBIN_DLND_BIT          BIT(25)
 
+#define PRONTO_IRIS_REG_READ_OFFSET       0x1134
+#define PRONTO_IRIS_REG_CHIP_ID           0x04
+
 #define WCNSS_PMU_CFG_IRIS_XO_CFG          BIT(3)
 #define WCNSS_PMU_CFG_IRIS_XO_EN           BIT(4)
 #define WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP   BIT(5)
 #define WCNSS_PMU_CFG_IRIS_XO_CFG_STS      BIT(6) /* 1: in progress, 0: done */
 
+#define WCNSS_PMU_CFG_IRIS_XO_READ         BIT(9)
+#define WCNSS_PMU_CFG_IRIS_XO_READ_STS     BIT(10)
+
 #define WCNSS_PMU_CFG_IRIS_XO_MODE         0x6
 #define WCNSS_PMU_CFG_IRIS_XO_MODE_48      (3 << 1)
 
@@ -56,6 +63,8 @@
 #define VREG_OPTIMUM_MODE_MASK      0x0004
 #define VREG_ENABLE_MASK            0x0008
 
+#define WCNSS_INVALID_IRIS_REG      0xbaadbaad
+
 struct vregs_info {
 	const char * const name;
 	int state;
@@ -110,10 +119,38 @@
 	struct list_head list;
 };
 
+enum {
+	WCNSS_XO_48MHZ = 1,
+	WCNSS_XO_19MHZ,
+	WCNSS_XO_INVALID,
+};
+
+enum {
+	IRIS_3660, /* also 3660A and 3680 */
+	IRIS_3620
+};
+
+
+int xo_auto_detect(u32 reg)
+{
+	reg >>= 30;
+
+	switch (reg) {
+	case IRIS_3660:
+		return WCNSS_XO_48MHZ;
+
+	case IRIS_3620:
+		return WCNSS_XO_19MHZ;
+
+	default:
+		return WCNSS_XO_INVALID;
+	}
+}
 
 static int configure_iris_xo(struct device *dev, bool use_48mhz_xo, int on)
 {
 	u32 reg = 0;
+	u32 iris_reg = WCNSS_INVALID_IRIS_REG;
 	int rc = 0;
 	int size = 0;
 	int pmu_offset = 0;
@@ -121,6 +158,7 @@
 	unsigned long wcnss_phys_addr;
 	void __iomem *pmu_conf_reg;
 	void __iomem *spare_reg;
+	void __iomem *iris_read_reg;
 	struct clk *clk;
 	struct clk *clk_rf = NULL;
 
@@ -136,14 +174,6 @@
 			return PTR_ERR(clk);
 		}
 
-		if (!use_48mhz_xo) {
-			clk_rf = clk_get(dev, "rf_clk");
-			if (IS_ERR(clk_rf)) {
-				pr_err("Couldn't get rf_clk\n");
-				clk_put(clk);
-				return PTR_ERR(clk_rf);
-			}
-		}
 	} else {
 		wcnss_phys_addr = MSM_RIVA_PHYS;
 		pmu_offset = RIVA_PMU_OFFSET;
@@ -187,10 +217,44 @@
 				WCNSS_PMU_CFG_IRIS_XO_EN;
 		writel_relaxed(reg, pmu_conf_reg);
 
+		if (wcnss_xo_auto_detect_enabled()) {
+			iris_read_reg = msm_wcnss_base +
+				PRONTO_IRIS_REG_READ_OFFSET;
+			iris_reg = readl_relaxed(iris_read_reg);
+		}
+
+		if (iris_reg != WCNSS_INVALID_IRIS_REG) {
+			iris_reg &= 0xffff;
+			iris_reg |= PRONTO_IRIS_REG_CHIP_ID;
+			writel_relaxed(iris_reg, iris_read_reg);
+
+			/* Iris read */
+			reg = readl_relaxed(pmu_conf_reg);
+			reg |= WCNSS_PMU_CFG_IRIS_XO_READ;
+			writel_relaxed(reg, pmu_conf_reg);
+
+			/* Wait for PMU_CFG.iris_reg_read_sts */
+			while (readl_relaxed(pmu_conf_reg) &
+					WCNSS_PMU_CFG_IRIS_XO_READ_STS)
+				cpu_relax();
+
+			iris_reg = readl_relaxed(iris_read_reg);
+			auto_detect = xo_auto_detect(iris_reg);
+
+			/* Reset iris read bit */
+			reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
+
+		} else if (wcnss_xo_auto_detect_enabled())
+			/* Default to 48 MHZ */
+			auto_detect = WCNSS_XO_48MHZ;
+		else
+			auto_detect = WCNSS_XO_INVALID;
+
 		/* Clear XO_MODE[b2:b1] bits. Clear implies 19.2 MHz TCXO */
 		reg &= ~(WCNSS_PMU_CFG_IRIS_XO_MODE);
 
-		if (use_48mhz_xo)
+		if ((use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
+				|| auto_detect ==  WCNSS_XO_48MHZ)
 			reg |= WCNSS_PMU_CFG_IRIS_XO_MODE_48;
 
 		writel_relaxed(reg, pmu_conf_reg);
@@ -210,30 +274,41 @@
 		writel_relaxed(reg, pmu_conf_reg);
 		clk_disable_unprepare(clk);
 
-		if (!use_48mhz_xo) {
+		if ((!use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
+				|| auto_detect ==  WCNSS_XO_19MHZ) {
+
+			clk_rf = clk_get(dev, "rf_clk");
+			if (IS_ERR(clk_rf)) {
+				pr_err("Couldn't get rf_clk\n");
+				goto fail;
+			}
+
 			rc = clk_prepare_enable(clk_rf);
 			if (rc) {
 				pr_err("clk_rf enable failed\n");
 				goto fail;
 			}
 		}
-	}  else if (clk_rf != NULL && !use_48mhz_xo)
-			clk_disable_unprepare(clk_rf);
+
+	}  else if ((!use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
+			|| auto_detect ==  WCNSS_XO_19MHZ) {
+		clk_rf = clk_get(dev, "rf_clk");
+		if (IS_ERR(clk_rf)) {
+			pr_err("Couldn't get rf_clk\n");
+			goto fail;
+		}
+		clk_disable_unprepare(clk_rf);
+	}
+
 	/* Add some delay for XO to settle */
 	msleep(20);
 
+fail:
 	clk_put(clk);
 
-	if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
-		if (!use_48mhz_xo)
-			clk_put(clk_rf);
-	}
-
-	return rc;
-fail:
 	if (clk_rf != NULL)
 		clk_put(clk_rf);
-	clk_put(clk);
+
 	return rc;
 }
 
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index c30f46f..a75687b 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -56,6 +56,10 @@
 module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
 MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
 
+static int has_autodetect_xo = WCNSS_CONFIG_UNSPECIFIED;
+module_param(has_autodetect_xo, int, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(has_autodetect_xo, "Perform auto detect to configure IRIS XO");
+
 static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
 module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
 MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
@@ -873,6 +877,11 @@
 module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
 		param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
 
+int wcnss_xo_auto_detect_enabled(void)
+{
+	return (has_autodetect_xo == 1 ? 1 : 0);
+}
+
 
 void wcnss_suspend_notify(void)
 {
@@ -1507,6 +1516,11 @@
 	penv->wcnss_hw_type = (has_pronto_hw) ? WCNSS_PRONTO_HW : WCNSS_RIVA_HW;
 	penv->wlan_config.use_48mhz_xo = has_48mhz_xo;
 
+	if (WCNSS_CONFIG_UNSPECIFIED == has_autodetect_xo && has_pronto_hw) {
+		has_autodetect_xo = of_property_read_bool(pdev->dev.of_node,
+									"qcom,has_autodetect_xo");
+	}
+
 	penv->thermal_mitigation = 0;
 	strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);
 
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index 4711ec8..2a53114 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -68,6 +68,7 @@
 void wcnss_pronto_log_debug_regs(void);
 int wcnss_device_ready(void);
 void wcnss_riva_dump_pmic_regs(void);
+int wcnss_xo_auto_detect_enabled(void);
 
 #define wcnss_wlan_get_drvdata(dev) dev_get_drvdata(dev)
 #define wcnss_wlan_set_drvdata(dev, data) dev_set_drvdata((dev), (data))