aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-02-01 06:13:55 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-02-21 16:12:37 +0800
commita194d37e73f4d0dc3fc983acc4d936877e45ee51 (patch)
tree407c6cd4035c8cf0bb19ddbb5b17f13d8b5650f5
parent2a80cddf306f4486a98db08811dafe7b84f41c75 (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/Kconfig5
-rw-r--r--drivers/regulator/hi6421-regulator.c377
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)
{