regulator: s5m8767: Use GPIO for controlling Buck9/eMMC

Add support for GPIO control (enable/disable) over Buck9. The Buck9
Converter is used as a supply for eMMC Host Controller.

BUCK9EN GPIO of S5M8767 chip may be used by application processor to
enable or disable the Buck9. This has two benefits:
 - It is faster than toggling it over I2C bus.
 - It allows disabling the regulator during suspend to RAM; The AP will
   enable it during resume; Without the patch the regulator supplying
   eMMC must be defined as fixed-regulator.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
Krzysztof Kozlowski 2014-01-24 14:37:57 +01:00 committed by Mark Brown
parent 07b1980848
commit ee1e0994ab
3 changed files with 93 additions and 4 deletions

View File

@ -11,11 +11,8 @@
* *
*/ */
#include <linux/bug.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
@ -483,6 +480,65 @@ static struct regulator_desc regulators[] = {
s5m8767_regulator_desc(BUCK9), s5m8767_regulator_desc(BUCK9),
}; };
/*
* Enable GPIO control over BUCK9 in regulator_config for that regulator.
*/
static void s5m8767_regulator_config_ext_control(struct s5m8767_info *s5m8767,
struct sec_regulator_data *rdata,
struct regulator_config *config)
{
int i, mode = 0;
if (rdata->id != S5M8767_BUCK9)
return;
/* Check if opmode for regulator matches S5M8767_ENCTRL_USE_GPIO */
for (i = 0; i < s5m8767->num_regulators; i++) {
const struct sec_opmode_data *opmode = &s5m8767->opmode[i];
if (opmode->id == rdata->id) {
mode = s5m8767_opmode_reg[rdata->id][opmode->mode];
break;
}
}
if (mode != S5M8767_ENCTRL_USE_GPIO) {
dev_warn(s5m8767->dev,
"ext-control for %s: mismatched op_mode (%x), ignoring\n",
rdata->reg_node->name, mode);
return;
}
if (!gpio_is_valid(rdata->ext_control_gpio)) {
dev_warn(s5m8767->dev,
"ext-control for %s: GPIO not valid, ignoring\n",
rdata->reg_node->name);
return;
}
config->ena_gpio = rdata->ext_control_gpio;
config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
}
/*
* Turn on GPIO control over BUCK9.
*/
static int s5m8767_enable_ext_control(struct s5m8767_info *s5m8767,
struct regulator_dev *rdev)
{
int ret, reg, enable_ctrl;
if (rdev_get_id(rdev) != S5M8767_BUCK9)
return -EINVAL;
ret = s5m8767_get_register(rdev, &reg, &enable_ctrl);
if (ret)
return ret;
return regmap_update_bits(s5m8767->iodev->regmap_pmic,
reg, S5M8767_ENCTRL_MASK,
S5M8767_ENCTRL_USE_GPIO << S5M8767_ENCTRL_SHIFT);
}
#ifdef CONFIG_OF #ifdef CONFIG_OF
static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev,
struct sec_platform_data *pdata, struct sec_platform_data *pdata,
@ -520,6 +576,16 @@ static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev,
return 0; return 0;
} }
static void s5m8767_pmic_dt_parse_ext_control_gpio(struct sec_pmic_dev *iodev,
struct sec_regulator_data *rdata,
struct device_node *reg_np)
{
rdata->ext_control_gpio = of_get_named_gpio(reg_np,
"s5m8767,pmic-ext-control-gpios", 0);
if (!gpio_is_valid(rdata->ext_control_gpio))
rdata->ext_control_gpio = 0;
}
static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev,
struct sec_platform_data *pdata) struct sec_platform_data *pdata)
{ {
@ -574,6 +640,8 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev,
continue; continue;
} }
s5m8767_pmic_dt_parse_ext_control_gpio(iodev, rdata, reg_np);
rdata->id = i; rdata->id = i;
rdata->initdata = of_get_regulator_init_data( rdata->initdata = of_get_regulator_init_data(
&pdev->dev, reg_np); &pdev->dev, reg_np);
@ -940,6 +1008,9 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
config.driver_data = s5m8767; config.driver_data = s5m8767;
config.regmap = iodev->regmap_pmic; config.regmap = iodev->regmap_pmic;
config.of_node = pdata->regulators[i].reg_node; config.of_node = pdata->regulators[i].reg_node;
if (pdata->regulators[i].ext_control_gpio)
s5m8767_regulator_config_ext_control(s5m8767,
&pdata->regulators[i], &config);
rdev[i] = devm_regulator_register(&pdev->dev, &regulators[id], rdev[i] = devm_regulator_register(&pdev->dev, &regulators[id],
&config); &config);
@ -949,6 +1020,16 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
id); id);
return ret; return ret;
} }
if (pdata->regulators[i].ext_control_gpio) {
ret = s5m8767_enable_ext_control(s5m8767, rdev[i]);
if (ret < 0) {
dev_err(s5m8767->dev,
"failed to enable gpio control over %s: %d\n",
rdev[i]->desc->name, ret);
return ret;
}
}
} }
return 0; return 0;

View File

@ -119,7 +119,8 @@ struct sec_platform_data {
struct sec_regulator_data { struct sec_regulator_data {
int id; int id;
struct regulator_init_data *initdata; struct regulator_init_data *initdata;
struct device_node *reg_node; struct device_node *reg_node;
int ext_control_gpio;
}; };
/* /*

View File

@ -183,9 +183,16 @@ enum s5m8767_regulators {
S5M8767_REG_MAX, S5M8767_REG_MAX,
}; };
/* LDO_EN/BUCK_EN field in registers */
#define S5M8767_ENCTRL_SHIFT 6 #define S5M8767_ENCTRL_SHIFT 6
#define S5M8767_ENCTRL_MASK (0x3 << S5M8767_ENCTRL_SHIFT) #define S5M8767_ENCTRL_MASK (0x3 << S5M8767_ENCTRL_SHIFT)
/*
* LDO_EN/BUCK_EN register value for controlling this Buck or LDO
* by GPIO (PWREN, BUCKEN).
*/
#define S5M8767_ENCTRL_USE_GPIO 0x1
/* /*
* Values for BUCK_RAMP field in DVS_RAMP register, matching raw values * Values for BUCK_RAMP field in DVS_RAMP register, matching raw values
* in mV/us. * in mV/us.