diff options
author | Guodong Xu <guodong.xu@linaro.org> | 2013-02-01 06:13:55 +0800 |
---|---|---|
committer | Guodong Xu <guodong.xu@linaro.org> | 2013-02-21 16:12:37 +0800 |
commit | a194d37e73f4d0dc3fc983acc4d936877e45ee51 (patch) | |
tree | 407c6cd4035c8cf0bb19ddbb5b17f13d8b5650f5 | |
parent | 2a80cddf306f4486a98db08811dafe7b84f41c75 (diff) |
regulator: hi6421: refactored as children of MFD device and more ops
Refactored hi6421 regulators as children of MFD device, and add more
regulator operation functions.
Signed-off-by: Guodong Xu <guodong.xu@linaro.org>
-rw-r--r-- | drivers/regulator/Kconfig | 5 | ||||
-rw-r--r-- | drivers/regulator/hi6421-regulator.c | 377 |
2 files changed, 233 insertions, 149 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index a0c63e7eaecd..55f7ca28d11f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -506,9 +506,10 @@ config REGULATOR_AS3711 config REGULATOR_HI6421 tristate "HiSilicon Hi6421 PMIC" + depends on MFD_HI6421_PMIC help - This driver provides support for the voltage regulators of the - HiSilicon Hi6421 PMIC. + This driver provides support for the voltage regulators on the + HiSilicon Hi6421 PMU / Codec IC. endif diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c index ebd3a7b49dce..89ff9fece655 100644 --- a/drivers/regulator/hi6421-regulator.c +++ b/drivers/regulator/hi6421-regulator.c @@ -2,7 +2,7 @@ * Device driver for regulators in Hi6421 IC * * Copyright (c) 2013 Linaro Ltd. - * Copyright (C) 2011 Hisilicon. + * Copyright (c) 2011 Hisilicon. * * Guodong Xu <guodong.xu@linaro.org> * @@ -33,6 +33,9 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> +#include <linux/mfd/hi6421-pmic.h> +#include <linux/delay.h> +#include <linux/time.h> struct hi6421_regulator_register_info { u32 ctrl_reg; @@ -45,84 +48,211 @@ struct hi6421_regulator_register_info { struct hi6421_regulator { const char *name; struct hi6421_regulator_register_info register_info; -/* u32 control_reg; - struct regmap *anatop; - int vol_bit_shift; - int vol_bit_width; - int min_bit_val; - int min_voltage; - int max_voltage; - */ + struct timeval last_off_time; u32 off_on_delay; + u32 eco_uA; struct regulator_desc rdesc; int (*dt_parse)(struct hi6421_regulator *, struct platform_device *); -/* struct regulator_init_data *initdata; - */ }; -#if 0 -static int anatop_regmap_set_voltage_sel(struct regulator_dev *reg, - unsigned selector) -{ - struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); +static DEFINE_MUTEX(enable_mutex); +struct timeval last_enabled; - if (!anatop_reg->control_reg) - return -ENOTSUPP; - return regulator_set_voltage_sel_regmap(reg, selector); +static inline struct hi6421_pmic *rdev_to_pmic(struct regulator_dev *dev) +{ + /* regulator_dev parent to-> + * hi6421 regulator platform device_dev parent to-> + * hi6421 pmic platform device_dev + */ + return dev_get_drvdata(rdev_get_dev(dev)->parent->parent); +} + +/* helper function to ensure when it returns it is at least 'delay_us' + * microseconds after 'since'. + */ +static void ensured_time_after(struct timeval since, u32 delay_us) +{ + struct timeval now; + u64 elapsed_ns64, delay_ns64; + u32 actual_us32; + + delay_ns64 = delay_us * NSEC_PER_USEC; + do_gettimeofday(&now); + elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since); + if (delay_ns64 > elapsed_ns64) { + actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) / \ + NSEC_PER_USEC); + if (actual_us32 >= 1000) { + mdelay(actual_us32 / 1000); + udelay(actual_us32 % 1000); + } else if (actual_us32 > 0) { + udelay(actual_us32); + } + } + return; } -static int anatop_regmap_get_voltage_sel(struct regulator_dev *reg) +static int hi6421_regulator_is_enabled(struct regulator_dev *dev) { - struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + u32 reg_val; + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); - if (!anatop_reg->control_reg) - return -ENOTSUPP; + reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg); - return regulator_get_voltage_sel_regmap(reg); + return ((reg_val & sreg->register_info.enable_mask) != 0); } -#endif -#if 0 static int hi6421_regulator_enable(struct regulator_dev *dev) { - int ret = 0; - struct timeval tv; - u32 diff; struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); -#ifdef HI6421_REGULATOR_DEBUG - rdev_info(dev, "will be enabled\n"); -#endif - - do_gettimeofday(&tv); - diff = (tv.tv_sec - hi6421_regulator_data->lastoff_time[regulator_id].tv_sec) * USEC_PER_SEC - + tv.tv_usec - hi6421_regulator_data->lastoff_time[regulator_id].tv_usec; - if (diff < on_off_delay_us[regulator_id]) { - msleep((on_off_delay_us[regulator_id] - diff + 999 )/1000); - } - - if (regulator_id == HI6421_LDO12) { - ret = gpio_request(SD_GPIO_DCDC, "sdcard_dcdc"); - if (ret < 0) { - rdev_err(NULL, "hi6421 regulator gpio_request failed,please check!\n"); - return ret; - } - gpio_direction_output(SD_GPIO_DCDC, 1); + /* keep a distance of off_on_delay from last time disabled */ + ensured_time_after(sreg->last_off_time, sreg->off_on_delay); + + /* cannot enable more than one regulator at one time */ + mutex_lock(&enable_mutex); + ensured_time_after(last_enabled, HI6421_REGS_ENA_PROTECT_TIME); + + /* set enable register */ + hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg, \ + sreg->register_info.enable_mask, \ + sreg->register_info.enable_mask); + + do_gettimeofday(&last_enabled); + mutex_unlock(&enable_mutex); + + return 0; +} + +static int hi6421_regulator_disable(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + + /* set enable register to 0 */ + hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg, \ + sreg->register_info.enable_mask, 0); + + do_gettimeofday(&sreg->last_off_time); + + return 0; +} + +static int hi6421_regulator_get_voltage(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 reg_val, selector; + + /* get voltage selector */ + reg_val = hi6421_pmic_read(pmic, sreg->register_info.vset_reg); + selector = (reg_val & sreg->register_info.vset_mask) >> \ + (ffs(sreg->register_info.vset_mask) - 1); + + return sreg->rdesc.ops->list_voltage(dev, selector); +} + +static int hi6421_regulator_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 vsel; + int ret = 0; + + for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) { + int uV = sreg->rdesc.volt_table[vsel]; + /* Break at the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + break; } - if (regulator_id <= HI6421_LDO20) { - hi6421_regulator_set_bits(hi6421_regulator_data, regulator_ctrl_to_reg[regulator_id], HI6421_LDO_ENA_MASK, HI6421_LDO_ENA); - } else if (regulator_id == HI6421_LDOAUDIO) { - hi6421_regulator_set_bits(hi6421_regulator_data, regulator_ctrl_to_reg[regulator_id], HI6421_LDOAUDIO_ENA_MASK, HI6421_LDOAUDIO_ENA); - } else { - hi6421_regulator_set_bits(hi6421_regulator_data, regulator_ctrl_to_reg[regulator_id], HI6421_BUCK_ENA_MASK, HI6421_BUCK_ENA); + + /* unlikely to happen. sanity test done by regulator core */ + if (unlikely(vsel == sreg->rdesc.n_voltages)) + return -EINVAL; + + *selector = vsel; + /* set voltage selector */ + hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg, \ + sreg->register_info.vset_mask, \ + vsel << (ffs(sreg->register_info.vset_mask) - 1)); + + return ret; +} + +static int hi6421_regulator_buck012_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 vsel; + int ret = 0; + + vsel = DIV_ROUND_UP((max_uV - sreg->rdesc.min_uV), \ + sreg->rdesc.uV_step); + + *selector = vsel; + /* set voltage selector */ + hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg, \ + sreg->register_info.vset_mask, \ + vsel << (ffs(sreg->register_info.vset_mask) - 1)); + + return ret; +} + +static unsigned int hi6421_regulator_get_mode(struct regulator_dev *dev) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 reg_val; + + reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg); + if (reg_val & sreg->register_info.eco_mode_mask) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +static int hi6421_regulator_set_mode(struct regulator_dev *dev, \ + unsigned int mode) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + struct hi6421_pmic *pmic = rdev_to_pmic(dev); + u32 eco_mode; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + eco_mode = HI6421_ECO_MODE_DISABLE; + break; + case REGULATOR_MODE_IDLE: + eco_mode = HI6421_ECO_MODE_ENABLE; + break; + default: + return -EINVAL; } - udelay(on_enable_delay_us[regulator_id]); + /* set mode */ + hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg, \ + sreg->register_info.eco_mode_mask, \ + eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1)); return 0; } -#endif + + +unsigned int hi6421_regulator_get_optimum_mode(struct regulator_dev *dev, int input_uV, + int output_uV, int load_uA) +{ + struct hi6421_regulator *sreg = rdev_get_drvdata(dev); + + if ((load_uA == 0) || (load_uA > sreg->eco_uA)) + return REGULATOR_MODE_NORMAL; + else + return REGULATOR_MODE_IDLE; +} static int hi6421_dt_parse_common(struct hi6421_regulator *sreg, struct platform_device *pdev) { @@ -150,14 +280,6 @@ static int hi6421_dt_parse_common(struct hi6421_regulator *sreg, struct platform } sreg->register_info.vset_reg = register_info[0]; sreg->register_info.vset_mask = register_info[1]; - /* debug info */ - { - int i; - printk("regulator: hi6421-register_info:\n"); - for(i=0;i<5;i++){ - printk("regulator: reg_infor[%d]=%d\n", i, ((u32 *)&sreg->register_info)[i]); - } - } /* parse .off-on-delay */ ret = of_property_read_u32(np, "hisilicon,hi6421-off-on-delay-us", @@ -175,9 +297,16 @@ static int hi6421_dt_parse_common(struct hi6421_regulator *sreg, struct platform goto dt_parse_common_end; } + /* parse .eco_uA */ + ret = of_property_read_u32(np, "hisilicon,hi6421-eco-microamp", + &sreg->eco_uA); + if (ret) { + sreg->eco_uA = 0; + ret = 0; + } + dt_parse_common_end: return ret; - } static int hi6421_dt_parse_ldo(struct hi6421_regulator *sreg, struct platform_device *pdev) @@ -211,15 +340,6 @@ static int hi6421_dt_parse_ldo(struct hi6421_regulator *sreg, struct platform_de } rdesc->volt_table = v_table; - /* debug info */ - { - int i; - printk("regulator: hi6421-vset-table:\n"); - for(i=0;i<rdesc->n_voltages;i++){ - printk("regulator: hi6421 desc.volt_table[%d]=%d\n", i, rdesc->volt_table[i]); - } - } - /* parse hi6421 regulator's dt common part */ ret = hi6421_dt_parse_common(sreg, pdev); if (ret) { @@ -265,39 +385,45 @@ dt_parse_buck012_end: return ret; } -static struct regulator_ops hi6421_rops_ldo = { -#if 0 /* ref from v3.0.8 */ +static struct regulator_ops hi6421_ldo_rops = { .is_enabled = hi6421_regulator_is_enabled, .enable = hi6421_regulator_enable, .disable = hi6421_regulator_disable, - .list_voltage = hi6421_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .get_voltage = hi6421_regulator_get_voltage, - .set_voltage = hi6421_regulator_set_voltage, + .set_voltage = hi6421_regulator_ldo_set_voltage, .get_mode = hi6421_regulator_get_mode, .set_mode = hi6421_regulator_set_mode, .get_optimum_mode = hi6421_regulator_get_optimum_mode, -#endif -/* - .set_voltage_sel = anatop_regmap_set_voltage_sel, - .get_voltage_sel = anatop_regmap_get_voltage_sel, - .list_voltage = regulator_list_voltage_linear, - .map_voltage = regulator_map_voltage_linear, -*/ }; +static struct regulator_ops hi6421_buck012_rops = { + .is_enabled = hi6421_regulator_is_enabled, + .enable = hi6421_regulator_enable, + .disable = hi6421_regulator_disable, + .list_voltage = regulator_list_voltage_linear, + .get_voltage = hi6421_regulator_get_voltage, + .set_voltage = hi6421_regulator_buck012_set_voltage, + .get_mode = hi6421_regulator_get_mode, + .set_mode = hi6421_regulator_set_mode, + .get_optimum_mode = hi6421_regulator_get_optimum_mode, +}; +static struct regulator_ops hi6421_buck345_rops = { + .is_enabled = hi6421_regulator_is_enabled, + .enable = hi6421_regulator_enable, + .disable = hi6421_regulator_disable, + .list_voltage = regulator_list_voltage_table, + .get_voltage = hi6421_regulator_get_voltage, + .set_voltage = hi6421_regulator_ldo_set_voltage, + .get_mode = hi6421_regulator_get_mode, + .set_mode = hi6421_regulator_set_mode, + .get_optimum_mode = hi6421_regulator_get_optimum_mode, +}; static const struct hi6421_regulator hi6421_regulator_ldo = { -/* .base = offset, - .min_mV = min_mVolts, - .max_mV = max_mVolts, - */ .rdesc = { -/* .name = #label, \ - .id = TWL6030_REG_##label, \ - .n_voltages = 32, \ - */ - .ops = &hi6421_rops_ldo, + .ops = &hi6421_ldo_rops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -305,16 +431,8 @@ static const struct hi6421_regulator hi6421_regulator_ldo = { }; static const struct hi6421_regulator hi6421_regulator_buck012 = { -/* .base = offset, - .min_mV = min_mVolts, - .max_mV = max_mVolts, - */ .rdesc = { -/* .name = #label, \ - .id = TWL6030_REG_##label, \ - .n_voltages = 32, \ - */ - .ops = &hi6421_rops_ldo, + .ops = &hi6421_buck012_rops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -322,23 +440,15 @@ static const struct hi6421_regulator hi6421_regulator_buck012 = { }; static const struct hi6421_regulator hi6421_regulator_buck345 = { -/* .base = offset, - .min_mV = min_mVolts, - .max_mV = max_mVolts, - */ .rdesc = { -/* .name = #label, \ - .id = TWL6030_REG_##label, \ - .n_voltages = 32, \ - */ - .ops = &hi6421_rops_ldo, + .ops = &hi6421_buck345_rops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, .dt_parse = hi6421_dt_parse_ldo, }; -static struct of_device_id of_hi6421_regulator_match_tbl[] = { +struct of_device_id of_hi6421_regulator_match_tbl[] = { { .compatible = "hisilicon,hi6421-ldo", .data = &hi6421_regulator_ldo, @@ -358,46 +468,31 @@ static int hi6421_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; -/* struct device_node *hi6421_np; - */ struct regulator_desc *rdesc; struct regulator_dev *rdev; struct hi6421_regulator *sreg = NULL; struct regulator_init_data *initdata; struct regulator_config config = { }; const struct of_device_id *match; - const struct hi6421_regulator *template; + const struct hi6421_regulator *template = NULL; int ret = 0; /* to check which type of regulator this is */ match = of_match_device(of_hi6421_regulator_match_tbl, &pdev->dev); - if (match) { + if (match) template = match->data; -/* id = template->desc.id; */ -/* initdata = of_get_regulator_init_data(dev, np); */ - printk("regulator: hi6421 match found!\n"); - } else { - printk("regulator: hi6421 ERROR!\n"); - } + else + return -EINVAL; initdata = of_get_regulator_init_data(dev, np); sreg = kmemdup(template, sizeof (*sreg), GFP_KERNEL); if (!sreg) return -ENOMEM; -/* initdata is already in regulator_config */ -/* sreg->initdata = initdata; */ - sreg->name = initdata->constraints.name; rdesc = &sreg->rdesc; - rdesc->name = initdata->constraints.name; - - /* not needed any more. coming from template. */ -/* rdesc->ops = &hi6421_rops_ldo; - rdesc->type = REGULATOR_VOLTAGE; - rdesc->owner = THIS_MODULE; - */ - + rdesc->name = sreg->name; + rdesc->min_uV = initdata->constraints.min_uV; /* to parse device tree data for regulator specific */ ret = sreg->dt_parse(sreg, pdev); @@ -406,23 +501,11 @@ static int hi6421_regulator_probe(struct platform_device *pdev) goto hi6421_probe_end; } -#if 0 - anatop_np = of_get_parent(np); - if (!anatop_np) - return -ENODEV; - sreg->anatop = syscon_node_to_regmap(anatop_np); - of_node_put(anatop_np); - if (IS_ERR(sreg->anatop)) - return PTR_ERR(sreg->anatop); -#endif - config.dev = &pdev->dev; config.init_data = initdata; config.driver_data = sreg; config.of_node = pdev->dev.of_node; -/* config.regmap = sreg->anatop; */ /* ?? */ - /* register regulator */ rdev = regulator_register(rdesc, &config); if (IS_ERR(rdev)) { @@ -435,7 +518,7 @@ static int hi6421_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rdev); hi6421_probe_end: - if (sreg) + if (ret) kfree(sreg); return ret; } @@ -469,7 +552,7 @@ static int __init hi6421_regulator_init(void) { return platform_driver_register(&hi6421_regulator_driver); } -postcore_initcall(hi6421_regulator_init); +module_init(hi6421_regulator_init); static void __exit hi6421_regulator_exit(void) { |