diff --git a/Documentation/devicetree/bindings/arm/msm/wil6210.txt b/Documentation/devicetree/bindings/arm/msm/wil6210.txt index 599edf47a463..1840965b978f 100644 --- a/Documentation/devicetree/bindings/arm/msm/wil6210.txt +++ b/Documentation/devicetree/bindings/arm/msm/wil6210.txt @@ -21,6 +21,9 @@ Required properties: Optional properties: - qcom,sleep-clk-en: GPIO for sleep clock used for low power modes by 11ad card - qcom,wigig-en: Enable GPIO connected to 11ad card +- qcom,use-ext-supply: Boolean flag to indicate if 11ad SIP uses external power supply +- vdd-supply: phandle to 11ad VDD regulator node +- vddio-supply: phandle to 11ad VDDIO regulator node Example: wil6210: qcom,wil6210 { @@ -34,5 +37,8 @@ Example: qcom,msm-bus,vectors-KBps = <100 512 0 0>, <100 512 600000 800000>; /* ~4.6Gbps (MCS12) */ + qcom,use-ext-supply; + vdd-supply= <&pm8998_s7>; + vddio-supply= <&pm8998_s5>; }; diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index 28aeb489c13d..ec2e15e9478a 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "wil_platform.h" #include "msm_11ad.h" @@ -42,12 +43,27 @@ #define WIGIG_RAMDUMP_SIZE 0x200000 /* maximum ramdump size */ #define WIGIG_DUMP_FORMAT_VER 0x1 #define WIGIG_DUMP_MAGIC_VER_V1 0x57474947 +#define VDD_MIN_UV 1028000 +#define VDD_MAX_UV 1028000 +#define VDD_MAX_UA 575000 +#define VDDIO_MIN_UV 1950000 +#define VDDIO_MAX_UV 2040000 +#define VDDIO_MAX_UA 70300 struct device; static const char * const gpio_en_name = "qcom,wigig-en"; static const char * const sleep_clk_en_name = "qcom,sleep-clk-en"; +struct msm11ad_vreg { + const char *name; + struct regulator *reg; + int max_uA; + int min_uV; + int max_uV; + bool enabled; +}; + struct msm11ad_ctx { struct list_head list; struct device *dev; /* for platform device */ @@ -79,6 +95,8 @@ struct msm11ad_ctx { void *ramdump_addr; struct msm_dump_data dump_data; struct ramdump_device *ramdump_dev; + struct msm11ad_vreg vdd; + struct msm11ad_vreg vddio; }; static LIST_HEAD(dev_list); @@ -94,6 +112,218 @@ static struct msm11ad_ctx *pcidev2ctx(struct pci_dev *pcidev) return NULL; } +static int msm_11ad_init_vreg(struct device *dev, + struct msm11ad_vreg *vreg, const char *name) +{ + int rc = 0; + + if (!vreg) + return 0; + + vreg->name = kstrdup(name, GFP_KERNEL); + if (!vreg->name) + return -ENOMEM; + + vreg->reg = devm_regulator_get(dev, name); + if (IS_ERR_OR_NULL(vreg->reg)) { + rc = PTR_ERR(vreg->reg); + dev_err(dev, "%s: failed to get %s, rc=%d\n", + __func__, name, rc); + kfree(vreg->name); + vreg->reg = NULL; + goto out; + } + + dev_info(dev, "%s: %s initialized successfully\n", __func__, name); + +out: + return rc; +} + +static int msm_11ad_release_vreg(struct device *dev, struct msm11ad_vreg *vreg) +{ + if (!vreg || !vreg->reg) + return 0; + + dev_info(dev, "%s: %s released\n", __func__, vreg->name); + + devm_regulator_put(vreg->reg); + vreg->reg = NULL; + kfree(vreg->name); + + return 0; +} + +static int msm_11ad_init_vregs(struct msm11ad_ctx *ctx) +{ + int rc; + struct device *dev = ctx->dev; + + if (!of_property_read_bool(dev->of_node, "qcom,use-ext-supply")) + return 0; + + rc = msm_11ad_init_vreg(dev, &ctx->vdd, "vdd"); + if (rc) + goto out; + + ctx->vdd.max_uV = VDD_MAX_UV; + ctx->vdd.min_uV = VDD_MIN_UV; + ctx->vdd.max_uA = VDD_MAX_UA; + + rc = msm_11ad_init_vreg(dev, &ctx->vddio, "vddio"); + if (rc) + goto vddio_fail; + + ctx->vddio.max_uV = VDDIO_MAX_UV; + ctx->vddio.min_uV = VDDIO_MIN_UV; + ctx->vddio.max_uA = VDDIO_MAX_UA; + + return rc; + +vddio_fail: + msm_11ad_release_vreg(dev, &ctx->vdd); +out: + return rc; +} + +static void msm_11ad_release_vregs(struct msm11ad_ctx *ctx) +{ + msm_11ad_release_vreg(ctx->dev, &ctx->vdd); + msm_11ad_release_vreg(ctx->dev, &ctx->vddio); +} + +static int msm_11ad_cfg_vreg(struct device *dev, + struct msm11ad_vreg *vreg, bool on) +{ + int rc = 0; + int min_uV; + int uA_load; + + if (!vreg || !vreg->reg) + goto out; + + if (regulator_count_voltages(vreg->reg) > 0) { + min_uV = on ? vreg->min_uV : 0; + rc = regulator_set_voltage(vreg->reg, min_uV, vreg->max_uV); + if (rc) { + dev_err(dev, "%s: %s set voltage failed, err=%d\n", + __func__, vreg->name, rc); + goto out; + } + uA_load = on ? vreg->max_uA : 0; + rc = regulator_set_load(vreg->reg, uA_load); + if (rc >= 0) { + /* + * regulator_set_load() returns new regulator + * mode upon success. + */ + dev_dbg(dev, + "%s: %s regulator_set_load rc(%d)\n", + __func__, vreg->name, rc); + rc = 0; + } else { + dev_err(dev, + "%s: %s set load(uA_load=%d) failed, rc=%d\n", + __func__, vreg->name, uA_load, rc); + goto out; + } + } + +out: + return rc; +} + +static int msm_11ad_enable_vreg(struct msm11ad_ctx *ctx, + struct msm11ad_vreg *vreg) +{ + struct device *dev = ctx->dev; + int rc = 0; + + if (!vreg || !vreg->reg || vreg->enabled) + goto out; + + rc = msm_11ad_cfg_vreg(dev, vreg, true); + if (rc) + goto out; + + rc = regulator_enable(vreg->reg); + if (rc) { + dev_err(dev, "%s: %s enable failed, rc=%d\n", + __func__, vreg->name, rc); + goto enable_fail; + } + + vreg->enabled = true; + + dev_info(dev, "%s: %s enabled\n", __func__, vreg->name); + + return rc; + +enable_fail: + msm_11ad_cfg_vreg(dev, vreg, false); +out: + return rc; +} + +static int msm_11ad_disable_vreg(struct msm11ad_ctx *ctx, + struct msm11ad_vreg *vreg) +{ + struct device *dev = ctx->dev; + int rc = 0; + + if (!vreg || !vreg->reg || !vreg->enabled) + goto out; + + rc = regulator_disable(vreg->reg); + if (rc) { + dev_err(dev, "%s: %s disable failed, rc=%d\n", + __func__, vreg->name, rc); + goto out; + } + + /* ignore errors on applying disable config */ + msm_11ad_cfg_vreg(dev, vreg, false); + vreg->enabled = false; + + dev_info(dev, "%s: %s disabled\n", __func__, vreg->name); + +out: + return rc; +} + +static int msm_11ad_enable_vregs(struct msm11ad_ctx *ctx) +{ + int rc = 0; + + rc = msm_11ad_enable_vreg(ctx, &ctx->vdd); + if (rc) + goto out; + + rc = msm_11ad_enable_vreg(ctx, &ctx->vddio); + if (rc) + goto vddio_fail; + + return rc; + +vddio_fail: + msm_11ad_disable_vreg(ctx, &ctx->vdd); +out: + return rc; +} + +static int msm_11ad_disable_vregs(struct msm11ad_ctx *ctx) +{ + if (!ctx->vdd.reg && !ctx->vddio.reg) + goto out; + + /* ignore errors on disable vreg */ + msm_11ad_disable_vreg(ctx, &ctx->vdd); + msm_11ad_disable_vreg(ctx, &ctx->vddio); + +out: + return 0; +} + static int ops_suspend(void *handle) { int rc; @@ -123,6 +353,9 @@ static int ops_suspend(void *handle) if (ctx->sleep_clk_en >= 0) gpio_direction_output(ctx->sleep_clk_en, 0); + + msm_11ad_disable_vregs(ctx); + return rc; } @@ -138,6 +371,13 @@ static int ops_resume(void *handle) return -ENODEV; } + rc = msm_11ad_enable_vregs(ctx); + if (rc) { + dev_err(ctx->dev, "msm_11ad_enable_vregs failed :%d\n", + rc); + return rc; + } + if (ctx->sleep_clk_en >= 0) gpio_direction_output(ctx->sleep_clk_en, 1); @@ -172,6 +412,9 @@ err_disable_power: if (ctx->sleep_clk_en >= 0) gpio_direction_output(ctx->sleep_clk_en, 0); + + msm_11ad_disable_vregs(ctx); + return rc; } @@ -456,6 +699,19 @@ static int msm_11ad_probe(struct platform_device *pdev) /*== execute ==*/ /* turn device on */ + rc = msm_11ad_init_vregs(ctx); + if (rc) { + dev_err(ctx->dev, "msm_11ad_init_vregs failed :%d\n", + rc); + return rc; + } + rc = msm_11ad_enable_vregs(ctx); + if (rc) { + dev_err(ctx->dev, "msm_11ad_enable_vregs failed :%d\n", + rc); + goto out_vreg; + } + if (ctx->gpio_en >= 0) { rc = gpio_request(ctx->gpio_en, gpio_en_name); if (rc < 0) { @@ -545,6 +801,10 @@ out_set: gpio_free(ctx->gpio_en); out_req: ctx->gpio_en = -EINVAL; + msm_11ad_disable_vregs(ctx); +out_vreg: + msm_11ad_release_vregs(ctx); + return rc; } @@ -566,6 +826,10 @@ static int msm_11ad_remove(struct platform_device *pdev) } if (ctx->sleep_clk_en >= 0) gpio_free(ctx->sleep_clk_en); + + msm_11ad_disable_vregs(ctx); + msm_11ad_release_vregs(ctx); + return 0; }