diff options
Diffstat (limited to 'drivers/regulator/s5m8767.c')
-rw-r--r-- | drivers/regulator/s5m8767.c | 327 |
1 files changed, 161 insertions, 166 deletions
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 4ca2db059004..290d6fc01029 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -12,7 +12,6 @@ */ #include <linux/bug.h> -#include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/slab.h> @@ -28,6 +27,7 @@ struct s5m8767_info { struct s5m87xx_dev *iodev; int num_regulators; struct regulator_dev **rdev; + struct s5m_opmode_data *opmode; int ramp_delay; bool buck2_ramp; @@ -141,9 +141,56 @@ static int s5m8767_list_voltage(struct regulator_dev *rdev, return val; } -static int s5m8767_get_register(struct regulator_dev *rdev, int *reg) +static unsigned int s5m8767_opmode_reg[][4] = { + /* {OFF, ON, LOWPOWER, SUSPEND} */ + /* LDO1 ... LDO28 */ + {0x0, 0x3, 0x2, 0x1}, /* LDO1 */ + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x0, 0x0, 0x0}, + {0x0, 0x3, 0x2, 0x1}, /* LDO5 */ + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, /* LDO10 */ + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, /* LDO15 */ + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x0, 0x0, 0x0}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, /* LDO20 */ + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x0, 0x0, 0x0}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, /* LDO25 */ + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, + {0x0, 0x3, 0x2, 0x1}, /* LDO28 */ + + /* BUCK1 ... BUCK9 */ + {0x0, 0x3, 0x1, 0x1}, /* BUCK1 */ + {0x0, 0x3, 0x1, 0x1}, + {0x0, 0x3, 0x1, 0x1}, + {0x0, 0x3, 0x1, 0x1}, + {0x0, 0x3, 0x2, 0x1}, /* BUCK5 */ + {0x0, 0x3, 0x1, 0x1}, + {0x0, 0x3, 0x1, 0x1}, + {0x0, 0x3, 0x1, 0x1}, + {0x0, 0x3, 0x1, 0x1}, /* BUCK9 */ +}; + +static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, + int *enable_ctrl) { int reg_id = rdev_get_id(rdev); + unsigned int mode; + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); switch (reg_id) { case S5M8767_LDO1 ... S5M8767_LDO2: @@ -168,6 +215,8 @@ static int s5m8767_get_register(struct regulator_dev *rdev, int *reg) return -EINVAL; } + mode = s5m8767->opmode[reg_id].mode; + *enable_ctrl = s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT; return 0; } @@ -175,10 +224,10 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) { struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int ret, reg; - int mask = 0xc0, pattern = 0xc0; + int mask = 0xc0, enable_ctrl; u8 val; - ret = s5m8767_get_register(rdev, ®); + ret = s5m8767_get_register(rdev, ®, &enable_ctrl); if (ret == -EINVAL) return 1; else if (ret) @@ -188,33 +237,33 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) if (ret) return ret; - return (val & mask) == pattern; + return (val & mask) == enable_ctrl; } static int s5m8767_reg_enable(struct regulator_dev *rdev) { struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int ret, reg; - int mask = 0xc0, pattern = 0xc0; + int mask = 0xc0, enable_ctrl; - ret = s5m8767_get_register(rdev, ®); + ret = s5m8767_get_register(rdev, ®, &enable_ctrl); if (ret) return ret; - return s5m_reg_update(s5m8767->iodev, reg, pattern, mask); + return s5m_reg_update(s5m8767->iodev, reg, enable_ctrl, mask); } static int s5m8767_reg_disable(struct regulator_dev *rdev) { struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int ret, reg; - int mask = 0xc0, pattern = 0xc0; + int mask = 0xc0, enable_ctrl; - ret = s5m8767_get_register(rdev, ®); + ret = s5m8767_get_register(rdev, ®, &enable_ctrl); if (ret) return ret; - return s5m_reg_update(s5m8767->iodev, reg, ~pattern, mask); + return s5m_reg_update(s5m8767->iodev, reg, ~mask, mask); } static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) @@ -297,7 +346,10 @@ static int s5m8767_convert_voltage_to_sel( if (max_vol < desc->min || min_vol > desc->max) return -EINVAL; - selector = (min_vol - desc->min) / desc->step; + if (min_vol < desc->min) + min_vol = desc->min; + + selector = DIV_ROUND_UP(min_vol - desc->min, desc->step); if (desc->min + desc->step * selector > max_vol) return -EINVAL; @@ -305,14 +357,33 @@ static int s5m8767_convert_voltage_to_sel( return selector; } +static inline void s5m8767_set_high(struct s5m8767_info *s5m8767) +{ + int temp_index = s5m8767->buck_gpioindex; + + gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); + gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); + gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); +} + +static inline void s5m8767_set_low(struct s5m8767_info *s5m8767) +{ + int temp_index = s5m8767->buck_gpioindex; + + gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); + gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); + gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); +} + static int s5m8767_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); const struct s5m_voltage_desc *desc; int reg_id = rdev_get_id(rdev); - int sel, reg, mask, ret; + int sel, reg, mask, ret = 0, old_index, index = 0; u8 val; + u8 *buck234_vol = NULL; switch (reg_id) { case S5M8767_LDO1 ... S5M8767_LDO28: @@ -320,6 +391,12 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, break; case S5M8767_BUCK1 ... S5M8767_BUCK6: mask = 0xff; + if (reg_id == S5M8767_BUCK2 && s5m8767->buck2_gpiodvs) + buck234_vol = &s5m8767->buck2_vol[0]; + else if (reg_id == S5M8767_BUCK3 && s5m8767->buck3_gpiodvs) + buck234_vol = &s5m8767->buck3_vol[0]; + else if (reg_id == S5M8767_BUCK4 && s5m8767->buck4_gpiodvs) + buck234_vol = &s5m8767->buck4_vol[0]; break; case S5M8767_BUCK7 ... S5M8767_BUCK8: return -EINVAL; @@ -336,102 +413,32 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, if (sel < 0) return sel; - ret = s5m8767_get_voltage_register(rdev, ®); - if (ret) - return ret; - - s5m_reg_read(s5m8767->iodev, reg, &val); - val &= ~mask; - val |= sel; - - ret = s5m_reg_write(s5m8767->iodev, reg, val); - *selector = sel; - - return ret; -} - -static inline void s5m8767_set_high(struct s5m8767_info *s5m8767) -{ - int temp_index = s5m8767->buck_gpioindex; - - gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); - gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); - gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); -} - -static inline void s5m8767_set_low(struct s5m8767_info *s5m8767) -{ - int temp_index = s5m8767->buck_gpioindex; - - gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); - gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); - gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); -} - -static int s5m8767_set_voltage_buck(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int reg_id = rdev_get_id(rdev); - const struct s5m_voltage_desc *desc; - int new_val, old_val, i = 0; - - if (reg_id < S5M8767_BUCK1 || reg_id > S5M8767_BUCK6) - return -EINVAL; - - switch (reg_id) { - case S5M8767_BUCK1: - return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); - case S5M8767_BUCK2 ... S5M8767_BUCK4: - break; - case S5M8767_BUCK5 ... S5M8767_BUCK6: - return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); - case S5M8767_BUCK9: - return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); - } + /* buck234_vol != NULL means to control buck234 voltage via DVS GPIO */ + if (buck234_vol) { + while (*buck234_vol != sel) { + buck234_vol++; + index++; + } + old_index = s5m8767->buck_gpioindex; + s5m8767->buck_gpioindex = index; + + if (index > old_index) + s5m8767_set_high(s5m8767); + else + s5m8767_set_low(s5m8767); + } else { + ret = s5m8767_get_voltage_register(rdev, ®); + if (ret) + return ret; - desc = reg_voltage_map[reg_id]; - new_val = s5m8767_convert_voltage_to_sel(desc, min_uV, max_uV); - if (new_val < 0) - return new_val; + s5m_reg_read(s5m8767->iodev, reg, &val); + val = (val & ~mask) | sel; - switch (reg_id) { - case S5M8767_BUCK2: - if (s5m8767->buck2_gpiodvs) { - while (s5m8767->buck2_vol[i] != new_val) - i++; - } else - return s5m8767_set_voltage(rdev, min_uV, - max_uV, selector); - break; - case S5M8767_BUCK3: - if (s5m8767->buck3_gpiodvs) { - while (s5m8767->buck3_vol[i] != new_val) - i++; - } else - return s5m8767_set_voltage(rdev, min_uV, - max_uV, selector); - break; - case S5M8767_BUCK4: - if (s5m8767->buck3_gpiodvs) { - while (s5m8767->buck4_vol[i] != new_val) - i++; - } else - return s5m8767_set_voltage(rdev, min_uV, - max_uV, selector); - break; + ret = s5m_reg_write(s5m8767->iodev, reg, val); } - old_val = s5m8767->buck_gpioindex; - s5m8767->buck_gpioindex = i; - - if (i > old_val) - s5m8767_set_high(s5m8767); - else - s5m8767_set_low(s5m8767); - - *selector = new_val; - return 0; + *selector = sel; + return ret; } static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, @@ -450,7 +457,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, return 0; } -static struct regulator_ops s5m8767_ldo_ops = { +static struct regulator_ops s5m8767_ops = { .list_voltage = s5m8767_list_voltage, .is_enabled = s5m8767_reg_is_enabled, .enable = s5m8767_reg_enable, @@ -460,75 +467,59 @@ static struct regulator_ops s5m8767_ldo_ops = { .set_voltage_time_sel = s5m8767_set_voltage_time_sel, }; -static struct regulator_ops s5m8767_buck_ops = { - .list_voltage = s5m8767_list_voltage, - .is_enabled = s5m8767_reg_is_enabled, - .enable = s5m8767_reg_enable, - .disable = s5m8767_reg_disable, - .get_voltage_sel = s5m8767_get_voltage_sel, - .set_voltage = s5m8767_set_voltage_buck, - .set_voltage_time_sel = s5m8767_set_voltage_time_sel, -}; - -#define regulator_desc_ldo(num) { \ - .name = "LDO"#num, \ - .id = S5M8767_LDO##num, \ - .ops = &s5m8767_ldo_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ -} -#define regulator_desc_buck(num) { \ - .name = "BUCK"#num, \ - .id = S5M8767_BUCK##num, \ - .ops = &s5m8767_buck_ops, \ +#define s5m8767_regulator_desc(_name) { \ + .name = #_name, \ + .id = S5M8767_##_name, \ + .ops = &s5m8767_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ } static struct regulator_desc regulators[] = { - regulator_desc_ldo(1), - regulator_desc_ldo(2), - regulator_desc_ldo(3), - regulator_desc_ldo(4), - regulator_desc_ldo(5), - regulator_desc_ldo(6), - regulator_desc_ldo(7), - regulator_desc_ldo(8), - regulator_desc_ldo(9), - regulator_desc_ldo(10), - regulator_desc_ldo(11), - regulator_desc_ldo(12), - regulator_desc_ldo(13), - regulator_desc_ldo(14), - regulator_desc_ldo(15), - regulator_desc_ldo(16), - regulator_desc_ldo(17), - regulator_desc_ldo(18), - regulator_desc_ldo(19), - regulator_desc_ldo(20), - regulator_desc_ldo(21), - regulator_desc_ldo(22), - regulator_desc_ldo(23), - regulator_desc_ldo(24), - regulator_desc_ldo(25), - regulator_desc_ldo(26), - regulator_desc_ldo(27), - regulator_desc_ldo(28), - regulator_desc_buck(1), - regulator_desc_buck(2), - regulator_desc_buck(3), - regulator_desc_buck(4), - regulator_desc_buck(5), - regulator_desc_buck(6), - regulator_desc_buck(7), - regulator_desc_buck(8), - regulator_desc_buck(9), + s5m8767_regulator_desc(LDO1), + s5m8767_regulator_desc(LDO2), + s5m8767_regulator_desc(LDO3), + s5m8767_regulator_desc(LDO4), + s5m8767_regulator_desc(LDO5), + s5m8767_regulator_desc(LDO6), + s5m8767_regulator_desc(LDO7), + s5m8767_regulator_desc(LDO8), + s5m8767_regulator_desc(LDO9), + s5m8767_regulator_desc(LDO10), + s5m8767_regulator_desc(LDO11), + s5m8767_regulator_desc(LDO12), + s5m8767_regulator_desc(LDO13), + s5m8767_regulator_desc(LDO14), + s5m8767_regulator_desc(LDO15), + s5m8767_regulator_desc(LDO16), + s5m8767_regulator_desc(LDO17), + s5m8767_regulator_desc(LDO18), + s5m8767_regulator_desc(LDO19), + s5m8767_regulator_desc(LDO20), + s5m8767_regulator_desc(LDO21), + s5m8767_regulator_desc(LDO22), + s5m8767_regulator_desc(LDO23), + s5m8767_regulator_desc(LDO24), + s5m8767_regulator_desc(LDO25), + s5m8767_regulator_desc(LDO26), + s5m8767_regulator_desc(LDO27), + s5m8767_regulator_desc(LDO28), + s5m8767_regulator_desc(BUCK1), + s5m8767_regulator_desc(BUCK2), + s5m8767_regulator_desc(BUCK3), + s5m8767_regulator_desc(BUCK4), + s5m8767_regulator_desc(BUCK5), + s5m8767_regulator_desc(BUCK6), + s5m8767_regulator_desc(BUCK7), + s5m8767_regulator_desc(BUCK8), + s5m8767_regulator_desc(BUCK9), }; static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) { struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; int i, ret, size; @@ -586,6 +577,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck2_ramp = pdata->buck2_ramp_enable; s5m8767->buck3_ramp = pdata->buck3_ramp_enable; s5m8767->buck4_ramp = pdata->buck4_ramp_enable; + s5m8767->opmode = pdata->opmode; for (i = 0; i < 8; i++) { if (s5m8767->buck2_gpiodvs) { @@ -723,8 +715,11 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) regulators[id].n_voltages = (desc->max - desc->min) / desc->step + 1; - rdev[i] = regulator_register(®ulators[id], s5m8767->dev, - pdata->regulators[i].initdata, s5m8767, NULL); + config.dev = s5m8767->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = s5m8767; + + rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(s5m8767->dev, "regulator init failed for %d\n", |