msm_11ad: add VDD and VDDIO regulators voting

On platforms where the power supply for 11AD is external
the wil6210 driver has to vote for the VDD and VDDIO regulators.

This patch adds such voting and guarantees the required
voltage for each regulator.

Change-Id: I472fe6b0600557bc4e623a3dd1b6352fd4a86e27
[merez@codeaurora.org: using regulator_set_load]
Signed-off-by: Maya Erez <merez@codeaurora.org>
This commit is contained in:
Maya Erez 2016-02-15 12:14:01 +02:00 committed by David Keitel
parent add338b4a5
commit 5e89efd09c
2 changed files with 270 additions and 0 deletions

View file

@ -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>;
};

View file

@ -24,6 +24,7 @@
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/ramdump.h>
#include <soc/qcom/memory_dump.h>
#include <linux/regulator/consumer.h>
#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;
}