[PATCH 4/5] regulator: s5m8767: Use GPIO for controlling Buck9/eMMC

From: Krzysztof Kozlowski
Date: Tue Dec 17 2013 - 08:25:12 EST


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@xxxxxxxxxxx>
Cc: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
Cc: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
---
drivers/regulator/s5m8767.c | 120 ++++++++++++++++++++++++++++++++++-
include/linux/mfd/samsung/core.h | 2 +
include/linux/mfd/samsung/s5m8767.h | 7 ++
3 files changed, 128 insertions(+), 1 deletion(-)

diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index d7164bb75d3e..86fb44c56bac 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -48,6 +48,8 @@ struct s5m8767_info {
int buck_gpios[3];
int buck_ds[3];
int buck_gpioindex;
+ bool buck9_uses_gpio;
+ int buck9_gpio;
};

struct sec_voltage_desc {
@@ -261,6 +263,43 @@ static int s5m8767_reg_disable(struct regulator_dev *rdev)
S5M8767_ENCTRL_MASK, ~S5M8767_ENCTRL_MASK);
}

+static int s5m8767_reg_gpio_is_enabled(struct regulator_dev *rdev)
+{
+ struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+ int val;
+
+ if (!s5m8767->buck9_uses_gpio)
+ return s5m8767_reg_is_enabled(rdev);
+
+ val = gpio_get_value(s5m8767->buck9_gpio);
+
+ return val == 1;
+}
+
+static int s5m8767_reg_gpio_enable(struct regulator_dev *rdev)
+{
+ struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+
+ if (!s5m8767->buck9_uses_gpio)
+ return s5m8767_reg_enable(rdev);
+
+ gpio_set_value(s5m8767->buck9_gpio, 1);
+
+ return 0;
+}
+
+static int s5m8767_reg_gpio_disable(struct regulator_dev *rdev)
+{
+ struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+
+ if (!s5m8767->buck9_uses_gpio)
+ return s5m8767_reg_disable(rdev);
+
+ gpio_set_value(s5m8767->buck9_gpio, 0);
+
+ return 0;
+}
+
static int s5m8767_get_vsel_reg(int reg_id, struct s5m8767_info *s5m8767)
{
int reg;
@@ -427,6 +466,17 @@ static struct regulator_ops s5m8767_buck78_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};

+static struct regulator_ops s5m8767_buck_gpio_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = s5m8767_reg_gpio_is_enabled,
+ .enable = s5m8767_reg_gpio_enable,
+ .disable = s5m8767_reg_gpio_disable,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = s5m8767_set_voltage_time_sel,
+};
+
+
#define s5m8767_regulator_desc(_name) { \
.name = #_name, \
.id = S5M8767_##_name, \
@@ -443,6 +493,14 @@ static struct regulator_ops s5m8767_buck78_ops = {
.owner = THIS_MODULE, \
}

+#define s5m8767_regulator_gpio_desc(_name) { \
+ .name = #_name, \
+ .id = S5M8767_##_name, \
+ .ops = &s5m8767_buck_gpio_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+}
+
static struct regulator_desc regulators[] = {
s5m8767_regulator_desc(LDO1),
s5m8767_regulator_desc(LDO2),
@@ -480,9 +538,50 @@ static struct regulator_desc regulators[] = {
s5m8767_regulator_desc(BUCK6),
s5m8767_regulator_buck78_desc(BUCK7),
s5m8767_regulator_buck78_desc(BUCK8),
- s5m8767_regulator_desc(BUCK9),
+ s5m8767_regulator_gpio_desc(BUCK9),
};

+/*
+ * Initialize BUCK9EN GPIO and BUCK9 control register.
+ * Assuming DT or platform data is already parsed and buck9_uses_gpio is true.
+ */
+static int s5m8767_init_buck9en_gpio(struct s5m8767_info *s5m8767)
+{
+ int i, ret, mode = 0;
+ unsigned int data;
+
+ /* Check if opmode for Buck9 matches pmic-buck9-uses-gpio */
+ for (i = 0; i < s5m8767->num_regulators; i++) {
+ const struct sec_opmode_data *opmode = &s5m8767->opmode[i];
+ if (opmode->id == S5M8767_BUCK9) {
+ mode = s5m8767_opmode_reg[S5M8767_BUCK9][opmode->mode];
+ break;
+ }
+ }
+ if (mode != S5M8767_ENCTRL_USE_GPIO) {
+ dev_err(s5m8767->dev,
+ "Mismatched op_mode (%x) and uses-gpio for Buck9, fallback to normal mode\n",
+ mode);
+ s5m8767->buck9_uses_gpio = false;
+ return 0;
+ }
+
+ if (!gpio_is_valid(s5m8767->buck9_gpio)) {
+ dev_err(s5m8767->dev, "Buck9 GPIO not valid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(s5m8767->dev, s5m8767->buck9_gpio,
+ GPIOF_OUT_INIT_HIGH, "S5M8767 BUCK9EN");
+ if (ret)
+ return ret;
+
+ data = S5M8767_ENCTRL_USE_GPIO << S5M8767_ENCTRL_SHIFT;
+
+ return regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK9CTRL1, S5M8767_ENCTRL_MASK, data);
+}
+
#ifdef CONFIG_OF
static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev,
struct sec_platform_data *pdata,
@@ -663,6 +762,17 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev,
pdata->buck_ramp_delay = 0;
}

+ if (of_get_property(pmic_np, "s5m8767,pmic-buck9-uses-gpio", NULL)) {
+ int gpio = of_get_named_gpio(pmic_np,
+ "s5m8767,pmic-buck9-gpio", 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(iodev->dev, "invalid buck9 gpio: %d\n", gpio);
+ return -EINVAL;
+ }
+ pdata->buck9_uses_gpio = true;
+ pdata->buck9_gpio = gpio;
+ }
+
return 0;
}
#else
@@ -740,6 +850,8 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
s5m8767->buck_ds[0] = pdata->buck_ds[0];
s5m8767->buck_ds[1] = pdata->buck_ds[1];
s5m8767->buck_ds[2] = pdata->buck_ds[2];
+ s5m8767->buck9_uses_gpio = pdata->buck9_uses_gpio;
+ s5m8767->buck9_gpio = pdata->buck9_gpio;

s5m8767->ramp_delay = pdata->buck_ramp_delay;
s5m8767->buck2_ramp = pdata->buck2_ramp_enable;
@@ -917,6 +1029,12 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
val << S5M8767_DVS_BUCK_RAMP_SHIFT);
}

+ if (s5m8767->buck9_uses_gpio) {
+ ret = s5m8767_init_buck9en_gpio(s5m8767);
+ if (ret)
+ return ret;
+ }
+
for (i = 0; i < pdata->num_regulators; i++) {
const struct sec_voltage_desc *desc;
int id = pdata->regulators[i].id;
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index 41c9bde410c5..8c52ecea3f11 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -80,6 +80,8 @@ struct sec_platform_data {
bool buck3_gpiodvs;
unsigned int buck4_voltage[8];
bool buck4_gpiodvs;
+ int buck9_gpio;
+ bool buck9_uses_gpio;

int buck_set1;
int buck_set2;
diff --git a/include/linux/mfd/samsung/s5m8767.h b/include/linux/mfd/samsung/s5m8767.h
index 2ab0b0f03641..d383c46a9ad8 100644
--- a/include/linux/mfd/samsung/s5m8767.h
+++ b/include/linux/mfd/samsung/s5m8767.h
@@ -183,8 +183,15 @@ enum s5m8767_regulators {
S5M8767_REG_MAX,
};

+/* LDO_EN/BUCK_EN field in registers */
#define S5M8767_ENCTRL_SHIFT 6
#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
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/