diff options
-rw-r--r-- | drivers/net/wireless/ath/wcn36xx/wcnss_core.c | 145 |
1 files changed, 144 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/wcn36xx/wcnss_core.c b/drivers/net/wireless/ath/wcn36xx/wcnss_core.c index 69aaf115733d..9d94d76ed05c 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcnss_core.c +++ b/drivers/net/wireless/ath/wcn36xx/wcnss_core.c @@ -12,12 +12,141 @@ #include "wcn36xx.h" #include "wcnss_core.h" +#define VREG_NULL_CONFIG 0x0000 +#define VREG_GET_REGULATOR_MASK 0x0001 +#define VREG_SET_VOLTAGE_MASK 0x0002 +#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; + const int nominal_min; + const int low_power_min; + const int max_voltage; + const int uA_load; + struct regulator *regulator; +}; + +/* IRIS regulators for Pronto v2 hardware */ +static struct vregs_info iris_vregs_pronto_v2[] = { + {"qcom,iris-vddxo", VREG_NULL_CONFIG, 1800000, 0, + 1800000, 10000, NULL}, + {"qcom,iris-vddrfa", VREG_NULL_CONFIG, 1300000, 0, + 1300000, 100000, NULL}, + {"qcom,iris-vddpa", VREG_NULL_CONFIG, 3300000, 0, + 3300000, 515000, NULL}, + {"qcom,iris-vdddig", VREG_NULL_CONFIG, 1800000, 0, + 1800000, 10000, NULL}, +}; + +/* WCNSS regulators for Pronto v2 hardware */ +static struct vregs_info pronto_vregs_pronto_v2[] = { + {"qcom,pronto-vddmx", VREG_NULL_CONFIG, 1287500, 0, + 1287500, 0, NULL}, + {"qcom,pronto-vddcx", VREG_NULL_CONFIG, RPM_REGULATOR_CORNER_NORMAL, + RPM_REGULATOR_CORNER_NONE, RPM_REGULATOR_CORNER_SUPER_TURBO, + 0, NULL}, + {"qcom,pronto-vddpx", VREG_NULL_CONFIG, 1800000, 0, + 1800000, 0, NULL}, +}; + +/* Common helper routine to turn on all WCNSS & IRIS vregs */ +static int wcnss_vregs_on(struct device *dev, + struct vregs_info regulators[], uint size) +{ + int i, rc = 0, reg_cnt; + + for (i = 0; i < size; i++) { + /* Get regulator source */ + regulators[i].regulator = + regulator_get(dev, regulators[i].name); + if (IS_ERR(regulators[i].regulator)) { + rc = PTR_ERR(regulators[i].regulator); + pr_err("regulator get of %s failed (%d)\n", + regulators[i].name, rc); + goto fail; + } + regulators[i].state |= VREG_GET_REGULATOR_MASK; + reg_cnt = regulator_count_voltages(regulators[i].regulator); + /* Set voltage to nominal. Exclude swtiches e.g. LVS */ + if ((regulators[i].nominal_min || regulators[i].max_voltage) + && (reg_cnt > 0)) { + rc = regulator_set_voltage(regulators[i].regulator, + regulators[i].nominal_min, + regulators[i].max_voltage); + if (rc) { + pr_err("regulator_set_voltage(%s) failed (%d)\n", + regulators[i].name, rc); + goto fail; + } + regulators[i].state |= VREG_SET_VOLTAGE_MASK; + } + + /* Vote for PWM/PFM mode if needed */ + if (regulators[i].uA_load && (reg_cnt > 0)) { + rc = regulator_set_optimum_mode(regulators[i].regulator, + regulators[i].uA_load); + if (rc < 0) { + pr_err("regulator_set_optimum_mode(%s) failed (%d)\n", + regulators[i].name, rc); + goto fail; + } + regulators[i].state |= VREG_OPTIMUM_MODE_MASK; + } + + /* Enable the regulator */ + rc = regulator_enable(regulators[i].regulator); + if (rc) { + pr_err("vreg %s enable failed (%d)\n", + regulators[i].name, rc); + goto fail; + } + regulators[i].state |= VREG_ENABLE_MASK; + } + + return rc; + +fail: + return rc; + +} + static int wcnss_core_config(struct platform_device *pdev, void __iomem *base) { int ret = 0; u32 value, iris_read_v = INVALID_IRIS_REG; + struct clk *clk_rf = NULL; + struct clk *clk_xo = NULL; int clk_48m = 0; + clk_xo = clk_get(&pdev->dev, "xo"); + if (IS_ERR(clk_xo)) { + pr_err("Couldn't get xo clock\n"); + return PTR_ERR(clk_xo); + } + ret = clk_prepare_enable(clk_xo); + if (ret) { + pr_err("xo clk enable failed\n"); + return ret; + } + + ret = wcnss_vregs_on(&pdev->dev, pronto_vregs_pronto_v2, + ARRAY_SIZE(pronto_vregs_pronto_v2)); + if (ret) { + pr_info("pronto_vreg turn on failed\n"); + return ret; + } + + ret = wcnss_vregs_on(&pdev->dev, iris_vregs_pronto_v2, + ARRAY_SIZE(iris_vregs_pronto_v2)); + if (ret) { + pr_info("iris_vreg turn on failed\n"); + return ret; + } + value = readl_relaxed(base + SPARE_OFFSET); value |= WCNSS_FW_DOWNLOAD_ENABLE; writel_relaxed(value, base + SPARE_OFFSET); @@ -82,7 +211,21 @@ static int wcnss_core_config(struct platform_device *pdev, void __iomem *base) WCNSS_PMU_CFG_IRIS_XO_CFG); writel_relaxed(value, base + PMU_OFFSET); - msleep(1000); + if (!clk_48m) { + clk_rf = clk_get(&pdev->dev, "rf_clk"); + if (IS_ERR(clk_rf)) { + pr_err("couldn't get rf_clk\n"); + return -1; + } + + ret = clk_prepare_enable(clk_rf); + if (ret) { + pr_err("clk_rf enable failed\n"); + } + clk_put(clk_rf); + } + + msleep(20); return ret; } |