From ddd86436f4e3643c04b797f858dab95d5f2e4de9 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 25 Dec 2015 15:00:15 +0000 Subject: fish --- drivers/regulator/88pm8607.c | 451 ++++ drivers/regulator/Kconfig | 614 +++++ drivers/regulator/Makefile | 75 + drivers/regulator/aat2870-regulator.c | 222 ++ drivers/regulator/ab3100.c | 747 +++++ drivers/regulator/ab8500-ext.c | 407 +++ drivers/regulator/ab8500.c | 3239 ++++++++++++++++++++++ drivers/regulator/ad5398.c | 291 ++ drivers/regulator/anatop-regulator.c | 262 ++ drivers/regulator/arizona-ldo1.c | 266 ++ drivers/regulator/arizona-micsupp.c | 265 ++ drivers/regulator/as3711-regulator.c | 430 +++ drivers/regulator/core.c | 4038 ++++++++++++++++++++++++++++ drivers/regulator/da903x.c | 535 ++++ drivers/regulator/da9052-regulator.c | 437 +++ drivers/regulator/da9055-regulator.c | 638 +++++ drivers/regulator/db8500-prcmu.c | 574 ++++ drivers/regulator/dbx500-prcmu.c | 242 ++ drivers/regulator/dbx500-prcmu.h | 61 + drivers/regulator/dummy.c | 90 + drivers/regulator/dummy.h | 27 + drivers/regulator/fan53555.c | 320 +++ drivers/regulator/fixed-helper.c | 61 + drivers/regulator/fixed.c | 281 ++ drivers/regulator/gpio-regulator.c | 396 +++ drivers/regulator/isl6271a-regulator.c | 196 ++ drivers/regulator/lp3971.c | 501 ++++ drivers/regulator/lp3972.c | 607 +++++ drivers/regulator/lp872x.c | 906 +++++++ drivers/regulator/lp8755.c | 566 ++++ drivers/regulator/lp8788-buck.c | 566 ++++ drivers/regulator/lp8788-ldo.c | 663 +++++ drivers/regulator/max1586.c | 270 ++ drivers/regulator/max77686.c | 540 ++++ drivers/regulator/max8649.c | 287 ++ drivers/regulator/max8660.c | 465 ++++ drivers/regulator/max8907-regulator.c | 408 +++ drivers/regulator/max8925-regulator.c | 360 +++ drivers/regulator/max8952.c | 363 +++ drivers/regulator/max8973-regulator.c | 505 ++++ drivers/regulator/max8997.c | 1267 +++++++++ drivers/regulator/max8998.c | 854 ++++++ drivers/regulator/mc13783-regulator.c | 501 ++++ drivers/regulator/mc13892-regulator.c | 671 +++++ drivers/regulator/mc13xxx-regulator-core.c | 240 ++ drivers/regulator/mc13xxx.h | 113 + drivers/regulator/of_regulator.c | 158 ++ drivers/regulator/palmas-regulator.c | 1083 ++++++++ drivers/regulator/pcap-regulator.c | 292 ++ drivers/regulator/pcf50633-regulator.c | 138 + drivers/regulator/rc5t583-regulator.c | 229 ++ drivers/regulator/s2mps11.c | 352 +++ drivers/regulator/s5m8767.c | 974 +++++++ drivers/regulator/tps51632-regulator.c | 396 +++ drivers/regulator/tps6105x-regulator.c | 193 ++ drivers/regulator/tps62360-regulator.c | 553 ++++ drivers/regulator/tps65023-regulator.c | 451 ++++ drivers/regulator/tps6507x-regulator.c | 570 ++++ drivers/regulator/tps65090-regulator.c | 368 +++ drivers/regulator/tps65217-regulator.c | 436 +++ drivers/regulator/tps6524x-regulator.c | 664 +++++ drivers/regulator/tps6586x-regulator.c | 464 ++++ drivers/regulator/tps65910-regulator.c | 1266 +++++++++ drivers/regulator/tps65912-regulator.c | 562 ++++ drivers/regulator/tps80031-regulator.c | 791 ++++++ drivers/regulator/twl-regulator.c | 1253 +++++++++ drivers/regulator/userspace-consumer.c | 186 ++ drivers/regulator/vexpress.c | 147 + drivers/regulator/virtual.c | 352 +++ drivers/regulator/wm831x-dcdc.c | 997 +++++++ drivers/regulator/wm831x-isink.c | 267 ++ drivers/regulator/wm831x-ldo.c | 793 ++++++ drivers/regulator/wm8350-regulator.c | 1422 ++++++++++ drivers/regulator/wm8400-regulator.c | 312 +++ drivers/regulator/wm8994-regulator.c | 210 ++ 75 files changed, 41697 insertions(+) create mode 100644 drivers/regulator/88pm8607.c create mode 100644 drivers/regulator/Kconfig create mode 100644 drivers/regulator/Makefile create mode 100644 drivers/regulator/aat2870-regulator.c create mode 100644 drivers/regulator/ab3100.c create mode 100644 drivers/regulator/ab8500-ext.c create mode 100644 drivers/regulator/ab8500.c create mode 100644 drivers/regulator/ad5398.c create mode 100644 drivers/regulator/anatop-regulator.c create mode 100644 drivers/regulator/arizona-ldo1.c create mode 100644 drivers/regulator/arizona-micsupp.c create mode 100644 drivers/regulator/as3711-regulator.c create mode 100644 drivers/regulator/core.c create mode 100644 drivers/regulator/da903x.c create mode 100644 drivers/regulator/da9052-regulator.c create mode 100644 drivers/regulator/da9055-regulator.c create mode 100644 drivers/regulator/db8500-prcmu.c create mode 100644 drivers/regulator/dbx500-prcmu.c create mode 100644 drivers/regulator/dbx500-prcmu.h create mode 100644 drivers/regulator/dummy.c create mode 100644 drivers/regulator/dummy.h create mode 100644 drivers/regulator/fan53555.c create mode 100644 drivers/regulator/fixed-helper.c create mode 100644 drivers/regulator/fixed.c create mode 100644 drivers/regulator/gpio-regulator.c create mode 100644 drivers/regulator/isl6271a-regulator.c create mode 100644 drivers/regulator/lp3971.c create mode 100644 drivers/regulator/lp3972.c create mode 100644 drivers/regulator/lp872x.c create mode 100644 drivers/regulator/lp8755.c create mode 100644 drivers/regulator/lp8788-buck.c create mode 100644 drivers/regulator/lp8788-ldo.c create mode 100644 drivers/regulator/max1586.c create mode 100644 drivers/regulator/max77686.c create mode 100644 drivers/regulator/max8649.c create mode 100644 drivers/regulator/max8660.c create mode 100644 drivers/regulator/max8907-regulator.c create mode 100644 drivers/regulator/max8925-regulator.c create mode 100644 drivers/regulator/max8952.c create mode 100644 drivers/regulator/max8973-regulator.c create mode 100644 drivers/regulator/max8997.c create mode 100644 drivers/regulator/max8998.c create mode 100644 drivers/regulator/mc13783-regulator.c create mode 100644 drivers/regulator/mc13892-regulator.c create mode 100644 drivers/regulator/mc13xxx-regulator-core.c create mode 100644 drivers/regulator/mc13xxx.h create mode 100644 drivers/regulator/of_regulator.c create mode 100644 drivers/regulator/palmas-regulator.c create mode 100644 drivers/regulator/pcap-regulator.c create mode 100644 drivers/regulator/pcf50633-regulator.c create mode 100644 drivers/regulator/rc5t583-regulator.c create mode 100644 drivers/regulator/s2mps11.c create mode 100644 drivers/regulator/s5m8767.c create mode 100644 drivers/regulator/tps51632-regulator.c create mode 100644 drivers/regulator/tps6105x-regulator.c create mode 100644 drivers/regulator/tps62360-regulator.c create mode 100644 drivers/regulator/tps65023-regulator.c create mode 100644 drivers/regulator/tps6507x-regulator.c create mode 100644 drivers/regulator/tps65090-regulator.c create mode 100644 drivers/regulator/tps65217-regulator.c create mode 100644 drivers/regulator/tps6524x-regulator.c create mode 100644 drivers/regulator/tps6586x-regulator.c create mode 100644 drivers/regulator/tps65910-regulator.c create mode 100644 drivers/regulator/tps65912-regulator.c create mode 100644 drivers/regulator/tps80031-regulator.c create mode 100644 drivers/regulator/twl-regulator.c create mode 100644 drivers/regulator/userspace-consumer.c create mode 100644 drivers/regulator/vexpress.c create mode 100644 drivers/regulator/virtual.c create mode 100644 drivers/regulator/wm831x-dcdc.c create mode 100644 drivers/regulator/wm831x-isink.c create mode 100644 drivers/regulator/wm831x-ldo.c create mode 100644 drivers/regulator/wm8350-regulator.c create mode 100644 drivers/regulator/wm8400-regulator.c create mode 100644 drivers/regulator/wm8994-regulator.c (limited to 'drivers/regulator') diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c new file mode 100644 index 0000000..493948a --- /dev/null +++ b/drivers/regulator/88pm8607.c @@ -0,0 +1,451 @@ +/* + * Regulators driver for Marvell 88PM8607 + * + * Copyright (C) 2009 Marvell International Ltd. + * Haojian Zhuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pm8607_regulator_info { + struct regulator_desc desc; + struct pm860x_chip *chip; + struct regulator_dev *regulator; + struct i2c_client *i2c; + struct i2c_client *i2c_8606; + + unsigned int *vol_table; + unsigned int *vol_suspend; + + int slope_double; +}; + +static const unsigned int BUCK1_table[] = { + 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, + 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, + 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, + 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, + 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, +}; + +static const unsigned int BUCK1_suspend_table[] = { + 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, + 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, + 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, + 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, + 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000, +}; + +static const unsigned int BUCK2_table[] = { + 0, 50000, 100000, 150000, 200000, 250000, 300000, 350000, + 400000, 450000, 500000, 550000, 600000, 650000, 700000, 750000, + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000, + 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000, + 2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000, + 2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000, +}; + +static const unsigned int BUCK2_suspend_table[] = { + 0, 50000, 100000, 150000, 200000, 250000, 300000, 350000, + 400000, 450000, 500000, 550000, 600000, 650000, 700000, 750000, + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000, + 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, 2350000, + 2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000, + 2800000, 2850000, 2900000, 2950000, 3000000, 3000000, 3000000, 3000000, +}; + +static const unsigned int BUCK3_table[] = { + 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, + 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, + 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, + 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, + 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000, +}; + +static const unsigned int BUCK3_suspend_table[] = { + 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, + 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, + 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, + 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, + 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, 1500000, 1500000, 1500000, 1500000, +}; + +static const unsigned int LDO1_table[] = { + 1800000, 1200000, 2800000, 0, +}; + +static const unsigned int LDO1_suspend_table[] = { + 1800000, 1200000, 0, 0, +}; + +static const unsigned int LDO2_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, +}; + +static const unsigned int LDO2_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO3_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, +}; + +static const unsigned int LDO3_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO4_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 3300000, +}; + +static const unsigned int LDO4_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2900000, 2900000, +}; + +static const unsigned int LDO5_table[] = { + 2900000, 3000000, 3100000, 3300000, +}; + +static const unsigned int LDO5_suspend_table[] = { + 2900000, 0, 0, 0, +}; + +static const unsigned int LDO6_table[] = { + 1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 3300000, +}; + +static const unsigned int LDO6_suspend_table[] = { + 1800000, 1850000, 2600000, 2650000, 2700000, 2750000, 2800000, 2900000, +}; + +static const unsigned int LDO7_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO7_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO8_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO8_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO9_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, +}; + +static const unsigned int LDO9_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, +}; + +static const unsigned int LDO10_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 3300000, + 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, +}; + +static const unsigned int LDO10_suspend_table[] = { + 1800000, 1850000, 1900000, 2700000, 2750000, 2800000, 2850000, 2900000, + 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, +}; + +static const unsigned int LDO12_table[] = { + 1800000, 1900000, 2700000, 2800000, 2900000, 3000000, 3100000, 3300000, + 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, +}; + +static const unsigned int LDO12_suspend_table[] = { + 1800000, 1900000, 2700000, 2800000, 2900000, 2900000, 2900000, 2900000, + 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, 1200000, +}; + +static const unsigned int LDO13_table[] = { + 1200000, 1300000, 1800000, 2000000, 2500000, 2800000, 3000000, 0, +}; + +static const unsigned int LDO13_suspend_table[] = { + 0, +}; + +static const unsigned int LDO14_table[] = { + 1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 3300000, +}; + +static const unsigned int LDO14_suspend_table[] = { + 1800000, 1850000, 2700000, 2750000, 2800000, 2850000, 2900000, 2900000, +}; + +static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); + int ret = -EINVAL; + + if (info->vol_table && (index < rdev->desc->n_voltages)) { + ret = info->vol_table[index]; + if (info->slope_double) + ret <<= 1; + } + return ret; +} + +static struct regulator_ops pm8607_regulator_ops = { + .list_voltage = pm8607_list_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops pm8606_preg_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define PM8606_PREG(ereg, ebit) \ +{ \ + .desc = { \ + .name = "PREG", \ + .ops = &pm8606_preg_ops, \ + .type = REGULATOR_CURRENT, \ + .id = PM8606_ID_PREG, \ + .owner = THIS_MODULE, \ + .enable_reg = PM8606_##ereg, \ + .enable_mask = (ebit), \ + .enable_is_inverted = true, \ + }, \ +} + +#define PM8607_DVC(vreg, ureg, ubit, ereg, ebit) \ +{ \ + .desc = { \ + .name = #vreg, \ + .ops = &pm8607_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PM8607_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(vreg##_table), \ + .vsel_reg = PM8607_##vreg, \ + .vsel_mask = ARRAY_SIZE(vreg##_table) - 1, \ + .apply_reg = PM8607_##ureg, \ + .apply_bit = (ubit), \ + .enable_reg = PM8607_##ereg, \ + .enable_mask = 1 << (ebit), \ + }, \ + .slope_double = (0), \ + .vol_table = (unsigned int *)&vreg##_table, \ + .vol_suspend = (unsigned int *)&vreg##_suspend_table, \ +} + +#define PM8607_LDO(_id, vreg, shift, ereg, ebit) \ +{ \ + .desc = { \ + .name = "LDO" #_id, \ + .ops = &pm8607_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PM8607_ID_LDO##_id, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(LDO##_id##_table), \ + .vsel_reg = PM8607_##vreg, \ + .vsel_mask = (ARRAY_SIZE(LDO##_id##_table) - 1) << (shift), \ + .enable_reg = PM8607_##ereg, \ + .enable_mask = 1 << (ebit), \ + }, \ + .slope_double = (0), \ + .vol_table = (unsigned int *)&LDO##_id##_table, \ + .vol_suspend = (unsigned int *)&LDO##_id##_suspend_table, \ +} + +static struct pm8607_regulator_info pm8607_regulator_info[] = { + PM8607_DVC(BUCK1, GO, BIT(0), SUPPLIES_EN11, 0), + PM8607_DVC(BUCK2, GO, BIT(1), SUPPLIES_EN11, 1), + PM8607_DVC(BUCK3, GO, BIT(2), SUPPLIES_EN11, 2), + + PM8607_LDO(1, LDO1, 0, SUPPLIES_EN11, 3), + PM8607_LDO(2, LDO2, 0, SUPPLIES_EN11, 4), + PM8607_LDO(3, LDO3, 0, SUPPLIES_EN11, 5), + PM8607_LDO(4, LDO4, 0, SUPPLIES_EN11, 6), + PM8607_LDO(5, LDO5, 0, SUPPLIES_EN11, 7), + PM8607_LDO(6, LDO6, 0, SUPPLIES_EN12, 0), + PM8607_LDO(7, LDO7, 0, SUPPLIES_EN12, 1), + PM8607_LDO(8, LDO8, 0, SUPPLIES_EN12, 2), + PM8607_LDO(9, LDO9, 0, SUPPLIES_EN12, 3), + PM8607_LDO(10, LDO10, 0, SUPPLIES_EN12, 4), + PM8607_LDO(12, LDO12, 0, SUPPLIES_EN12, 5), + PM8607_LDO(13, VIBRATOR_SET, 1, VIBRATOR_SET, 0), + PM8607_LDO(14, LDO14, 0, SUPPLIES_EN12, 6), +}; + +static struct pm8607_regulator_info pm8606_regulator_info[] = { + PM8606_PREG(PREREGULATORB, 5), +}; + +#ifdef CONFIG_OF +static int pm8607_regulator_dt_init(struct platform_device *pdev, + struct pm8607_regulator_info *info, + struct regulator_config *config) +{ + struct device_node *nproot, *np; + nproot = of_node_get(pdev->dev.parent->of_node); + if (!nproot) + return -ENODEV; + nproot = of_find_node_by_name(nproot, "regulators"); + if (!nproot) { + dev_err(&pdev->dev, "failed to find regulators node\n"); + return -ENODEV; + } + for_each_child_of_node(nproot, np) { + if (!of_node_cmp(np->name, info->desc.name)) { + config->init_data = + of_get_regulator_init_data(&pdev->dev, np); + config->of_node = np; + break; + } + } + of_node_put(nproot); + return 0; +} +#else +#define pm8607_regulator_dt_init(x, y, z) (-1) +#endif + +static int pm8607_regulator_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm8607_regulator_info *info = NULL; + struct regulator_init_data *pdata = pdev->dev.platform_data; + struct regulator_config config = { }; + struct resource *res; + int i; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res) { + /* There're resources in 88PM8607 regulator driver */ + for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) { + info = &pm8607_regulator_info[i]; + if (info->desc.vsel_reg == res->start) + break; + } + if (i == ARRAY_SIZE(pm8607_regulator_info)) { + dev_err(&pdev->dev, "Failed to find regulator %llu\n", + (unsigned long long)res->start); + return -EINVAL; + } + } else { + /* There's no resource in 88PM8606 PREG regulator driver */ + info = &pm8606_regulator_info[0]; + /* i is used to check regulator ID */ + i = -1; + } + info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; + info->i2c_8606 = (chip->id == CHIP_PM8607) ? chip->companion : + chip->client; + info->chip = chip; + + /* check DVC ramp slope double */ + if ((i == PM8607_ID_BUCK3) && info->chip->buck3_double) + info->slope_double = 1; + + config.dev = &pdev->dev; + config.driver_data = info; + + if (pm8607_regulator_dt_init(pdev, info, &config)) + if (pdata) + config.init_data = pdata; + + if (chip->id == CHIP_PM8607) + config.regmap = chip->regmap; + else + config.regmap = chip->regmap_companion; + + info->regulator = regulator_register(&info->desc, &config); + if (IS_ERR(info->regulator)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + info->desc.name); + return PTR_ERR(info->regulator); + } + + platform_set_drvdata(pdev, info); + return 0; +} + +static int pm8607_regulator_remove(struct platform_device *pdev) +{ + struct pm8607_regulator_info *info = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(info->regulator); + return 0; +} + +static struct platform_device_id pm8607_regulator_driver_ids[] = { + { + .name = "88pm860x-regulator", + .driver_data = 0, + }, { + .name = "88pm860x-preg", + .driver_data = 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(platform, pm8607_regulator_driver_ids); + +static struct platform_driver pm8607_regulator_driver = { + .driver = { + .name = "88pm860x-regulator", + .owner = THIS_MODULE, + }, + .probe = pm8607_regulator_probe, + .remove = pm8607_regulator_remove, + .id_table = pm8607_regulator_driver_ids, +}; + +static int __init pm8607_regulator_init(void) +{ + return platform_driver_register(&pm8607_regulator_driver); +} +subsys_initcall(pm8607_regulator_init); + +static void __exit pm8607_regulator_exit(void) +{ + platform_driver_unregister(&pm8607_regulator_driver); +} +module_exit(pm8607_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC"); +MODULE_ALIAS("platform:88pm8607-regulator"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig new file mode 100644 index 0000000..7182757 --- /dev/null +++ b/drivers/regulator/Kconfig @@ -0,0 +1,614 @@ +menuconfig REGULATOR + depends on !BACKPORT_KERNEL_3_4 + bool "Voltage and Current Regulator Support" + help + Generic Voltage and Current Regulator support. + + This framework is designed to provide a generic interface to voltage + and current regulators within the Linux kernel. It's intended to + provide voltage and current control to client or consumer drivers and + also provide status information to user space applications through a + sysfs interface. + + The intention is to allow systems to dynamically control regulator + output in order to save power and prolong battery life. This applies + to both voltage regulators (where voltage output is controllable) and + current sinks (where current output is controllable). + + This framework safely compiles out if not selected so that client + drivers can still be used in systems with no software controllable + regulators. + + If unsure, say no. + + +if REGULATOR + +config REGULATOR_DEBUG + bool "Regulator debug support" + help + Say yes here to enable debugging support. + +config REGULATOR_DUMMY + bool "Provide a dummy regulator if regulator lookups fail" + help + If this option is enabled then when a regulator lookup fails + and the board has not specified that it has provided full + constraints the regulator core will provide an always + enabled dummy regulator, allowing consumer drivers to continue. + + A warning will be generated when this substitution is done. + +config REGULATOR_FIXED_VOLTAGE + tristate "Fixed voltage regulator support" + depends on m + help + This driver provides support for fixed voltage regulators, + useful for systems which use a combination of software + managed regulators and simple non-configurable regulators. + +config REGULATOR_VIRTUAL_CONSUMER + tristate "Virtual regulator consumer support" + depends on m + help + This driver provides a virtual consumer for the voltage and + current regulator API which provides sysfs controls for + configuring the supplies requested. This is mainly useful + for test purposes. + + If unsure, say no. + +config REGULATOR_USERSPACE_CONSUMER + tristate "Userspace regulator consumer support" + depends on m + help + There are some classes of devices that are controlled entirely + from user space. Userspace consumer driver provides ability to + control power supplies for such devices. + + If unsure, say no. + +config REGULATOR_GPIO + tristate "GPIO regulator support" + depends on m + depends on GPIOLIB + help + This driver provides support for regulators that can be + controlled via gpios. + It is capable of supporting current and voltage regulators + and the platform has to provide a mapping of GPIO-states + to target volts/amps. + +config REGULATOR_AD5398 + tristate "Analog Devices AD5398/AD5821 regulators" + depends on m + depends on I2C + help + This driver supports AD5398 and AD5821 current regulator chips. + If building into module, its name is ad5398.ko. + +config REGULATOR_AAT2870 + depends on !BACKPORT_KERNEL_3_6 + tristate "AnalogicTech AAT2870 Regulators" + depends on m + depends on MFD_AAT2870_CORE + help + If you have a AnalogicTech AAT2870 say Y to enable the + regulator driver. + +config REGULATOR_ARIZONA + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Wolfson Arizona class devices" + depends on m + depends on MFD_ARIZONA + depends on SND_SOC + help + Support for the regulators found on Wolfson Arizona class + devices. + +config REGULATOR_DA903X + depends on !BACKPORT_KERNEL_3_5 + tristate "Dialog Semiconductor DA9030/DA9034 regulators" + depends on m + depends on PMIC_DA903X + help + Say y here to support the BUCKs and LDOs regulators found on + Dialog Semiconductor DA9030/DA9034 PMIC. + +config REGULATOR_DA9052 + depends on !BACKPORT_KERNEL_3_5 + tristate "Dialog Semiconductor DA9052/DA9053 regulators" + depends on m + depends on PMIC_DA9052 + help + This driver supports the voltage regulators of DA9052-BC and + DA9053-AA/Bx PMIC. + +config REGULATOR_DA9055 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Dialog Semiconductor DA9055 regulators" + depends on m + depends on MFD_DA9055 + help + Say y here to support the BUCKs and LDOs regulators found on + Dialog Semiconductor DA9055 PMIC. + + This driver can also be built as a module. If so, the module + will be called da9055-regulator. + +config REGULATOR_FAN53555 + depends on !BACKPORT_KERNEL_3_5 + tristate "Fairchild FAN53555 Regulator" + depends on m + depends on I2C + depends on REGMAP_I2C + help + This driver supports Fairchild FAN53555 Digitally Programmable + TinyBuck Regulator. The FAN53555 is a step-down switching voltage + regulator that delivers a digitally programmable output from an + input voltage supply of 2.5V to 5.5V. The output voltage is + programmed through an I2C interface. + +config REGULATOR_ANATOP + tristate "Freescale i.MX on-chip ANATOP LDO regulators" + depends on m + depends on MFD_SYSCON + help + Say y here to support Freescale i.MX on-chip ANATOP LDOs + regulators. It is recommended that this option be + enabled on i.MX6 platform. + +config REGULATOR_MC13XXX_CORE + depends on !BACKPORT_KERNEL_3_6 + tristate + depends on m + +config REGULATOR_MC13783 + depends on !BACKPORT_KERNEL_3_6 + tristate "Freescale MC13783 regulator driver" + depends on m + depends on MFD_MC13783 + select REGULATOR_MC13XXX_CORE + help + Say y here to support the regulators found on the Freescale MC13783 + PMIC. + +config REGULATOR_MC13892 + depends on !BACKPORT_KERNEL_3_6 + tristate "Freescale MC13892 regulator driver" + depends on m + depends on MFD_MC13XXX + select REGULATOR_MC13XXX_CORE + help + Say y here to support the regulators found on the Freescale MC13892 + PMIC. + +config REGULATOR_ISL6271A + depends on !BACKPORT_KERNEL_3_5 + tristate "Intersil ISL6271A Power regulator" + depends on m + depends on I2C + help + This driver supports ISL6271A voltage regulator chip. + +config REGULATOR_88PM8607 + bool "Marvell 88PM8607 Power regulators" + depends on MFD_88PM860X=y + help + This driver supports 88PM8607 voltage regulator chips. + +config REGULATOR_MAX1586 + depends on !BACKPORT_KERNEL_3_6 + tristate "Maxim 1586/1587 voltage regulator" + depends on m + depends on I2C + help + This driver controls a Maxim 1586 or 1587 voltage output + regulator via I2C bus. The provided regulator is suitable + for PXA27x chips to control VCC_CORE and VCC_USIM voltages. + +config REGULATOR_MAX8649 + depends on !BACKPORT_KERNEL_3_5 + tristate "Maxim 8649 voltage regulator" + depends on m + depends on I2C + depends on REGMAP_I2C + help + This driver controls a Maxim 8649 voltage output regulator via + I2C bus. + +config REGULATOR_MAX8660 + depends on !BACKPORT_KERNEL_3_5 + tristate "Maxim 8660/8661 voltage regulator" + depends on m + depends on I2C + help + This driver controls a Maxim 8660/8661 voltage output + regulator via I2C bus. + +config REGULATOR_MAX8907 + tristate "Maxim 8907 voltage regulator" + depends on m + depends on MFD_MAX8907 + help + This driver controls a Maxim 8907 voltage output regulator + via I2C bus. The provided regulator is suitable for Tegra + chip to control Step-Down DC-DC and LDOs. + +config REGULATOR_MAX8925 + depends on !BACKPORT_KERNEL_3_5 + tristate "Maxim MAX8925 Power Management IC" + depends on m + depends on MFD_MAX8925 + help + Say y here to support the voltage regulaltor of Maxim MAX8925 PMIC. + +config REGULATOR_MAX8952 + tristate "Maxim MAX8952 Power Management IC" + depends on m + depends on I2C + help + This driver controls a Maxim 8952 voltage output regulator + via I2C bus. Maxim 8952 has one voltage output and supports 4 DVS + modes ranging from 0.77V to 1.40V by 0.01V steps. + +config REGULATOR_MAX8973 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Maxim MAX8973 voltage regulator " + depends on m + depends on I2C + depends on REGMAP_I2C + help + The MAXIM MAX8973 high-efficiency. three phase, DC-DC step-down + switching regulator delievers up to 9A of output current. Each + phase operates at a 2MHz fixed frequency with a 120 deg shift + from the adjacent phase, allowing the use of small magnetic component. + +config REGULATOR_MAX8997 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Maxim 8997/8966 regulator" + depends on m + depends on MFD_MAX8997 + help + This driver controls a Maxim 8997/8966 regulator + via I2C bus. The provided regulator is suitable for S5PC110, + S5PV210, and Exynos-4 chips to control VCC_CORE and + VCC_USIM voltages. + +config REGULATOR_MAX8998 + depends on !BACKPORT_KERNEL_3_5 + tristate "Maxim 8998 voltage regulator" + depends on m + depends on MFD_MAX8998 + help + This driver controls a Maxim 8998 voltage output regulator + via I2C bus. The provided regulator is suitable for S3C6410 + and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. + +config REGULATOR_MAX77686 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Maxim 77686 regulator" + depends on m + depends on MFD_MAX77686 + help + This driver controls a Maxim 77686 regulator + via I2C bus. The provided regulator is suitable for + Exynos-4 chips to control VARM and VINT voltages. + +config REGULATOR_PCAP + depends on !BACKPORT_KERNEL_3_6 + tristate "Motorola PCAP2 regulator driver" + depends on m + depends on EZX_PCAP + help + This driver provides support for the voltage regulators of the + PCAP2 PMIC. + +config REGULATOR_LP3971 + depends on !BACKPORT_KERNEL_3_6 + tristate "National Semiconductors LP3971 PMIC regulator driver" + depends on m + depends on I2C + help + Say Y here to support the voltage regulators and convertors + on National Semiconductors LP3971 PMIC + +config REGULATOR_LP3972 + depends on !BACKPORT_KERNEL_3_6 + tristate "National Semiconductors LP3972 PMIC regulator driver" + depends on m + depends on I2C + help + Say Y here to support the voltage regulators and convertors + on National Semiconductors LP3972 PMIC + +config REGULATOR_LP872X + bool "TI/National Semiconductor LP8720/LP8725 voltage regulators" + depends on I2C=y + depends on REGMAP_I2C + help + This driver supports LP8720/LP8725 PMIC + +config REGULATOR_LP8755 + depends on !BACKPORT_KERNEL_3_5 + tristate "TI LP8755 High Performance PMU driver" + depends on m + depends on I2C + depends on REGMAP_I2C + help + This driver supports LP8755 High Performance PMU driver. This + chip contains six step-down DC/DC converters which can support + 9 mode multiphase configuration. + +config REGULATOR_LP8788 + bool "TI LP8788 Power Regulators" + depends on MFD_LP8788 + help + This driver supports LP8788 voltage regulator chip. + +config REGULATOR_PCF50633 + depends on !BACKPORT_KERNEL_3_5 + tristate "NXP PCF50633 regulator driver" + depends on m + depends on MFD_PCF50633 + help + Say Y here to support the voltage regulators and convertors + on PCF50633 + +config REGULATOR_RC5T583 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "RICOH RC5T583 Power regulators" + depends on m + depends on MFD_RC5T583 + help + Select this option to enable the power regulator of RICOH + PMIC RC5T583. + This driver supports the control of different power rails of device + through regulator interface. The device supports multiple DCDC/LDO + outputs which can be controlled by i2c communication. + +config REGULATOR_S2MPS11 + tristate "Samsung S2MPS11 voltage regulator" + depends on m + depends on MFD_SEC_CORE + help + This driver supports a Samsung S2MPS11 voltage output regulator + via I2C bus. S2MPS11 is comprised of high efficient Buck converters + including Dual-Phase Buck converter, Buck-Boost converter, various LDOs. + +config REGULATOR_S5M8767 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Samsung S5M8767A voltage regulator" + depends on m + depends on MFD_SEC_CORE + help + This driver supports a Samsung S5M8767A voltage output regulator + via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and + supports DVS mode with 8bits of output voltage control. + +config REGULATOR_AB3100 + depends on !BACKPORT_KERNEL_3_9 + tristate "ST-Ericsson AB3100 Regulator functions" + depends on m + depends on AB3100_CORE + default y if AB3100_CORE + help + These regulators correspond to functionality in the + AB3100 analog baseband dealing with power regulators + for the system. + +config REGULATOR_AB8500 + bool "ST-Ericsson AB8500 Power Regulators" + depends on AB8500_CORE + help + This driver supports the regulators found on the ST-Ericsson mixed + signal AB8500 PMIC + +config REGULATOR_DBX500_PRCMU + bool + +config REGULATOR_DB8500_PRCMU + bool "ST-Ericsson DB8500 Voltage Domain Regulators" + depends on MFD_DB8500_PRCMU + select REGULATOR_DBX500_PRCMU + help + This driver supports the voltage domain regulators controlled by the + DB8500 PRCMU + +config REGULATOR_PALMAS + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "TI Palmas PMIC Regulators" + depends on m + depends on MFD_PALMAS + help + If you wish to control the regulators on the Palmas series of + chips say Y here. This will enable support for all the software + controllable SMPS/LDO regulators. + + The regulators available on Palmas series chips vary depending + on the muxing. This is handled automatically in the driver by + reading the mux info from OTP. + +config REGULATOR_TPS51632 + depends on !BACKPORT_KERNEL_3_6 + tristate "TI TPS51632 Power Regulator" + depends on m + depends on I2C + depends on REGMAP_I2C + help + This driver supports TPS51632 voltage regulator chip. + The TPS51632 is 3-2-1 Phase D-Cap+ Step Down Driverless Controller + with Serial VID control and DVFS. + The voltage output can be configure through I2C interface or PWM + interface. + +config REGULATOR_TPS6105X + depends on !BACKPORT_KERNEL_3_6 + tristate "TI TPS6105X Power regulators" + depends on m + depends on TPS6105X + default y if TPS6105X + help + This driver supports TPS61050/TPS61052 voltage regulator chips. + It is a single boost converter primarily for white LEDs and + audio amplifiers. + +config REGULATOR_TPS62360 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "TI TPS6236x Power Regulator" + depends on m + depends on I2C + depends on REGMAP_I2C + help + This driver supports TPS6236x voltage regulator chip. This + regulator is meant for processor core supply. This chip is + high-frequency synchronous step down dc-dc converter optimized + for battery-powered portable applications. + +config REGULATOR_TPS65023 + depends on !BACKPORT_KERNEL_3_6 + tristate "TI TPS65023 Power regulators" + depends on m + depends on I2C + depends on REGMAP_I2C + help + This driver supports TPS65023 voltage regulator chips. TPS65023 provides + three step-down converters and two general-purpose LDO voltage regulators. + It supports TI's software based Class-2 SmartReflex implementation. + +config REGULATOR_TPS6507X + depends on !BACKPORT_KERNEL_3_6 + tristate "TI TPS6507X Power regulators" + depends on m + depends on I2C + help + This driver supports TPS6507X voltage regulator chips. TPS6507X provides + three step-down converters and two general-purpose LDO voltage regulators. + It supports TI's software based Class-2 SmartReflex implementation. + +config REGULATOR_TPS65090 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "TI TPS65090 Power regulator" + depends on m + depends on MFD_TPS65090 + help + This driver provides support for the voltage regulators on the + TI TPS65090 PMIC. + +config REGULATOR_TPS65217 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "TI TPS65217 Power regulators" + depends on m + depends on MFD_TPS65217 + help + This driver supports TPS65217 voltage regulator chips. TPS65217 + provides three step-down converters and four general-purpose LDO + voltage regulators. It supports software based voltage control + for different voltage domains + +config REGULATOR_TPS6524X + depends on !BACKPORT_KERNEL_3_6 + tristate "TI TPS6524X Power regulators" + depends on m + depends on SPI + help + This driver supports TPS6524X voltage regulator chips. TPS6524X + provides three step-down converters and two general-purpose LDO + voltage regulators. This device is interfaced using a customized + serial interface currently supported on the sequencer serial + port controller. + +config REGULATOR_TPS6586X + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "TI TPS6586X Power regulators" + depends on m + depends on MFD_TPS6586X + help + This driver supports TPS6586X voltage regulator chips. + +config REGULATOR_TPS65910 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "TI TPS65910/TPS65911 Power Regulators" + depends on m + depends on MFD_TPS65910 + help + This driver supports TPS65910/TPS65911 voltage regulator chips. + +config REGULATOR_TPS65912 + tristate "TI TPS65912 Power regulator" + depends on m + depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) + help + This driver supports TPS65912 voltage regulator chip. + +config REGULATOR_TPS80031 + tristate "TI TPS80031/TPS80032 power regualtor driver" + depends on m + depends on MFD_TPS80031 + help + TPS80031/ TPS80032 Fully Integrated Power Management with Power + Path and Battery Charger. It has 5 configurable step-down + converters, 11 general purpose LDOs, VBUS generator and digital + output to control regulators. + +config REGULATOR_TWL4030 + bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" + depends on TWL4030_CORE + help + This driver supports the voltage regulators provided by + this family of companion chips. + +config REGULATOR_VEXPRESS + tristate "Versatile Express regulators" + depends on m + depends on VEXPRESS_CONFIG + help + This driver provides support for voltage regulators available + on the ARM Ltd's Versatile Express platform. + +config REGULATOR_WM831X + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Wolfson Microelectronics WM831x PMIC regulators" + depends on m + depends on MFD_WM831X + help + Support the voltage and current regulators of the WM831x series + of PMIC devices. + +config REGULATOR_WM8350 + depends on BACKPORT_DISABLED_KCONFIG_OPTION + tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC" + depends on m + depends on MFD_WM8350 + help + This driver provides support for the voltage and current regulators + of the WM8350 AudioPlus PMIC. + +config REGULATOR_WM8400 + depends on !BACKPORT_KERNEL_3_5 + tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC" + depends on m + depends on MFD_WM8400 + help + This driver provides support for the voltage regulators of the + WM8400 AudioPlus PMIC. + +config REGULATOR_WM8994 + depends on !BACKPORT_KERNEL_3_8 + tristate "Wolfson Microelectronics WM8994 CODEC" + depends on m + depends on MFD_WM8994 + help + This driver provides support for the voltage regulators on the + WM8994 CODEC. + +config REGULATOR_AS3711 + tristate "AS3711 PMIC" + depends on m + depends on MFD_AS3711 + help + This driver provides support for the voltage regulators on the + AS3711 PMIC + +endif + diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile new file mode 100644 index 0000000..e4be924 --- /dev/null +++ b/drivers/regulator/Makefile @@ -0,0 +1,75 @@ +# +# Makefile for regulator drivers. +# + + +obj-$(CPTCFG_REGULATOR) += core.o dummy.o fixed-helper.o +obj-$(CONFIG_OF) += of_regulator.o +obj-$(CPTCFG_REGULATOR_FIXED_VOLTAGE) += fixed.o +obj-$(CPTCFG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o +obj-$(CPTCFG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o + +obj-$(CPTCFG_REGULATOR_88PM8607) += 88pm8607.o +obj-$(CPTCFG_REGULATOR_AAT2870) += aat2870-regulator.o +obj-$(CPTCFG_REGULATOR_AB3100) += ab3100.o +obj-$(CPTCFG_REGULATOR_AB8500) += ab8500.o ab8500-ext.o +obj-$(CPTCFG_REGULATOR_AD5398) += ad5398.o +obj-$(CPTCFG_REGULATOR_ANATOP) += anatop-regulator.o +obj-$(CPTCFG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o +obj-$(CPTCFG_REGULATOR_AS3711) += as3711-regulator.o +obj-$(CPTCFG_REGULATOR_DA903X) += da903x.o +obj-$(CPTCFG_REGULATOR_DA9052) += da9052-regulator.o +obj-$(CPTCFG_REGULATOR_DA9055) += da9055-regulator.o +obj-$(CPTCFG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o +obj-$(CPTCFG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o +obj-$(CPTCFG_REGULATOR_FAN53555) += fan53555.o +obj-$(CPTCFG_REGULATOR_GPIO) += gpio-regulator.o +obj-$(CPTCFG_REGULATOR_ISL6271A) += isl6271a-regulator.o +obj-$(CPTCFG_REGULATOR_LP3971) += lp3971.o +obj-$(CPTCFG_REGULATOR_LP3972) += lp3972.o +obj-$(CPTCFG_REGULATOR_LP872X) += lp872x.o +obj-$(CPTCFG_REGULATOR_LP8788) += lp8788-buck.o +obj-$(CPTCFG_REGULATOR_LP8788) += lp8788-ldo.o +obj-$(CPTCFG_REGULATOR_LP8755) += lp8755.o +obj-$(CPTCFG_REGULATOR_MAX1586) += max1586.o +obj-$(CPTCFG_REGULATOR_MAX8649) += max8649.o +obj-$(CPTCFG_REGULATOR_MAX8660) += max8660.o +obj-$(CPTCFG_REGULATOR_MAX8907) += max8907-regulator.o +obj-$(CPTCFG_REGULATOR_MAX8925) += max8925-regulator.o +obj-$(CPTCFG_REGULATOR_MAX8952) += max8952.o +obj-$(CPTCFG_REGULATOR_MAX8973) += max8973-regulator.o +obj-$(CPTCFG_REGULATOR_MAX8997) += max8997.o +obj-$(CPTCFG_REGULATOR_MAX8998) += max8998.o +obj-$(CPTCFG_REGULATOR_MAX77686) += max77686.o +obj-$(CPTCFG_REGULATOR_MC13783) += mc13783-regulator.o +obj-$(CPTCFG_REGULATOR_MC13892) += mc13892-regulator.o +obj-$(CPTCFG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o +obj-$(CPTCFG_REGULATOR_PALMAS) += palmas-regulator.o +obj-$(CPTCFG_REGULATOR_TPS51632) += tps51632-regulator.o +obj-$(CPTCFG_REGULATOR_PCAP) += pcap-regulator.o +obj-$(CPTCFG_REGULATOR_PCF50633) += pcf50633-regulator.o +obj-$(CPTCFG_REGULATOR_RC5T583) += rc5t583-regulator.o +obj-$(CPTCFG_REGULATOR_S2MPS11) += s2mps11.o +obj-$(CPTCFG_REGULATOR_S5M8767) += s5m8767.o +obj-$(CPTCFG_REGULATOR_TPS6105X) += tps6105x-regulator.o +obj-$(CPTCFG_REGULATOR_TPS62360) += tps62360-regulator.o +obj-$(CPTCFG_REGULATOR_TPS65023) += tps65023-regulator.o +obj-$(CPTCFG_REGULATOR_TPS6507X) += tps6507x-regulator.o +obj-$(CPTCFG_REGULATOR_TPS65090) += tps65090-regulator.o +obj-$(CPTCFG_REGULATOR_TPS65217) += tps65217-regulator.o +obj-$(CPTCFG_REGULATOR_TPS6524X) += tps6524x-regulator.o +obj-$(CPTCFG_REGULATOR_TPS6586X) += tps6586x-regulator.o +obj-$(CPTCFG_REGULATOR_TPS65910) += tps65910-regulator.o +obj-$(CPTCFG_REGULATOR_TPS65912) += tps65912-regulator.o +obj-$(CPTCFG_REGULATOR_TPS80031) += tps80031-regulator.o +obj-$(CPTCFG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CPTCFG_REGULATOR_VEXPRESS) += vexpress.o +obj-$(CPTCFG_REGULATOR_WM831X) += wm831x-dcdc.o +obj-$(CPTCFG_REGULATOR_WM831X) += wm831x-isink.o +obj-$(CPTCFG_REGULATOR_WM831X) += wm831x-ldo.o +obj-$(CPTCFG_REGULATOR_WM8350) += wm8350-regulator.o +obj-$(CPTCFG_REGULATOR_WM8400) += wm8400-regulator.o +obj-$(CPTCFG_REGULATOR_WM8994) += wm8994-regulator.o + + +ccflags-$(CPTCFG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c new file mode 100644 index 0000000..8b58763 --- /dev/null +++ b/drivers/regulator/aat2870-regulator.c @@ -0,0 +1,222 @@ +/* + * linux/drivers/regulator/aat2870-regulator.c + * + * Copyright (c) 2011, NVIDIA Corporation. + * Author: Jin Park + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct aat2870_regulator { + struct aat2870_data *aat2870; + struct regulator_desc desc; + + u8 enable_addr; + u8 enable_shift; + u8 enable_mask; + + u8 voltage_addr; + u8 voltage_shift; + u8 voltage_mask; +}; + +static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = ri->aat2870; + + return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask, + selector << ri->voltage_shift); +} + +static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = ri->aat2870; + u8 val; + int ret; + + ret = aat2870->read(aat2870, ri->voltage_addr, &val); + if (ret) + return ret; + + return (val & ri->voltage_mask) >> ri->voltage_shift; +} + +static int aat2870_ldo_enable(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = ri->aat2870; + + return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, + ri->enable_mask); +} + +static int aat2870_ldo_disable(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = ri->aat2870; + + return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0); +} + +static int aat2870_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = ri->aat2870; + u8 val; + int ret; + + ret = aat2870->read(aat2870, ri->enable_addr, &val); + if (ret) + return ret; + + return val & ri->enable_mask ? 1 : 0; +} + +static struct regulator_ops aat2870_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = aat2870_ldo_set_voltage_sel, + .get_voltage_sel = aat2870_ldo_get_voltage_sel, + .enable = aat2870_ldo_enable, + .disable = aat2870_ldo_disable, + .is_enabled = aat2870_ldo_is_enabled, +}; + +static const unsigned int aat2870_ldo_voltages[] = { + 1200000, 1300000, 1500000, 1600000, + 1800000, 2000000, 2200000, 2500000, + 2600000, 2700000, 2800000, 2900000, + 3000000, 3100000, 3200000, 3300000, +}; + +#define AAT2870_LDO(ids) \ + { \ + .desc = { \ + .name = #ids, \ + .id = AAT2870_ID_##ids, \ + .n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \ + .volt_table = aat2870_ldo_voltages, \ + .ops = &aat2870_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +static struct aat2870_regulator aat2870_regulators[] = { + AAT2870_LDO(LDOA), + AAT2870_LDO(LDOB), + AAT2870_LDO(LDOC), + AAT2870_LDO(LDOD), +}; + +static struct aat2870_regulator *aat2870_get_regulator(int id) +{ + struct aat2870_regulator *ri = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) { + ri = &aat2870_regulators[i]; + if (ri->desc.id == id) + break; + } + + if (i == ARRAY_SIZE(aat2870_regulators)) + return NULL; + + ri->enable_addr = AAT2870_LDO_EN; + ri->enable_shift = id - AAT2870_ID_LDOA; + ri->enable_mask = 0x1 << ri->enable_shift; + + ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ? + AAT2870_LDO_CD : AAT2870_LDO_AB; + ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4; + ri->voltage_mask = 0xF << ri->voltage_shift; + + return ri; +} + +static int aat2870_regulator_probe(struct platform_device *pdev) +{ + struct aat2870_regulator *ri; + struct regulator_config config = { }; + struct regulator_dev *rdev; + + ri = aat2870_get_regulator(pdev->id); + if (!ri) { + dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id); + return -EINVAL; + } + ri->aat2870 = dev_get_drvdata(pdev->dev.parent); + + config.dev = &pdev->dev; + config.driver_data = ri; + config.init_data = pdev->dev.platform_data; + + rdev = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int aat2870_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver aat2870_regulator_driver = { + .driver = { + .name = "aat2870-regulator", + .owner = THIS_MODULE, + }, + .probe = aat2870_regulator_probe, + .remove = aat2870_regulator_remove, +}; + +static int __init aat2870_regulator_init(void) +{ + return platform_driver_register(&aat2870_regulator_driver); +} +subsys_initcall(aat2870_regulator_init); + +static void __exit aat2870_regulator_exit(void) +{ + platform_driver_unregister(&aat2870_regulator_driver); +} +module_exit(aat2870_regulator_exit); + +MODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jin Park "); +MODULE_ALIAS("platform:aat2870-regulator"); diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c new file mode 100644 index 0000000..3be9e46 --- /dev/null +++ b/drivers/regulator/ab3100.c @@ -0,0 +1,747 @@ +/* + * drivers/regulator/ab3100.c + * + * Copyright (C) 2008-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Low-level control of the AB3100 IC Low Dropout (LDO) + * regulators, external regulator and buck converter + * Author: Mattias Wallin + * Author: Linus Walleij + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LDO registers and some handy masking definitions for AB3100 */ +#define AB3100_LDO_A 0x40 +#define AB3100_LDO_C 0x41 +#define AB3100_LDO_D 0x42 +#define AB3100_LDO_E 0x43 +#define AB3100_LDO_E_SLEEP 0x44 +#define AB3100_LDO_F 0x45 +#define AB3100_LDO_G 0x46 +#define AB3100_LDO_H 0x47 +#define AB3100_LDO_H_SLEEP_MODE 0 +#define AB3100_LDO_H_SLEEP_EN 2 +#define AB3100_LDO_ON 4 +#define AB3100_LDO_H_VSEL_AC 5 +#define AB3100_LDO_K 0x48 +#define AB3100_LDO_EXT 0x49 +#define AB3100_BUCK 0x4A +#define AB3100_BUCK_SLEEP 0x4B +#define AB3100_REG_ON_MASK 0x10 + +/** + * struct ab3100_regulator + * A struct passed around the individual regulator functions + * @platform_device: platform device holding this regulator + * @dev: handle to the device + * @plfdata: AB3100 platform data passed in at probe time + * @regreg: regulator register number in the AB3100 + */ +struct ab3100_regulator { + struct regulator_dev *rdev; + struct device *dev; + struct ab3100_platform_data *plfdata; + u8 regreg; +}; + +/* The order in which registers are initialized */ +static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = { + AB3100_LDO_A, + AB3100_LDO_C, + AB3100_LDO_E, + AB3100_LDO_E_SLEEP, + AB3100_LDO_F, + AB3100_LDO_G, + AB3100_LDO_H, + AB3100_LDO_K, + AB3100_LDO_EXT, + AB3100_BUCK, + AB3100_BUCK_SLEEP, + AB3100_LDO_D, +}; + +/* Preset (hardware defined) voltages for these regulators */ +#define LDO_A_VOLTAGE 2750000 +#define LDO_C_VOLTAGE 2650000 +#define LDO_D_VOLTAGE 2650000 + +static const unsigned int ldo_e_buck_typ_voltages[] = { + 1800000, + 1400000, + 1300000, + 1200000, + 1100000, + 1050000, + 900000, +}; + +static const unsigned int ldo_f_typ_voltages[] = { + 1800000, + 1400000, + 1300000, + 1200000, + 1100000, + 1050000, + 2500000, + 2650000, +}; + +static const unsigned int ldo_g_typ_voltages[] = { + 2850000, + 2750000, + 1800000, + 1500000, +}; + +static const unsigned int ldo_h_typ_voltages[] = { + 2750000, + 1800000, + 1500000, + 1200000, +}; + +static const unsigned int ldo_k_typ_voltages[] = { + 2750000, + 1800000, +}; + + +/* The regulator devices */ +static struct ab3100_regulator +ab3100_regulators[AB3100_NUM_REGULATORS] = { + { + .regreg = AB3100_LDO_A, + }, + { + .regreg = AB3100_LDO_C, + }, + { + .regreg = AB3100_LDO_D, + }, + { + .regreg = AB3100_LDO_E, + }, + { + .regreg = AB3100_LDO_F, + }, + { + .regreg = AB3100_LDO_G, + }, + { + .regreg = AB3100_LDO_H, + }, + { + .regreg = AB3100_LDO_K, + }, + { + .regreg = AB3100_LDO_EXT, + /* No voltages for the external regulator */ + }, + { + .regreg = AB3100_BUCK, + }, +}; + +/* + * General functions for enable, disable and is_enabled used for + * LDO: A,C,E,F,G,H,K,EXT and BUCK + */ +static int ab3100_enable_regulator(struct regulator_dev *reg) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + int err; + u8 regval; + + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, + ®val); + if (err) { + dev_warn(®->dev, "failed to get regid %d value\n", + abreg->regreg); + return err; + } + + /* The regulator is already on, no reason to go further */ + if (regval & AB3100_REG_ON_MASK) + return 0; + + regval |= AB3100_REG_ON_MASK; + + err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, + regval); + if (err) { + dev_warn(®->dev, "failed to set regid %d value\n", + abreg->regreg); + return err; + } + + return 0; +} + +static int ab3100_disable_regulator(struct regulator_dev *reg) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + int err; + u8 regval; + + /* + * LDO D is a special regulator. When it is disabled, the entire + * system is shut down. So this is handled specially. + */ + pr_info("Called ab3100_disable_regulator\n"); + if (abreg->regreg == AB3100_LDO_D) { + dev_info(®->dev, "disabling LDO D - shut down system\n"); + /* Setting LDO D to 0x00 cuts the power to the SoC */ + return abx500_set_register_interruptible(abreg->dev, 0, + AB3100_LDO_D, 0x00U); + } + + /* + * All other regulators are handled here + */ + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, + ®val); + if (err) { + dev_err(®->dev, "unable to get register 0x%x\n", + abreg->regreg); + return err; + } + regval &= ~AB3100_REG_ON_MASK; + return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, + regval); +} + +static int ab3100_is_enabled_regulator(struct regulator_dev *reg) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + u8 regval; + int err; + + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, + ®val); + if (err) { + dev_err(®->dev, "unable to get register 0x%x\n", + abreg->regreg); + return err; + } + + return regval & AB3100_REG_ON_MASK; +} + +static int ab3100_get_voltage_regulator(struct regulator_dev *reg) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + u8 regval; + int err; + + /* + * For variable types, read out setting and index into + * supplied voltage list. + */ + err = abx500_get_register_interruptible(abreg->dev, 0, + abreg->regreg, ®val); + if (err) { + dev_warn(®->dev, + "failed to get regulator value in register %02x\n", + abreg->regreg); + return err; + } + + /* The 3 highest bits index voltages */ + regval &= 0xE0; + regval >>= 5; + + if (regval >= reg->desc->n_voltages) { + dev_err(®->dev, + "regulator register %02x contains an illegal voltage setting\n", + abreg->regreg); + return -EINVAL; + } + + return reg->desc->volt_table[regval]; +} + +static int ab3100_set_voltage_regulator_sel(struct regulator_dev *reg, + unsigned selector) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + u8 regval; + int err; + + err = abx500_get_register_interruptible(abreg->dev, 0, + abreg->regreg, ®val); + if (err) { + dev_warn(®->dev, + "failed to get regulator register %02x\n", + abreg->regreg); + return err; + } + + /* The highest three bits control the variable regulators */ + regval &= ~0xE0; + regval |= (selector << 5); + + err = abx500_set_register_interruptible(abreg->dev, 0, + abreg->regreg, regval); + if (err) + dev_warn(®->dev, "failed to set regulator register %02x\n", + abreg->regreg); + + return err; +} + +static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, + int uV) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + u8 regval; + int err; + int bestindex; + u8 targetreg; + + if (abreg->regreg == AB3100_LDO_E) + targetreg = AB3100_LDO_E_SLEEP; + else if (abreg->regreg == AB3100_BUCK) + targetreg = AB3100_BUCK_SLEEP; + else + return -EINVAL; + + /* LDO E and BUCK have special suspend voltages you can set */ + bestindex = regulator_map_voltage_iterate(reg, uV, uV); + + err = abx500_get_register_interruptible(abreg->dev, 0, + targetreg, ®val); + if (err) { + dev_warn(®->dev, + "failed to get regulator register %02x\n", + targetreg); + return err; + } + + /* The highest three bits control the variable regulators */ + regval &= ~0xE0; + regval |= (bestindex << 5); + + err = abx500_set_register_interruptible(abreg->dev, 0, + targetreg, regval); + if (err) + dev_warn(®->dev, "failed to set regulator register %02x\n", + abreg->regreg); + + return err; +} + +/* + * The external regulator can just define a fixed voltage. + */ +static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg) +{ + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); + + if (abreg->plfdata) + return abreg->plfdata->external_voltage; + else + /* TODO: encode external voltage into device tree */ + return 0; +} + +static struct regulator_ops regulator_ops_fixed = { + .list_voltage = regulator_list_voltage_linear, + .enable = ab3100_enable_regulator, + .disable = ab3100_disable_regulator, + .is_enabled = ab3100_is_enabled_regulator, +}; + +static struct regulator_ops regulator_ops_variable = { + .enable = ab3100_enable_regulator, + .disable = ab3100_disable_regulator, + .is_enabled = ab3100_is_enabled_regulator, + .get_voltage = ab3100_get_voltage_regulator, + .set_voltage_sel = ab3100_set_voltage_regulator_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_ops regulator_ops_variable_sleepable = { + .enable = ab3100_enable_regulator, + .disable = ab3100_disable_regulator, + .is_enabled = ab3100_is_enabled_regulator, + .get_voltage = ab3100_get_voltage_regulator, + .set_voltage_sel = ab3100_set_voltage_regulator_sel, + .set_suspend_voltage = ab3100_set_suspend_voltage_regulator, + .list_voltage = regulator_list_voltage_table, +}; + +/* + * LDO EXT is an external regulator so it is really + * not possible to set any voltage locally here, AB3100 + * is an on/off switch plain an simple. The external + * voltage is defined in the board set-up if any. + */ +static struct regulator_ops regulator_ops_external = { + .enable = ab3100_enable_regulator, + .disable = ab3100_disable_regulator, + .is_enabled = ab3100_is_enabled_regulator, + .get_voltage = ab3100_get_voltage_regulator_external, +}; + +static struct regulator_desc +ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { + { + .name = "LDO_A", + .id = AB3100_LDO_A, + .ops = ®ulator_ops_fixed, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = LDO_A_VOLTAGE, + .enable_time = 200, + }, + { + .name = "LDO_C", + .id = AB3100_LDO_C, + .ops = ®ulator_ops_fixed, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = LDO_C_VOLTAGE, + .enable_time = 200, + }, + { + .name = "LDO_D", + .id = AB3100_LDO_D, + .ops = ®ulator_ops_fixed, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = LDO_D_VOLTAGE, + .enable_time = 200, + }, + { + .name = "LDO_E", + .id = AB3100_LDO_E, + .ops = ®ulator_ops_variable_sleepable, + .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages), + .volt_table = ldo_e_buck_typ_voltages, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_time = 200, + }, + { + .name = "LDO_F", + .id = AB3100_LDO_F, + .ops = ®ulator_ops_variable, + .n_voltages = ARRAY_SIZE(ldo_f_typ_voltages), + .volt_table = ldo_f_typ_voltages, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_time = 600, + }, + { + .name = "LDO_G", + .id = AB3100_LDO_G, + .ops = ®ulator_ops_variable, + .n_voltages = ARRAY_SIZE(ldo_g_typ_voltages), + .volt_table = ldo_g_typ_voltages, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_time = 400, + }, + { + .name = "LDO_H", + .id = AB3100_LDO_H, + .ops = ®ulator_ops_variable, + .n_voltages = ARRAY_SIZE(ldo_h_typ_voltages), + .volt_table = ldo_h_typ_voltages, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_time = 200, + }, + { + .name = "LDO_K", + .id = AB3100_LDO_K, + .ops = ®ulator_ops_variable, + .n_voltages = ARRAY_SIZE(ldo_k_typ_voltages), + .volt_table = ldo_k_typ_voltages, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_time = 200, + }, + { + .name = "LDO_EXT", + .id = AB3100_LDO_EXT, + .ops = ®ulator_ops_external, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "BUCK", + .id = AB3100_BUCK, + .ops = ®ulator_ops_variable_sleepable, + .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages), + .volt_table = ldo_e_buck_typ_voltages, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_time = 1000, + }, +}; + +static int ab3100_regulator_register(struct platform_device *pdev, + struct ab3100_platform_data *plfdata, + struct regulator_init_data *init_data, + struct device_node *np, + int id) +{ + struct regulator_desc *desc; + struct ab3100_regulator *reg; + struct regulator_dev *rdev; + struct regulator_config config = { }; + int err, i; + + for (i = 0; i < AB3100_NUM_REGULATORS; i++) { + desc = &ab3100_regulator_desc[i]; + if (desc->id == id) + break; + } + if (desc->id != id) + return -ENODEV; + + /* Same index used for this array */ + reg = &ab3100_regulators[i]; + + /* + * Initialize per-regulator struct. + * Inherit platform data, this comes down from the + * i2c boarddata, from the machine. So if you want to + * see what it looks like for a certain machine, go + * into the machine I2C setup. + */ + reg->dev = &pdev->dev; + if (plfdata) { + reg->plfdata = plfdata; + config.init_data = &plfdata->reg_constraints[i]; + } else if (np) { + config.of_node = np; + config.init_data = init_data; + } + config.dev = &pdev->dev; + config.driver_data = reg; + + rdev = regulator_register(desc, &config); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); + dev_err(&pdev->dev, + "%s: failed to register regulator %s err %d\n", + __func__, desc->name, + err); + return err; + } + + /* Then set a pointer back to the registered regulator */ + reg->rdev = rdev; + return 0; +} + +static struct of_regulator_match ab3100_regulator_matches[] = { + { .name = "ab3100_ldo_a", .driver_data = (void *) AB3100_LDO_A, }, + { .name = "ab3100_ldo_c", .driver_data = (void *) AB3100_LDO_C, }, + { .name = "ab3100_ldo_d", .driver_data = (void *) AB3100_LDO_D, }, + { .name = "ab3100_ldo_e", .driver_data = (void *) AB3100_LDO_E, }, + { .name = "ab3100_ldo_f", .driver_data = (void *) AB3100_LDO_F }, + { .name = "ab3100_ldo_g", .driver_data = (void *) AB3100_LDO_G }, + { .name = "ab3100_ldo_h", .driver_data = (void *) AB3100_LDO_H }, + { .name = "ab3100_ldo_k", .driver_data = (void *) AB3100_LDO_K }, + { .name = "ab3100_ext", .driver_data = (void *) AB3100_LDO_EXT }, + { .name = "ab3100_buck", .driver_data = (void *) AB3100_BUCK }, +}; + +/* + * Initial settings of ab3100 registers. + * Common for below LDO regulator settings are that + * bit 7-5 controls voltage. Bit 4 turns regulator ON(1) or OFF(0). + * Bit 3-2 controls sleep enable and bit 1-0 controls sleep mode. + */ +/* LDO_A 0x16: 2.75V, ON, SLEEP_A, SLEEP OFF GND */ +#define LDO_A_SETTING 0x16 +/* LDO_C 0x10: 2.65V, ON, SLEEP_A or B, SLEEP full power */ +#define LDO_C_SETTING 0x10 +/* LDO_D 0x10: 2.65V, ON, sleep mode not used */ +#define LDO_D_SETTING 0x10 +/* LDO_E 0x10: 1.8V, ON, SLEEP_A or B, SLEEP full power */ +#define LDO_E_SETTING 0x10 +/* LDO_E SLEEP 0x00: 1.8V, not used, SLEEP_A or B, not used */ +#define LDO_E_SLEEP_SETTING 0x00 +/* LDO_F 0xD0: 2.5V, ON, SLEEP_A or B, SLEEP full power */ +#define LDO_F_SETTING 0xD0 +/* LDO_G 0x00: 2.85V, OFF, SLEEP_A or B, SLEEP full power */ +#define LDO_G_SETTING 0x00 +/* LDO_H 0x18: 2.75V, ON, SLEEP_B, SLEEP full power */ +#define LDO_H_SETTING 0x18 +/* LDO_K 0x00: 2.75V, OFF, SLEEP_A or B, SLEEP full power */ +#define LDO_K_SETTING 0x00 +/* LDO_EXT 0x00: Voltage not set, OFF, not used, not used */ +#define LDO_EXT_SETTING 0x00 +/* BUCK 0x7D: 1.2V, ON, SLEEP_A and B, SLEEP low power */ +#define BUCK_SETTING 0x7D +/* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */ +#define BUCK_SLEEP_SETTING 0xAC + +static const u8 ab3100_reg_initvals[] = { + LDO_A_SETTING, + LDO_C_SETTING, + LDO_E_SETTING, + LDO_E_SLEEP_SETTING, + LDO_F_SETTING, + LDO_G_SETTING, + LDO_H_SETTING, + LDO_K_SETTING, + LDO_EXT_SETTING, + BUCK_SETTING, + BUCK_SLEEP_SETTING, + LDO_D_SETTING, +}; + +static int ab3100_regulators_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < AB3100_NUM_REGULATORS; i++) { + struct ab3100_regulator *reg = &ab3100_regulators[i]; + + regulator_unregister(reg->rdev); + reg->rdev = NULL; + } + return 0; +} + +static int +ab3100_regulator_of_probe(struct platform_device *pdev, struct device_node *np) +{ + int err, i; + + /* + * Set up the regulator registers, as was previously done with + * platform data. + */ + /* Set up regulators */ + for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) { + err = abx500_set_register_interruptible(&pdev->dev, 0, + ab3100_reg_init_order[i], + ab3100_reg_initvals[i]); + if (err) { + dev_err(&pdev->dev, "regulator initialization failed with error %d\n", + err); + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(ab3100_regulator_matches); i++) { + err = ab3100_regulator_register( + pdev, NULL, ab3100_regulator_matches[i].init_data, + ab3100_regulator_matches[i].of_node, + (int) ab3100_regulator_matches[i].driver_data); + if (err) { + ab3100_regulators_remove(pdev); + return err; + } + } + + return 0; +} + + +static int ab3100_regulators_probe(struct platform_device *pdev) +{ + struct ab3100_platform_data *plfdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + int err = 0; + u8 data; + int i; + + /* Check chip state */ + err = abx500_get_register_interruptible(&pdev->dev, 0, + AB3100_LDO_D, &data); + if (err) { + dev_err(&pdev->dev, "could not read initial status of LDO_D\n"); + return err; + } + if (data & 0x10) + dev_notice(&pdev->dev, + "chip is already in active mode (Warm start)\n"); + else + dev_notice(&pdev->dev, + "chip is in inactive mode (Cold start)\n"); + + if (np) { + err = of_regulator_match(&pdev->dev, np, + ab3100_regulator_matches, + ARRAY_SIZE(ab3100_regulator_matches)); + if (err < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", err); + return err; + } + return ab3100_regulator_of_probe(pdev, np); + } + + /* Set up regulators */ + for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) { + err = abx500_set_register_interruptible(&pdev->dev, 0, + ab3100_reg_init_order[i], + plfdata->reg_initvals[i]); + if (err) { + dev_err(&pdev->dev, "regulator initialization failed with error %d\n", + err); + return err; + } + } + + /* Register the regulators */ + for (i = 0; i < AB3100_NUM_REGULATORS; i++) { + struct regulator_desc *desc = &ab3100_regulator_desc[i]; + + err = ab3100_regulator_register(pdev, plfdata, NULL, NULL, + desc->id); + if (err) { + ab3100_regulators_remove(pdev); + return err; + } + } + + return 0; +} + +static struct platform_driver ab3100_regulators_driver = { + .driver = { + .name = "ab3100-regulators", + .owner = THIS_MODULE, + }, + .probe = ab3100_regulators_probe, + .remove = ab3100_regulators_remove, +}; + +static __init int ab3100_regulators_init(void) +{ + return platform_driver_register(&ab3100_regulators_driver); +} + +static __exit void ab3100_regulators_exit(void) +{ + platform_driver_unregister(&ab3100_regulators_driver); +} + +subsys_initcall(ab3100_regulators_init); +module_exit(ab3100_regulators_exit); + +MODULE_AUTHOR("Mattias Wallin "); +MODULE_DESCRIPTION("AB3100 Regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ab3100-regulators"); diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c new file mode 100644 index 0000000..b4d4547 --- /dev/null +++ b/drivers/regulator/ab8500-ext.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * Authors: Bengt Jonsson + * + * This file is based on drivers/regulator/ab8500.c + * + * AB8500 external regulators + * + * ab8500-ext supports the following regulators: + * - VextSupply3 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ab8500_ext_regulator_info - ab8500 regulator information + * @dev: device pointer + * @desc: regulator description + * @rdev: regulator device + * @cfg: regulator configuration (extension of regulator FW configuration) + * @update_bank: bank to control on/off + * @update_reg: register to control on/off + * @update_mask: mask to enable/disable and set mode of regulator + * @update_val: bits holding the regulator current mode + * @update_val_hp: bits to set EN pin active (LPn pin deactive) + * normally this means high power mode + * @update_val_lp: bits to set EN pin active and LPn pin active + * normally this means low power mode + * @update_val_hw: bits to set regulator pins in HW control + * SysClkReq pins and logic will choose mode + */ +struct ab8500_ext_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct ab8500_ext_regulator_cfg *cfg; + u8 update_bank; + u8 update_reg; + u8 update_mask; + u8 update_val; + u8 update_val_hp; + u8 update_val_lp; + u8 update_val_hw; +}; + +static int ab8500_ext_regulator_enable(struct regulator_dev *rdev) +{ + int ret; + struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + /* + * To satisfy both HW high power request and SW request, the regulator + * must be on in high power. + */ + if (info->cfg && info->cfg->hwreq) + regval = info->update_val_hp; + else + regval = info->update_val; + + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, + info->update_mask, regval); + if (ret < 0) { + dev_err(rdev_get_dev(info->rdev), + "couldn't set enable bits for regulator\n"); + return ret; + } + + dev_dbg(rdev_get_dev(rdev), + "%s-enable (bank, reg, mask, value): 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, regval); + + return 0; +} + +static int ab8500_ext_regulator_disable(struct regulator_dev *rdev) +{ + int ret; + struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + /* + * Set the regulator in HW request mode if configured + */ + if (info->cfg && info->cfg->hwreq) + regval = info->update_val_hw; + else + regval = 0; + + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, + info->update_mask, regval); + if (ret < 0) { + dev_err(rdev_get_dev(info->rdev), + "couldn't set disable bits for regulator\n"); + return ret; + } + + dev_dbg(rdev_get_dev(rdev), "%s-disable (bank, reg, mask, value):" + " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, regval); + + return 0; +} + +static int ab8500_ext_regulator_is_enabled(struct regulator_dev *rdev) +{ + int ret; + struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + ret = abx500_get_register_interruptible(info->dev, + info->update_bank, info->update_reg, ®val); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read 0x%x register\n", info->update_reg); + return ret; + } + + dev_dbg(rdev_get_dev(rdev), "%s-is_enabled (bank, reg, mask, value):" + " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, regval); + + if (((regval & info->update_mask) == info->update_val_lp) || + ((regval & info->update_mask) == info->update_val_hp)) + return 1; + else + return 0; +} + +static int ab8500_ext_regulator_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + int ret = 0; + struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_NORMAL: + regval = info->update_val_hp; + break; + case REGULATOR_MODE_IDLE: + regval = info->update_val_lp; + break; + + default: + return -EINVAL; + } + + /* If regulator is enabled and info->cfg->hwreq is set, the regulator + must be on in high power, so we don't need to write the register with + the same value. + */ + if (ab8500_ext_regulator_is_enabled(rdev) && + !(info->cfg && info->cfg->hwreq)) { + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, + info->update_mask, regval); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "Could not set regulator mode.\n"); + return ret; + } + + dev_dbg(rdev_get_dev(rdev), + "%s-set_mode (bank, reg, mask, value): " + "0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, regval); + } + + info->update_val = regval; + + return 0; +} + +static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev) +{ + struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + if (info->update_val == info->update_val_hp) + ret = REGULATOR_MODE_NORMAL; + else if (info->update_val == info->update_val_lp) + ret = REGULATOR_MODE_IDLE; + else + ret = -EINVAL; + + return ret; +} + +static int ab8500_ext_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct regulation_constraints *regu_constraints = rdev->constraints; + + if (regu_constraints == NULL) { + dev_err(rdev_get_dev(rdev), "regulator constraints null pointer\n"); + return -EINVAL; + } + /* return the uV for the fixed regulators */ + if (regu_constraints->min_uV && regu_constraints->max_uV) { + if (regu_constraints->min_uV == regu_constraints->max_uV) + return regu_constraints->min_uV; + } + return -EINVAL; +} + +static struct regulator_ops ab8500_ext_regulator_ops = { + .enable = ab8500_ext_regulator_enable, + .disable = ab8500_ext_regulator_disable, + .is_enabled = ab8500_ext_regulator_is_enabled, + .set_mode = ab8500_ext_regulator_set_mode, + .get_mode = ab8500_ext_regulator_get_mode, + .list_voltage = ab8500_ext_list_voltage, +}; + +static struct ab8500_ext_regulator_info + ab8500_ext_regulator_info[AB8500_NUM_EXT_REGULATORS] = { + [AB8500_EXT_SUPPLY1] = { + .desc = { + .name = "VEXTSUPPLY1", + .ops = &ab8500_ext_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_EXT_SUPPLY1, + .owner = THIS_MODULE, + .n_voltages = 1, + }, + .update_bank = 0x04, + .update_reg = 0x08, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_hp = 0x01, + .update_val_lp = 0x03, + .update_val_hw = 0x02, + }, + [AB8500_EXT_SUPPLY2] = { + .desc = { + .name = "VEXTSUPPLY2", + .ops = &ab8500_ext_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_EXT_SUPPLY2, + .owner = THIS_MODULE, + .n_voltages = 1, + }, + .update_bank = 0x04, + .update_reg = 0x08, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_hp = 0x04, + .update_val_lp = 0x0c, + .update_val_hw = 0x08, + }, + [AB8500_EXT_SUPPLY3] = { + .desc = { + .name = "VEXTSUPPLY3", + .ops = &ab8500_ext_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_EXT_SUPPLY3, + .owner = THIS_MODULE, + .n_voltages = 1, + }, + .update_bank = 0x04, + .update_reg = 0x08, + .update_mask = 0x30, + .update_val = 0x10, + .update_val_hp = 0x10, + .update_val_lp = 0x30, + .update_val_hw = 0x20, + }, +}; + +int ab8500_ext_regulator_init(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_platform_data *ppdata; + struct ab8500_regulator_platform_data *pdata; + struct regulator_config config = { }; + int i, err; + + if (!ab8500) { + dev_err(&pdev->dev, "null mfd parent\n"); + return -EINVAL; + } + ppdata = dev_get_platdata(ab8500->dev); + if (!ppdata) { + dev_err(&pdev->dev, "null parent pdata\n"); + return -EINVAL; + } + + pdata = ppdata->regulator; + if (!pdata) { + dev_err(&pdev->dev, "null pdata\n"); + return -EINVAL; + } + + /* make sure the platform data has the correct size */ + if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) { + dev_err(&pdev->dev, "Configuration error: size mismatch.\n"); + return -EINVAL; + } + + /* check for AB8500 2.x */ + if (is_ab8500_2p0_or_earlier(ab8500)) { + struct ab8500_ext_regulator_info *info; + + /* VextSupply3LPn is inverted on AB8500 2.x */ + info = &ab8500_ext_regulator_info[AB8500_EXT_SUPPLY3]; + info->update_val = 0x30; + info->update_val_hp = 0x30; + info->update_val_lp = 0x10; + } + + /* register all regulators */ + for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) { + struct ab8500_ext_regulator_info *info = NULL; + + /* assign per-regulator data */ + info = &ab8500_ext_regulator_info[i]; + info->dev = &pdev->dev; + info->cfg = (struct ab8500_ext_regulator_cfg *) + pdata->ext_regulator[i].driver_data; + + config.dev = &pdev->dev; + config.init_data = &pdata->ext_regulator[i]; + config.driver_data = info; + + /* register regulator with framework */ + info->rdev = regulator_register(&info->desc, &config); + if (IS_ERR(info->rdev)) { + err = PTR_ERR(info->rdev); + dev_err(&pdev->dev, "failed to register regulator %s\n", + info->desc.name); + /* when we fail, un-register all earlier regulators */ + while (--i >= 0) { + info = &ab8500_ext_regulator_info[i]; + regulator_unregister(info->rdev); + } + return err; + } + + dev_dbg(rdev_get_dev(info->rdev), + "%s-probed\n", info->desc.name); + } + + return 0; +} + +void ab8500_ext_regulator_exit(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) { + struct ab8500_ext_regulator_info *info = NULL; + info = &ab8500_ext_regulator_info[i]; + + dev_vdbg(rdev_get_dev(info->rdev), + "%s-remove\n", info->desc.name); + + regulator_unregister(info->rdev); + } +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Bengt Jonsson "); +MODULE_DESCRIPTION("AB8500 external regulator driver"); +MODULE_ALIAS("platform:ab8500-ext-regulator"); diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c new file mode 100644 index 0000000..f6656b8 --- /dev/null +++ b/drivers/regulator/ab8500.c @@ -0,0 +1,3239 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * Authors: Sundar Iyer for ST-Ericsson + * Bengt Jonsson for ST-Ericsson + * Daniel Willerud for ST-Ericsson + * + * AB8500 peripheral regulators + * + * AB8500 supports the following regulators: + * VAUX1/2/3, VINTCORE, VTVOUT, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA + * + * AB8505 supports the following regulators: + * VAUX1/2/3/4/5/6, VINTCORE, VADC, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ab8500_shared_mode - is used when mode is shared between + * two regulators. + * @shared_regulator: pointer to the other sharing regulator + * @lp_mode_req: low power mode requested by this regulator + */ +struct ab8500_shared_mode { + struct ab8500_regulator_info *shared_regulator; + bool lp_mode_req; +}; + +/** + * struct ab8500_regulator_info - ab8500 regulator information + * @dev: device pointer + * @desc: regulator description + * @regulator_dev: regulator device + * @shared_mode: used when mode is shared between two regulators + * @load_lp_uA: maximum load in idle (low power) mode + * @update_bank: bank to control on/off + * @update_reg: register to control on/off + * @update_mask: mask to enable/disable and set mode of regulator + * @update_val: bits holding the regulator current mode + * @update_val_idle: bits to enable the regulator in idle (low power) mode + * @update_val_normal: bits to enable the regulator in normal (high power) mode + * @mode_bank: bank with location of mode register + * @mode_reg: mode register + * @mode_mask: mask for setting mode + * @mode_val_idle: mode setting for low power + * @mode_val_normal: mode setting for normal power + * @voltage_bank: bank to control regulator voltage + * @voltage_reg: register to control regulator voltage + * @voltage_mask: mask to control regulator voltage + */ +struct ab8500_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *regulator; + struct ab8500_shared_mode *shared_mode; + int load_lp_uA; + u8 update_bank; + u8 update_reg; + u8 update_mask; + u8 update_val; + u8 update_val_idle; + u8 update_val_normal; + u8 mode_bank; + u8 mode_reg; + u8 mode_mask; + u8 mode_val_idle; + u8 mode_val_normal; + u8 voltage_bank; + u8 voltage_reg; + u8 voltage_mask; + struct { + u8 voltage_limit; + u8 voltage_bank; + u8 voltage_reg; + u8 voltage_mask; + } expand_register; +}; + +/* voltage tables for the vauxn/vintcore supplies */ +static const unsigned int ldo_vauxn_voltages[] = { + 1100000, + 1200000, + 1300000, + 1400000, + 1500000, + 1800000, + 1850000, + 1900000, + 2500000, + 2650000, + 2700000, + 2750000, + 2800000, + 2900000, + 3000000, + 3300000, +}; + +static const unsigned int ldo_vaux3_voltages[] = { + 1200000, + 1500000, + 1800000, + 2100000, + 2500000, + 2750000, + 2790000, + 2910000, +}; + +static const unsigned int ldo_vaux56_voltages[] = { + 1800000, + 1050000, + 1100000, + 1200000, + 1500000, + 2200000, + 2500000, + 2790000, +}; + +static const unsigned int ldo_vaux3_ab8540_voltages[] = { + 1200000, + 1500000, + 1800000, + 2100000, + 2500000, + 2750000, + 2790000, + 2910000, + 3050000, +}; + +static const unsigned int ldo_vaux56_ab8540_voltages[] = { + 750000, 760000, 770000, 780000, 790000, 800000, + 810000, 820000, 830000, 840000, 850000, 860000, + 870000, 880000, 890000, 900000, 910000, 920000, + 930000, 940000, 950000, 960000, 970000, 980000, + 990000, 1000000, 1010000, 1020000, 1030000, + 1040000, 1050000, 1060000, 1070000, 1080000, + 1090000, 1100000, 1110000, 1120000, 1130000, + 1140000, 1150000, 1160000, 1170000, 1180000, + 1190000, 1200000, 1210000, 1220000, 1230000, + 1240000, 1250000, 1260000, 1270000, 1280000, + 1290000, 1300000, 1310000, 1320000, 1330000, + 1340000, 1350000, 1360000, 1800000, 2790000, +}; + +static const unsigned int ldo_vintcore_voltages[] = { + 1200000, + 1225000, + 1250000, + 1275000, + 1300000, + 1325000, + 1350000, +}; + +static const unsigned int ldo_sdio_voltages[] = { + 1160000, + 1050000, + 1100000, + 1500000, + 1800000, + 2200000, + 2910000, + 3050000, +}; + +static const unsigned int fixed_1200000_voltage[] = { + 1200000, +}; + +static const unsigned int fixed_1800000_voltage[] = { + 1800000, +}; + +static const unsigned int fixed_2000000_voltage[] = { + 2000000, +}; + +static const unsigned int fixed_2050000_voltage[] = { + 2050000, +}; + +static const unsigned int fixed_3300000_voltage[] = { + 3300000, +}; + +static const unsigned int ldo_vana_voltages[] = { + 1050000, + 1075000, + 1100000, + 1125000, + 1150000, + 1175000, + 1200000, + 1225000, +}; + +static const unsigned int ldo_vaudio_voltages[] = { + 2000000, + 2100000, + 2200000, + 2300000, + 2400000, + 2500000, + 2600000, + 2600000, /* Duplicated in Vaudio and IsoUicc Control register. */ +}; + +static const unsigned int ldo_vdmic_voltages[] = { + 1800000, + 1900000, + 2000000, + 2850000, +}; + +static DEFINE_MUTEX(shared_mode_mutex); +static struct ab8500_shared_mode ldo_anamic1_shared; +static struct ab8500_shared_mode ldo_anamic2_shared; +static struct ab8500_shared_mode ab8540_ldo_anamic1_shared; +static struct ab8500_shared_mode ab8540_ldo_anamic2_shared; + +static int ab8500_regulator_enable(struct regulator_dev *rdev) +{ + int ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, + info->update_mask, info->update_val); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't set enable bits for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-enable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, info->update_val); + + return ret; +} + +static int ab8500_regulator_disable(struct regulator_dev *rdev) +{ + int ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, + info->update_mask, 0x0); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't set disable bits for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-disable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, 0x0); + + return ret; +} + +static int ab8500_regulator_is_enabled(struct regulator_dev *rdev) +{ + int ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + ret = abx500_get_register_interruptible(info->dev, + info->update_bank, info->update_reg, ®val); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read 0x%x register\n", info->update_reg); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-is_enabled (bank, reg, mask, value): 0x%x, 0x%x, 0x%x," + " 0x%x\n", + info->desc.name, info->update_bank, info->update_reg, + info->update_mask, regval); + + if (regval & info->update_mask) + return 1; + else + return 0; +} + +static unsigned int ab8500_regulator_get_optimum_mode( + struct regulator_dev *rdev, int input_uV, + int output_uV, int load_uA) +{ + unsigned int mode; + + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + if (load_uA <= info->load_lp_uA) + mode = REGULATOR_MODE_IDLE; + else + mode = REGULATOR_MODE_NORMAL; + + return mode; +} + +static int ab8500_regulator_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + int ret = 0; + u8 bank, reg, mask, val; + bool lp_mode_req = false; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + if (info->mode_mask) { + bank = info->mode_bank; + reg = info->mode_reg; + mask = info->mode_mask; + } else { + bank = info->update_bank; + reg = info->update_reg; + mask = info->update_mask; + } + + if (info->shared_mode) + mutex_lock(&shared_mode_mutex); + + switch (mode) { + case REGULATOR_MODE_NORMAL: + if (info->shared_mode) + lp_mode_req = false; + + if (info->mode_mask) + val = info->mode_val_normal; + else + val = info->update_val_normal; + break; + case REGULATOR_MODE_IDLE: + if (info->shared_mode) { + struct ab8500_regulator_info *shared_regulator; + + shared_regulator = info->shared_mode->shared_regulator; + if (!shared_regulator->shared_mode->lp_mode_req) { + /* Other regulator prevent LP mode */ + info->shared_mode->lp_mode_req = true; + goto out_unlock; + } + + lp_mode_req = true; + } + + if (info->mode_mask) + val = info->mode_val_idle; + else + val = info->update_val_idle; + break; + default: + ret = -EINVAL; + goto out_unlock; + } + + if (info->mode_mask || ab8500_regulator_is_enabled(rdev)) { + ret = abx500_mask_and_set_register_interruptible(info->dev, + bank, reg, mask, val); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't set regulator mode\n"); + goto out_unlock; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-set_mode (bank, reg, mask, value): " + "0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, bank, reg, + mask, val); + } + + if (!info->mode_mask) + info->update_val = val; + + if (info->shared_mode) + info->shared_mode->lp_mode_req = lp_mode_req; + +out_unlock: + if (info->shared_mode) + mutex_unlock(&shared_mode_mutex); + + return ret; +} + +static unsigned int ab8500_regulator_get_mode(struct regulator_dev *rdev) +{ + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + u8 val; + u8 val_normal; + u8 val_idle; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + /* Need special handling for shared mode */ + if (info->shared_mode) { + if (info->shared_mode->lp_mode_req) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; + } + + if (info->mode_mask) { + /* Dedicated register for handling mode */ + ret = abx500_get_register_interruptible(info->dev, + info->mode_bank, info->mode_reg, &val); + val = val & info->mode_mask; + + val_normal = info->mode_val_normal; + val_idle = info->mode_val_idle; + } else { + /* Mode register same as enable register */ + val = info->update_val; + val_normal = info->update_val_normal; + val_idle = info->update_val_idle; + } + + if (val == val_normal) + ret = REGULATOR_MODE_NORMAL; + else if (val == val_idle) + ret = REGULATOR_MODE_IDLE; + else + ret = -EINVAL; + + return ret; +} + +static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret, voltage_shift; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + voltage_shift = ffs(info->voltage_mask) - 1; + + ret = abx500_get_register_interruptible(info->dev, + info->voltage_bank, info->voltage_reg, ®val); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read voltage reg for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-get_voltage (bank, reg, mask, shift, value): " + "0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->voltage_bank, + info->voltage_reg, info->voltage_mask, + voltage_shift, regval); + + return (regval & info->voltage_mask) >> voltage_shift; +} + +static int ab8540_aux3_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret, voltage_shift; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval, regval_expand; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + ret = abx500_get_register_interruptible(info->dev, + info->expand_register.voltage_bank, + info->expand_register.voltage_reg, ®val_expand); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read voltage expand reg for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-get_voltage expand (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->expand_register.voltage_bank, + info->expand_register.voltage_reg, + info->expand_register.voltage_mask, regval_expand); + + if (regval_expand & info->expand_register.voltage_mask) + return info->expand_register.voltage_limit; + + ret = abx500_get_register_interruptible(info->dev, + info->voltage_bank, info->voltage_reg, ®val); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read voltage reg for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->voltage_bank, info->voltage_reg, + info->voltage_mask, regval); + + voltage_shift = ffs(info->voltage_mask) - 1; + + return (regval & info->voltage_mask) >> voltage_shift; +} + +static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + int ret, voltage_shift; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + voltage_shift = ffs(info->voltage_mask) - 1; + + /* set the registers for the request */ + regval = (u8)selector << voltage_shift; + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->voltage_bank, info->voltage_reg, + info->voltage_mask, regval); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set voltage reg for regulator\n"); + + dev_vdbg(rdev_get_dev(rdev), + "%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x," + " 0x%x\n", + info->desc.name, info->voltage_bank, info->voltage_reg, + info->voltage_mask, regval); + + return ret; +} + +static int ab8540_aux3_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + int ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 regval, regval_expand; + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + if (selector < info->expand_register.voltage_limit) { + int voltage_shift = ffs(info->voltage_mask) - 1; + + regval = (u8)selector << voltage_shift; + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->voltage_bank, info->voltage_reg, + info->voltage_mask, regval); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't set voltage reg for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->voltage_bank, info->voltage_reg, + info->voltage_mask, regval); + + regval_expand = 0; + } else { + regval_expand = info->expand_register.voltage_mask; + } + + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->expand_register.voltage_bank, + info->expand_register.voltage_reg, + info->expand_register.voltage_mask, + regval_expand); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't set expand voltage reg for regulator\n"); + return ret; + } + + dev_vdbg(rdev_get_dev(rdev), + "%s-set_voltage expand (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", + info->desc.name, info->expand_register.voltage_bank, + info->expand_register.voltage_reg, + info->expand_register.voltage_mask, regval_expand); + + return 0; +} + +static struct regulator_ops ab8500_regulator_volt_mode_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .get_optimum_mode = ab8500_regulator_get_optimum_mode, + .set_mode = ab8500_regulator_set_mode, + .get_mode = ab8500_regulator_get_mode, + .get_voltage_sel = ab8500_regulator_get_voltage_sel, + .set_voltage_sel = ab8500_regulator_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_ops ab8540_aux3_regulator_volt_mode_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .get_optimum_mode = ab8500_regulator_get_optimum_mode, + .set_mode = ab8500_regulator_set_mode, + .get_mode = ab8500_regulator_get_mode, + .is_enabled = ab8500_regulator_is_enabled, + .get_voltage_sel = ab8540_aux3_regulator_get_voltage_sel, + .set_voltage_sel = ab8540_aux3_regulator_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_ops ab8500_regulator_volt_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .get_voltage_sel = ab8500_regulator_get_voltage_sel, + .set_voltage_sel = ab8500_regulator_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_ops ab8500_regulator_mode_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .get_optimum_mode = ab8500_regulator_get_optimum_mode, + .set_mode = ab8500_regulator_set_mode, + .get_mode = ab8500_regulator_get_mode, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_ops ab8500_regulator_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_ops ab8500_regulator_anamic_mode_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .set_mode = ab8500_regulator_set_mode, + .get_mode = ab8500_regulator_get_mode, + .list_voltage = regulator_list_voltage_table, +}; + +/* AB8500 regulator information */ +static struct ab8500_regulator_info + ab8500_regulator_info[AB8500_NUM_REGULATORS] = { + /* + * Variable Voltage Regulators + * name, min mV, max mV, + * update bank, reg, mask, enable val + * volt bank, reg, mask + */ + [AB8500_LDO_AUX1] = { + .desc = { + .name = "LDO-AUX1", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_AUX1, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + .enable_time = 200, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x1f, + .voltage_mask = 0x0f, + }, + [AB8500_LDO_AUX2] = { + .desc = { + .name = "LDO-AUX2", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_AUX2, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + .enable_time = 200, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + .voltage_bank = 0x04, + .voltage_reg = 0x20, + .voltage_mask = 0x0f, + }, + [AB8500_LDO_AUX3] = { + .desc = { + .name = "LDO-AUX3", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_AUX3, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), + .volt_table = ldo_vaux3_voltages, + .enable_time = 450, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x0a, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x21, + .voltage_mask = 0x07, + }, + [AB8500_LDO_INTCORE] = { + .desc = { + .name = "LDO-INTCORE", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_INTCORE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), + .volt_table = ldo_vintcore_voltages, + .enable_time = 750, + }, + .load_lp_uA = 5000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x44, + .update_val = 0x44, + .update_val_idle = 0x44, + .update_val_normal = 0x04, + .voltage_bank = 0x03, + .voltage_reg = 0x80, + .voltage_mask = 0x38, + }, + + /* + * Fixed Voltage Regulators + * name, fixed mV, + * update bank, reg, mask, enable val + */ + [AB8500_LDO_TVOUT] = { + .desc = { + .name = "LDO-TVOUT", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_TVOUT, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2000000_voltage, + .enable_time = 500, + }, + .load_lp_uA = 1000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x82, + .update_val = 0x02, + .update_val_idle = 0x82, + .update_val_normal = 0x02, + }, + [AB8500_LDO_AUDIO] = { + .desc = { + .name = "LDO-AUDIO", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_AUDIO, + .owner = THIS_MODULE, + .n_voltages = 1, + .enable_time = 140, + .volt_table = fixed_2000000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x02, + .update_val = 0x02, + }, + [AB8500_LDO_ANAMIC1] = { + .desc = { + .name = "LDO-ANAMIC1", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_ANAMIC1, + .owner = THIS_MODULE, + .n_voltages = 1, + .enable_time = 500, + .volt_table = fixed_2050000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x08, + .update_val = 0x08, + }, + [AB8500_LDO_ANAMIC2] = { + .desc = { + .name = "LDO-ANAMIC2", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_ANAMIC2, + .owner = THIS_MODULE, + .n_voltages = 1, + .enable_time = 500, + .volt_table = fixed_2050000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x10, + .update_val = 0x10, + }, + [AB8500_LDO_DMIC] = { + .desc = { + .name = "LDO-DMIC", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_DMIC, + .owner = THIS_MODULE, + .n_voltages = 1, + .enable_time = 420, + .volt_table = fixed_1800000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x04, + .update_val = 0x04, + }, + + /* + * Regulators with fixed voltage and normal/idle modes + */ + [AB8500_LDO_ANA] = { + .desc = { + .name = "LDO-ANA", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8500_LDO_ANA, + .owner = THIS_MODULE, + .n_voltages = 1, + .enable_time = 140, + .volt_table = fixed_1200000_voltage, + }, + .load_lp_uA = 1000, + .update_bank = 0x04, + .update_reg = 0x06, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + }, +}; + +/* AB8505 regulator information */ +static struct ab8500_regulator_info + ab8505_regulator_info[AB8505_NUM_REGULATORS] = { + /* + * Variable Voltage Regulators + * name, min mV, max mV, + * update bank, reg, mask, enable val + * volt bank, reg, mask + */ + [AB8505_LDO_AUX1] = { + .desc = { + .name = "LDO-AUX1", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX1, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x1f, + .voltage_mask = 0x0f, + }, + [AB8505_LDO_AUX2] = { + .desc = { + .name = "LDO-AUX2", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX2, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + .voltage_bank = 0x04, + .voltage_reg = 0x20, + .voltage_mask = 0x0f, + }, + [AB8505_LDO_AUX3] = { + .desc = { + .name = "LDO-AUX3", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX3, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), + .volt_table = ldo_vaux3_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x0a, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x21, + .voltage_mask = 0x07, + }, + [AB8505_LDO_AUX4] = { + .desc = { + .name = "LDO-AUX4", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX4, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + /* values for Vaux4Regu register */ + .update_bank = 0x04, + .update_reg = 0x2e, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + /* values for Vaux4SEL register */ + .voltage_bank = 0x04, + .voltage_reg = 0x2f, + .voltage_mask = 0x0f, + }, + [AB8505_LDO_AUX5] = { + .desc = { + .name = "LDO-AUX5", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX5, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux56_voltages), + .volt_table = ldo_vaux56_voltages, + }, + .load_lp_uA = 2000, + /* values for CtrlVaux5 register */ + .update_bank = 0x01, + .update_reg = 0x55, + .update_mask = 0x18, + .update_val = 0x10, + .update_val_idle = 0x18, + .update_val_normal = 0x10, + .voltage_bank = 0x01, + .voltage_reg = 0x55, + .voltage_mask = 0x07, + }, + [AB8505_LDO_AUX6] = { + .desc = { + .name = "LDO-AUX6", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX6, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux56_voltages), + .volt_table = ldo_vaux56_voltages, + }, + .load_lp_uA = 2000, + /* values for CtrlVaux6 register */ + .update_bank = 0x01, + .update_reg = 0x56, + .update_mask = 0x18, + .update_val = 0x10, + .update_val_idle = 0x18, + .update_val_normal = 0x10, + .voltage_bank = 0x01, + .voltage_reg = 0x56, + .voltage_mask = 0x07, + }, + [AB8505_LDO_INTCORE] = { + .desc = { + .name = "LDO-INTCORE", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_INTCORE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), + .volt_table = ldo_vintcore_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x44, + .update_val = 0x04, + .update_val_idle = 0x44, + .update_val_normal = 0x04, + .voltage_bank = 0x03, + .voltage_reg = 0x80, + .voltage_mask = 0x38, + }, + + /* + * Fixed Voltage Regulators + * name, fixed mV, + * update bank, reg, mask, enable val + */ + [AB8505_LDO_ADC] = { + .desc = { + .name = "LDO-ADC", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_ADC, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2000000_voltage, + .enable_time = 10000, + }, + .load_lp_uA = 1000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x82, + .update_val = 0x02, + .update_val_idle = 0x82, + .update_val_normal = 0x02, + }, + [AB8505_LDO_USB] = { + .desc = { + .name = "LDO-USB", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_USB, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_3300000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x82, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + }, + [AB8505_LDO_AUDIO] = { + .desc = { + .name = "LDO-AUDIO", + .ops = &ab8500_regulator_volt_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUDIO, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaudio_voltages), + .volt_table = ldo_vaudio_voltages, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x02, + .update_val = 0x02, + .voltage_bank = 0x01, + .voltage_reg = 0x57, + .voltage_mask = 0x70, + }, + [AB8505_LDO_ANAMIC1] = { + .desc = { + .name = "LDO-ANAMIC1", + .ops = &ab8500_regulator_anamic_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_ANAMIC1, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2050000_voltage, + }, + .shared_mode = &ldo_anamic1_shared, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x08, + .update_val = 0x08, + .mode_bank = 0x01, + .mode_reg = 0x54, + .mode_mask = 0x04, + .mode_val_idle = 0x04, + .mode_val_normal = 0x00, + }, + [AB8505_LDO_ANAMIC2] = { + .desc = { + .name = "LDO-ANAMIC2", + .ops = &ab8500_regulator_anamic_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_ANAMIC2, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2050000_voltage, + }, + .shared_mode = &ldo_anamic2_shared, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x10, + .update_val = 0x10, + .mode_bank = 0x01, + .mode_reg = 0x54, + .mode_mask = 0x04, + .mode_val_idle = 0x04, + .mode_val_normal = 0x00, + }, + [AB8505_LDO_AUX8] = { + .desc = { + .name = "LDO-AUX8", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_AUX8, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_1800000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x04, + .update_val = 0x04, + }, + /* + * Regulators with fixed voltage and normal/idle modes + */ + [AB8505_LDO_ANA] = { + .desc = { + .name = "LDO-ANA", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8505_LDO_ANA, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vana_voltages), + .volt_table = ldo_vana_voltages, + }, + .load_lp_uA = 1000, + .update_bank = 0x04, + .update_reg = 0x06, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + .voltage_bank = 0x04, + .voltage_reg = 0x29, + .voltage_mask = 0x7, + }, +}; + +/* AB9540 regulator information */ +static struct ab8500_regulator_info + ab9540_regulator_info[AB9540_NUM_REGULATORS] = { + /* + * Variable Voltage Regulators + * name, min mV, max mV, + * update bank, reg, mask, enable val + * volt bank, reg, mask + */ + [AB9540_LDO_AUX1] = { + .desc = { + .name = "LDO-AUX1", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_AUX1, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x1f, + .voltage_mask = 0x0f, + }, + [AB9540_LDO_AUX2] = { + .desc = { + .name = "LDO-AUX2", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_AUX2, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + .voltage_bank = 0x04, + .voltage_reg = 0x20, + .voltage_mask = 0x0f, + }, + [AB9540_LDO_AUX3] = { + .desc = { + .name = "LDO-AUX3", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_AUX3, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), + .volt_table = ldo_vaux3_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x0a, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x21, + .voltage_mask = 0x07, + }, + [AB9540_LDO_AUX4] = { + .desc = { + .name = "LDO-AUX4", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_AUX4, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + /* values for Vaux4Regu register */ + .update_bank = 0x04, + .update_reg = 0x2e, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + /* values for Vaux4SEL register */ + .voltage_bank = 0x04, + .voltage_reg = 0x2f, + .voltage_mask = 0x0f, + }, + [AB9540_LDO_INTCORE] = { + .desc = { + .name = "LDO-INTCORE", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_INTCORE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), + .volt_table = ldo_vintcore_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x44, + .update_val = 0x44, + .update_val_idle = 0x44, + .update_val_normal = 0x04, + .voltage_bank = 0x03, + .voltage_reg = 0x80, + .voltage_mask = 0x38, + }, + + /* + * Fixed Voltage Regulators + * name, fixed mV, + * update bank, reg, mask, enable val + */ + [AB9540_LDO_TVOUT] = { + .desc = { + .name = "LDO-TVOUT", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_TVOUT, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2000000_voltage, + .enable_time = 10000, + }, + .load_lp_uA = 1000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x82, + .update_val = 0x02, + .update_val_idle = 0x82, + .update_val_normal = 0x02, + }, + [AB9540_LDO_USB] = { + .desc = { + .name = "LDO-USB", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_USB, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_3300000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x82, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + }, + [AB9540_LDO_AUDIO] = { + .desc = { + .name = "LDO-AUDIO", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_AUDIO, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2000000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x02, + .update_val = 0x02, + }, + [AB9540_LDO_ANAMIC1] = { + .desc = { + .name = "LDO-ANAMIC1", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_ANAMIC1, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2050000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x08, + .update_val = 0x08, + }, + [AB9540_LDO_ANAMIC2] = { + .desc = { + .name = "LDO-ANAMIC2", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_ANAMIC2, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2050000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x10, + .update_val = 0x10, + }, + [AB9540_LDO_DMIC] = { + .desc = { + .name = "LDO-DMIC", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_DMIC, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_1800000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x04, + .update_val = 0x04, + }, + + /* + * Regulators with fixed voltage and normal/idle modes + */ + [AB9540_LDO_ANA] = { + .desc = { + .name = "LDO-ANA", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB9540_LDO_ANA, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_1200000_voltage, + }, + .load_lp_uA = 1000, + .update_bank = 0x04, + .update_reg = 0x06, + .update_mask = 0x0c, + .update_val = 0x08, + .update_val_idle = 0x0c, + .update_val_normal = 0x08, + }, +}; + +/* AB8540 regulator information */ +static struct ab8500_regulator_info + ab8540_regulator_info[AB8540_NUM_REGULATORS] = { + /* + * Variable Voltage Regulators + * name, min mV, max mV, + * update bank, reg, mask, enable val + * volt bank, reg, mask + */ + [AB8540_LDO_AUX1] = { + .desc = { + .name = "LDO-AUX1", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUX1, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x1f, + .voltage_mask = 0x0f, + }, + [AB8540_LDO_AUX2] = { + .desc = { + .name = "LDO-AUX2", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUX2, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x09, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + .voltage_bank = 0x04, + .voltage_reg = 0x20, + .voltage_mask = 0x0f, + }, + [AB8540_LDO_AUX3] = { + .desc = { + .name = "LDO-AUX3", + .ops = &ab8540_aux3_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUX3, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux3_ab8540_voltages), + .volt_table = ldo_vaux3_ab8540_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x04, + .update_reg = 0x0a, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + .voltage_bank = 0x04, + .voltage_reg = 0x21, + .voltage_mask = 0x07, + .expand_register = { + .voltage_limit = 8, + .voltage_bank = 0x04, + .voltage_reg = 0x01, + .voltage_mask = 0x10, + } + }, + [AB8540_LDO_AUX4] = { + .desc = { + .name = "LDO-AUX4", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUX4, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, + }, + .load_lp_uA = 5000, + /* values for Vaux4Regu register */ + .update_bank = 0x04, + .update_reg = 0x2e, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + /* values for Vaux4SEL register */ + .voltage_bank = 0x04, + .voltage_reg = 0x2f, + .voltage_mask = 0x0f, + }, + [AB8540_LDO_AUX5] = { + .desc = { + .name = "LDO-AUX5", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUX5, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux56_ab8540_voltages), + .volt_table = ldo_vaux56_ab8540_voltages, + }, + .load_lp_uA = 20000, + /* values for Vaux5Regu register */ + .update_bank = 0x04, + .update_reg = 0x32, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + /* values for Vaux5SEL register */ + .voltage_bank = 0x04, + .voltage_reg = 0x33, + .voltage_mask = 0x3f, + }, + [AB8540_LDO_AUX6] = { + .desc = { + .name = "LDO-AUX6", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUX6, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vaux56_ab8540_voltages), + .volt_table = ldo_vaux56_ab8540_voltages, + }, + .load_lp_uA = 20000, + /* values for Vaux6Regu register */ + .update_bank = 0x04, + .update_reg = 0x35, + .update_mask = 0x03, + .update_val = 0x01, + .update_val_idle = 0x03, + .update_val_normal = 0x01, + /* values for Vaux6SEL register */ + .voltage_bank = 0x04, + .voltage_reg = 0x36, + .voltage_mask = 0x3f, + }, + [AB8540_LDO_INTCORE] = { + .desc = { + .name = "LDO-INTCORE", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_INTCORE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), + .volt_table = ldo_vintcore_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x44, + .update_val = 0x44, + .update_val_idle = 0x44, + .update_val_normal = 0x04, + .voltage_bank = 0x03, + .voltage_reg = 0x80, + .voltage_mask = 0x38, + }, + + /* + * Fixed Voltage Regulators + * name, fixed mV, + * update bank, reg, mask, enable val + */ + [AB8540_LDO_TVOUT] = { + .desc = { + .name = "LDO-TVOUT", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_TVOUT, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2000000_voltage, + .enable_time = 10000, + }, + .load_lp_uA = 1000, + .update_bank = 0x03, + .update_reg = 0x80, + .update_mask = 0x82, + .update_val = 0x02, + .update_val_idle = 0x82, + .update_val_normal = 0x02, + }, + [AB8540_LDO_AUDIO] = { + .desc = { + .name = "LDO-AUDIO", + .ops = &ab8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_AUDIO, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2000000_voltage, + }, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x02, + .update_val = 0x02, + }, + [AB8540_LDO_ANAMIC1] = { + .desc = { + .name = "LDO-ANAMIC1", + .ops = &ab8500_regulator_anamic_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_ANAMIC1, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2050000_voltage, + }, + .shared_mode = &ab8540_ldo_anamic1_shared, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x08, + .update_val = 0x08, + .mode_bank = 0x03, + .mode_reg = 0x83, + .mode_mask = 0x20, + .mode_val_idle = 0x20, + .mode_val_normal = 0x00, + }, + [AB8540_LDO_ANAMIC2] = { + .desc = { + .name = "LDO-ANAMIC2", + .ops = &ab8500_regulator_anamic_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_ANAMIC2, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_2050000_voltage, + }, + .shared_mode = &ab8540_ldo_anamic2_shared, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x10, + .update_val = 0x10, + .mode_bank = 0x03, + .mode_reg = 0x83, + .mode_mask = 0x20, + .mode_val_idle = 0x20, + .mode_val_normal = 0x00, + }, + [AB8540_LDO_DMIC] = { + .desc = { + .name = "LDO-DMIC", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_DMIC, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_vdmic_voltages), + .volt_table = ldo_vdmic_voltages, + }, + .load_lp_uA = 1000, + .update_bank = 0x03, + .update_reg = 0x83, + .update_mask = 0x04, + .update_val = 0x04, + .voltage_bank = 0x03, + .voltage_reg = 0x83, + .voltage_mask = 0xc0, + }, + + /* + * Regulators with fixed voltage and normal/idle modes + */ + [AB8540_LDO_ANA] = { + .desc = { + .name = "LDO-ANA", + .ops = &ab8500_regulator_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_ANA, + .owner = THIS_MODULE, + .n_voltages = 1, + .volt_table = fixed_1200000_voltage, + }, + .load_lp_uA = 1000, + .update_bank = 0x04, + .update_reg = 0x06, + .update_mask = 0x0c, + .update_val = 0x04, + .update_val_idle = 0x0c, + .update_val_normal = 0x04, + }, + [AB8540_LDO_SDIO] = { + .desc = { + .name = "LDO-SDIO", + .ops = &ab8500_regulator_volt_mode_ops, + .type = REGULATOR_VOLTAGE, + .id = AB8540_LDO_SDIO, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(ldo_sdio_voltages), + .volt_table = ldo_sdio_voltages, + }, + .load_lp_uA = 5000, + .update_bank = 0x03, + .update_reg = 0x88, + .update_mask = 0x30, + .update_val = 0x10, + .update_val_idle = 0x30, + .update_val_normal = 0x10, + .voltage_bank = 0x03, + .voltage_reg = 0x88, + .voltage_mask = 0x07, + }, +}; + +static struct ab8500_shared_mode ldo_anamic1_shared = { + .shared_regulator = &ab8505_regulator_info[AB8505_LDO_ANAMIC2], +}; + +static struct ab8500_shared_mode ldo_anamic2_shared = { + .shared_regulator = &ab8505_regulator_info[AB8505_LDO_ANAMIC1], +}; + +static struct ab8500_shared_mode ab8540_ldo_anamic1_shared = { + .shared_regulator = &ab8540_regulator_info[AB8540_LDO_ANAMIC2], +}; + +static struct ab8500_shared_mode ab8540_ldo_anamic2_shared = { + .shared_regulator = &ab8540_regulator_info[AB8540_LDO_ANAMIC1], +}; + +struct ab8500_reg_init { + u8 bank; + u8 addr; + u8 mask; +}; + +#define REG_INIT(_id, _bank, _addr, _mask) \ + [_id] = { \ + .bank = _bank, \ + .addr = _addr, \ + .mask = _mask, \ + } + +/* AB8500 register init */ +static struct ab8500_reg_init ab8500_reg_init[] = { + /* + * 0x30, VanaRequestCtrl + * 0xc0, VextSupply1RequestCtrl + */ + REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xf0), + /* + * 0x03, VextSupply2RequestCtrl + * 0x0c, VextSupply3RequestCtrl + * 0x30, Vaux1RequestCtrl + * 0xc0, Vaux2RequestCtrl + */ + REG_INIT(AB8500_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), + /* + * 0x03, Vaux3RequestCtrl + * 0x04, SwHPReq + */ + REG_INIT(AB8500_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), + /* + * 0x08, VanaSysClkReq1HPValid + * 0x20, Vaux1SysClkReq1HPValid + * 0x40, Vaux2SysClkReq1HPValid + * 0x80, Vaux3SysClkReq1HPValid + */ + REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xe8), + /* + * 0x10, VextSupply1SysClkReq1HPValid + * 0x20, VextSupply2SysClkReq1HPValid + * 0x40, VextSupply3SysClkReq1HPValid + */ + REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x70), + /* + * 0x08, VanaHwHPReq1Valid + * 0x20, Vaux1HwHPReq1Valid + * 0x40, Vaux2HwHPReq1Valid + * 0x80, Vaux3HwHPReq1Valid + */ + REG_INIT(AB8500_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xe8), + /* + * 0x01, VextSupply1HwHPReq1Valid + * 0x02, VextSupply2HwHPReq1Valid + * 0x04, VextSupply3HwHPReq1Valid + */ + REG_INIT(AB8500_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07), + /* + * 0x08, VanaHwHPReq2Valid + * 0x20, Vaux1HwHPReq2Valid + * 0x40, Vaux2HwHPReq2Valid + * 0x80, Vaux3HwHPReq2Valid + */ + REG_INIT(AB8500_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xe8), + /* + * 0x01, VextSupply1HwHPReq2Valid + * 0x02, VextSupply2HwHPReq2Valid + * 0x04, VextSupply3HwHPReq2Valid + */ + REG_INIT(AB8500_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07), + /* + * 0x20, VanaSwHPReqValid + * 0x80, Vaux1SwHPReqValid + */ + REG_INIT(AB8500_REGUSWHPREQVALID1, 0x03, 0x0d, 0xa0), + /* + * 0x01, Vaux2SwHPReqValid + * 0x02, Vaux3SwHPReqValid + * 0x04, VextSupply1SwHPReqValid + * 0x08, VextSupply2SwHPReqValid + * 0x10, VextSupply3SwHPReqValid + */ + REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f), + /* + * 0x02, SysClkReq2Valid1 + * 0x04, SysClkReq3Valid1 + * 0x08, SysClkReq4Valid1 + * 0x10, SysClkReq5Valid1 + * 0x20, SysClkReq6Valid1 + * 0x40, SysClkReq7Valid1 + * 0x80, SysClkReq8Valid1 + */ + REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe), + /* + * 0x02, SysClkReq2Valid2 + * 0x04, SysClkReq3Valid2 + * 0x08, SysClkReq4Valid2 + * 0x10, SysClkReq5Valid2 + * 0x20, SysClkReq6Valid2 + * 0x40, SysClkReq7Valid2 + * 0x80, SysClkReq8Valid2 + */ + REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe), + /* + * 0x02, VTVoutEna + * 0x04, Vintcore12Ena + * 0x38, Vintcore12Sel + * 0x40, Vintcore12LP + * 0x80, VTVoutLP + */ + REG_INIT(AB8500_REGUMISC1, 0x03, 0x80, 0xfe), + /* + * 0x02, VaudioEna + * 0x04, VdmicEna + * 0x08, Vamic1Ena + * 0x10, Vamic2Ena + */ + REG_INIT(AB8500_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), + /* + * 0x01, Vamic1_dzout + * 0x02, Vamic2_dzout + */ + REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), + /* + * 0x03, VpllRegu (NOTE! PRCMU register bits) + * 0x0c, VanaRegu + */ + REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f), + /* + * 0x01, VrefDDREna + * 0x02, VrefDDRSleepMode + */ + REG_INIT(AB8500_VREFDDR, 0x04, 0x07, 0x03), + /* + * 0x03, VextSupply1Regu + * 0x0c, VextSupply2Regu + * 0x30, VextSupply3Regu + * 0x40, ExtSupply2Bypass + * 0x80, ExtSupply3Bypass + */ + REG_INIT(AB8500_EXTSUPPLYREGU, 0x04, 0x08, 0xff), + /* + * 0x03, Vaux1Regu + * 0x0c, Vaux2Regu + */ + REG_INIT(AB8500_VAUX12REGU, 0x04, 0x09, 0x0f), + /* + * 0x03, Vaux3Regu + */ + REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x03), + /* + * 0x0f, Vaux1Sel + */ + REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f), + /* + * 0x0f, Vaux2Sel + */ + REG_INIT(AB8500_VAUX2SEL, 0x04, 0x20, 0x0f), + /* + * 0x07, Vaux3Sel + */ + REG_INIT(AB8500_VRF1VAUX3SEL, 0x04, 0x21, 0x07), + /* + * 0x01, VextSupply12LP + */ + REG_INIT(AB8500_REGUCTRL2SPARE, 0x04, 0x22, 0x01), + /* + * 0x04, Vaux1Disch + * 0x08, Vaux2Disch + * 0x10, Vaux3Disch + * 0x20, Vintcore12Disch + * 0x40, VTVoutDisch + * 0x80, VaudioDisch + */ + REG_INIT(AB8500_REGUCTRLDISCH, 0x04, 0x43, 0xfc), + /* + * 0x02, VanaDisch + * 0x04, VdmicPullDownEna + * 0x10, VdmicDisch + */ + REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16), +}; + +/* AB8505 register init */ +static struct ab8500_reg_init ab8505_reg_init[] = { + /* + * 0x03, VarmRequestCtrl + * 0x0c, VsmpsCRequestCtrl + * 0x30, VsmpsARequestCtrl + * 0xc0, VsmpsBRequestCtrl + */ + REG_INIT(AB8505_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), + /* + * 0x03, VsafeRequestCtrl + * 0x0c, VpllRequestCtrl + * 0x30, VanaRequestCtrl + */ + REG_INIT(AB8505_REGUREQUESTCTRL2, 0x03, 0x04, 0x3f), + /* + * 0x30, Vaux1RequestCtrl + * 0xc0, Vaux2RequestCtrl + */ + REG_INIT(AB8505_REGUREQUESTCTRL3, 0x03, 0x05, 0xf0), + /* + * 0x03, Vaux3RequestCtrl + * 0x04, SwHPReq + */ + REG_INIT(AB8505_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), + /* + * 0x01, VsmpsASysClkReq1HPValid + * 0x02, VsmpsBSysClkReq1HPValid + * 0x04, VsafeSysClkReq1HPValid + * 0x08, VanaSysClkReq1HPValid + * 0x10, VpllSysClkReq1HPValid + * 0x20, Vaux1SysClkReq1HPValid + * 0x40, Vaux2SysClkReq1HPValid + * 0x80, Vaux3SysClkReq1HPValid + */ + REG_INIT(AB8505_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), + /* + * 0x01, VsmpsCSysClkReq1HPValid + * 0x02, VarmSysClkReq1HPValid + * 0x04, VbbSysClkReq1HPValid + * 0x08, VsmpsMSysClkReq1HPValid + */ + REG_INIT(AB8505_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x0f), + /* + * 0x01, VsmpsAHwHPReq1Valid + * 0x02, VsmpsBHwHPReq1Valid + * 0x04, VsafeHwHPReq1Valid + * 0x08, VanaHwHPReq1Valid + * 0x10, VpllHwHPReq1Valid + * 0x20, Vaux1HwHPReq1Valid + * 0x40, Vaux2HwHPReq1Valid + * 0x80, Vaux3HwHPReq1Valid + */ + REG_INIT(AB8505_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), + /* + * 0x08, VsmpsMHwHPReq1Valid + */ + REG_INIT(AB8505_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x08), + /* + * 0x01, VsmpsAHwHPReq2Valid + * 0x02, VsmpsBHwHPReq2Valid + * 0x04, VsafeHwHPReq2Valid + * 0x08, VanaHwHPReq2Valid + * 0x10, VpllHwHPReq2Valid + * 0x20, Vaux1HwHPReq2Valid + * 0x40, Vaux2HwHPReq2Valid + * 0x80, Vaux3HwHPReq2Valid + */ + REG_INIT(AB8505_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), + /* + * 0x08, VsmpsMHwHPReq2Valid + */ + REG_INIT(AB8505_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x08), + /* + * 0x01, VsmpsCSwHPReqValid + * 0x02, VarmSwHPReqValid + * 0x04, VsmpsASwHPReqValid + * 0x08, VsmpsBSwHPReqValid + * 0x10, VsafeSwHPReqValid + * 0x20, VanaSwHPReqValid + * 0x40, VpllSwHPReqValid + * 0x80, Vaux1SwHPReqValid + */ + REG_INIT(AB8505_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), + /* + * 0x01, Vaux2SwHPReqValid + * 0x02, Vaux3SwHPReqValid + * 0x20, VsmpsMSwHPReqValid + */ + REG_INIT(AB8505_REGUSWHPREQVALID2, 0x03, 0x0e, 0x23), + /* + * 0x02, SysClkReq2Valid1 + * 0x04, SysClkReq3Valid1 + * 0x08, SysClkReq4Valid1 + */ + REG_INIT(AB8505_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0x0e), + /* + * 0x02, SysClkReq2Valid2 + * 0x04, SysClkReq3Valid2 + * 0x08, SysClkReq4Valid2 + */ + REG_INIT(AB8505_REGUSYSCLKREQVALID2, 0x03, 0x10, 0x0e), + /* + * 0x01, Vaux4SwHPReqValid + * 0x02, Vaux4HwHPReq2Valid + * 0x04, Vaux4HwHPReq1Valid + * 0x08, Vaux4SysClkReq1HPValid + */ + REG_INIT(AB8505_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), + /* + * 0x02, VadcEna + * 0x04, VintCore12Ena + * 0x38, VintCore12Sel + * 0x40, VintCore12LP + * 0x80, VadcLP + */ + REG_INIT(AB8505_REGUMISC1, 0x03, 0x80, 0xfe), + /* + * 0x02, VaudioEna + * 0x04, VdmicEna + * 0x08, Vamic1Ena + * 0x10, Vamic2Ena + */ + REG_INIT(AB8505_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), + /* + * 0x01, Vamic1_dzout + * 0x02, Vamic2_dzout + */ + REG_INIT(AB8505_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), + /* + * 0x03, VsmpsARegu + * 0x0c, VsmpsASelCtrl + * 0x10, VsmpsAAutoMode + * 0x20, VsmpsAPWMMode + */ + REG_INIT(AB8505_VSMPSAREGU, 0x04, 0x03, 0x3f), + /* + * 0x03, VsmpsBRegu + * 0x0c, VsmpsBSelCtrl + * 0x10, VsmpsBAutoMode + * 0x20, VsmpsBPWMMode + */ + REG_INIT(AB8505_VSMPSBREGU, 0x04, 0x04, 0x3f), + /* + * 0x03, VsafeRegu + * 0x0c, VsafeSelCtrl + * 0x10, VsafeAutoMode + * 0x20, VsafePWMMode + */ + REG_INIT(AB8505_VSAFEREGU, 0x04, 0x05, 0x3f), + /* + * 0x03, VpllRegu (NOTE! PRCMU register bits) + * 0x0c, VanaRegu + */ + REG_INIT(AB8505_VPLLVANAREGU, 0x04, 0x06, 0x0f), + /* + * 0x03, VextSupply1Regu + * 0x0c, VextSupply2Regu + * 0x30, VextSupply3Regu + * 0x40, ExtSupply2Bypass + * 0x80, ExtSupply3Bypass + */ + REG_INIT(AB8505_EXTSUPPLYREGU, 0x04, 0x08, 0xff), + /* + * 0x03, Vaux1Regu + * 0x0c, Vaux2Regu + */ + REG_INIT(AB8505_VAUX12REGU, 0x04, 0x09, 0x0f), + /* + * 0x0f, Vaux3Regu + */ + REG_INIT(AB8505_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), + /* + * 0x3f, VsmpsASel1 + */ + REG_INIT(AB8505_VSMPSASEL1, 0x04, 0x13, 0x3f), + /* + * 0x3f, VsmpsASel2 + */ + REG_INIT(AB8505_VSMPSASEL2, 0x04, 0x14, 0x3f), + /* + * 0x3f, VsmpsASel3 + */ + REG_INIT(AB8505_VSMPSASEL3, 0x04, 0x15, 0x3f), + /* + * 0x3f, VsmpsBSel1 + */ + REG_INIT(AB8505_VSMPSBSEL1, 0x04, 0x17, 0x3f), + /* + * 0x3f, VsmpsBSel2 + */ + REG_INIT(AB8505_VSMPSBSEL2, 0x04, 0x18, 0x3f), + /* + * 0x3f, VsmpsBSel3 + */ + REG_INIT(AB8505_VSMPSBSEL3, 0x04, 0x19, 0x3f), + /* + * 0x7f, VsafeSel1 + */ + REG_INIT(AB8505_VSAFESEL1, 0x04, 0x1b, 0x7f), + /* + * 0x3f, VsafeSel2 + */ + REG_INIT(AB8505_VSAFESEL2, 0x04, 0x1c, 0x7f), + /* + * 0x3f, VsafeSel3 + */ + REG_INIT(AB8505_VSAFESEL3, 0x04, 0x1d, 0x7f), + /* + * 0x0f, Vaux1Sel + */ + REG_INIT(AB8505_VAUX1SEL, 0x04, 0x1f, 0x0f), + /* + * 0x0f, Vaux2Sel + */ + REG_INIT(AB8505_VAUX2SEL, 0x04, 0x20, 0x0f), + /* + * 0x07, Vaux3Sel + * 0x30, VRF1Sel + */ + REG_INIT(AB8505_VRF1VAUX3SEL, 0x04, 0x21, 0x37), + /* + * 0x03, Vaux4RequestCtrl + */ + REG_INIT(AB8505_VAUX4REQCTRL, 0x04, 0x2d, 0x03), + /* + * 0x03, Vaux4Regu + */ + REG_INIT(AB8505_VAUX4REGU, 0x04, 0x2e, 0x03), + /* + * 0x0f, Vaux4Sel + */ + REG_INIT(AB8505_VAUX4SEL, 0x04, 0x2f, 0x0f), + /* + * 0x04, Vaux1Disch + * 0x08, Vaux2Disch + * 0x10, Vaux3Disch + * 0x20, Vintcore12Disch + * 0x40, VTVoutDisch + * 0x80, VaudioDisch + */ + REG_INIT(AB8505_REGUCTRLDISCH, 0x04, 0x43, 0xfc), + /* + * 0x02, VanaDisch + * 0x04, VdmicPullDownEna + * 0x10, VdmicDisch + */ + REG_INIT(AB8505_REGUCTRLDISCH2, 0x04, 0x44, 0x16), + /* + * 0x01, Vaux4Disch + */ + REG_INIT(AB8505_REGUCTRLDISCH3, 0x04, 0x48, 0x01), + /* + * 0x07, Vaux5Sel + * 0x08, Vaux5LP + * 0x10, Vaux5Ena + * 0x20, Vaux5Disch + * 0x40, Vaux5DisSfst + * 0x80, Vaux5DisPulld + */ + REG_INIT(AB8505_CTRLVAUX5, 0x01, 0x55, 0xff), + /* + * 0x07, Vaux6Sel + * 0x08, Vaux6LP + * 0x10, Vaux6Ena + * 0x80, Vaux6DisPulld + */ + REG_INIT(AB8505_CTRLVAUX6, 0x01, 0x56, 0x9f), +}; + +/* AB9540 register init */ +static struct ab8500_reg_init ab9540_reg_init[] = { + /* + * 0x03, VarmRequestCtrl + * 0x0c, VapeRequestCtrl + * 0x30, Vsmps1RequestCtrl + * 0xc0, Vsmps2RequestCtrl + */ + REG_INIT(AB9540_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), + /* + * 0x03, Vsmps3RequestCtrl + * 0x0c, VpllRequestCtrl + * 0x30, VanaRequestCtrl + * 0xc0, VextSupply1RequestCtrl + */ + REG_INIT(AB9540_REGUREQUESTCTRL2, 0x03, 0x04, 0xff), + /* + * 0x03, VextSupply2RequestCtrl + * 0x0c, VextSupply3RequestCtrl + * 0x30, Vaux1RequestCtrl + * 0xc0, Vaux2RequestCtrl + */ + REG_INIT(AB9540_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), + /* + * 0x03, Vaux3RequestCtrl + * 0x04, SwHPReq + */ + REG_INIT(AB9540_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), + /* + * 0x01, Vsmps1SysClkReq1HPValid + * 0x02, Vsmps2SysClkReq1HPValid + * 0x04, Vsmps3SysClkReq1HPValid + * 0x08, VanaSysClkReq1HPValid + * 0x10, VpllSysClkReq1HPValid + * 0x20, Vaux1SysClkReq1HPValid + * 0x40, Vaux2SysClkReq1HPValid + * 0x80, Vaux3SysClkReq1HPValid + */ + REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), + /* + * 0x01, VapeSysClkReq1HPValid + * 0x02, VarmSysClkReq1HPValid + * 0x04, VbbSysClkReq1HPValid + * 0x08, VmodSysClkReq1HPValid + * 0x10, VextSupply1SysClkReq1HPValid + * 0x20, VextSupply2SysClkReq1HPValid + * 0x40, VextSupply3SysClkReq1HPValid + */ + REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x7f), + /* + * 0x01, Vsmps1HwHPReq1Valid + * 0x02, Vsmps2HwHPReq1Valid + * 0x04, Vsmps3HwHPReq1Valid + * 0x08, VanaHwHPReq1Valid + * 0x10, VpllHwHPReq1Valid + * 0x20, Vaux1HwHPReq1Valid + * 0x40, Vaux2HwHPReq1Valid + * 0x80, Vaux3HwHPReq1Valid + */ + REG_INIT(AB9540_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), + /* + * 0x01, VextSupply1HwHPReq1Valid + * 0x02, VextSupply2HwHPReq1Valid + * 0x04, VextSupply3HwHPReq1Valid + * 0x08, VmodHwHPReq1Valid + */ + REG_INIT(AB9540_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x0f), + /* + * 0x01, Vsmps1HwHPReq2Valid + * 0x02, Vsmps2HwHPReq2Valid + * 0x03, Vsmps3HwHPReq2Valid + * 0x08, VanaHwHPReq2Valid + * 0x10, VpllHwHPReq2Valid + * 0x20, Vaux1HwHPReq2Valid + * 0x40, Vaux2HwHPReq2Valid + * 0x80, Vaux3HwHPReq2Valid + */ + REG_INIT(AB9540_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), + /* + * 0x01, VextSupply1HwHPReq2Valid + * 0x02, VextSupply2HwHPReq2Valid + * 0x04, VextSupply3HwHPReq2Valid + * 0x08, VmodHwHPReq2Valid + */ + REG_INIT(AB9540_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x0f), + /* + * 0x01, VapeSwHPReqValid + * 0x02, VarmSwHPReqValid + * 0x04, Vsmps1SwHPReqValid + * 0x08, Vsmps2SwHPReqValid + * 0x10, Vsmps3SwHPReqValid + * 0x20, VanaSwHPReqValid + * 0x40, VpllSwHPReqValid + * 0x80, Vaux1SwHPReqValid + */ + REG_INIT(AB9540_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), + /* + * 0x01, Vaux2SwHPReqValid + * 0x02, Vaux3SwHPReqValid + * 0x04, VextSupply1SwHPReqValid + * 0x08, VextSupply2SwHPReqValid + * 0x10, VextSupply3SwHPReqValid + * 0x20, VmodSwHPReqValid + */ + REG_INIT(AB9540_REGUSWHPREQVALID2, 0x03, 0x0e, 0x3f), + /* + * 0x02, SysClkReq2Valid1 + * ... + * 0x80, SysClkReq8Valid1 + */ + REG_INIT(AB9540_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe), + /* + * 0x02, SysClkReq2Valid2 + * ... + * 0x80, SysClkReq8Valid2 + */ + REG_INIT(AB9540_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe), + /* + * 0x01, Vaux4SwHPReqValid + * 0x02, Vaux4HwHPReq2Valid + * 0x04, Vaux4HwHPReq1Valid + * 0x08, Vaux4SysClkReq1HPValid + */ + REG_INIT(AB9540_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), + /* + * 0x02, VTVoutEna + * 0x04, Vintcore12Ena + * 0x38, Vintcore12Sel + * 0x40, Vintcore12LP + * 0x80, VTVoutLP + */ + REG_INIT(AB9540_REGUMISC1, 0x03, 0x80, 0xfe), + /* + * 0x02, VaudioEna + * 0x04, VdmicEna + * 0x08, Vamic1Ena + * 0x10, Vamic2Ena + */ + REG_INIT(AB9540_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), + /* + * 0x01, Vamic1_dzout + * 0x02, Vamic2_dzout + */ + REG_INIT(AB9540_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), + /* + * 0x03, Vsmps1Regu + * 0x0c, Vsmps1SelCtrl + * 0x10, Vsmps1AutoMode + * 0x20, Vsmps1PWMMode + */ + REG_INIT(AB9540_VSMPS1REGU, 0x04, 0x03, 0x3f), + /* + * 0x03, Vsmps2Regu + * 0x0c, Vsmps2SelCtrl + * 0x10, Vsmps2AutoMode + * 0x20, Vsmps2PWMMode + */ + REG_INIT(AB9540_VSMPS2REGU, 0x04, 0x04, 0x3f), + /* + * 0x03, Vsmps3Regu + * 0x0c, Vsmps3SelCtrl + * NOTE! PRCMU register + */ + REG_INIT(AB9540_VSMPS3REGU, 0x04, 0x05, 0x0f), + /* + * 0x03, VpllRegu + * 0x0c, VanaRegu + */ + REG_INIT(AB9540_VPLLVANAREGU, 0x04, 0x06, 0x0f), + /* + * 0x03, VextSupply1Regu + * 0x0c, VextSupply2Regu + * 0x30, VextSupply3Regu + * 0x40, ExtSupply2Bypass + * 0x80, ExtSupply3Bypass + */ + REG_INIT(AB9540_EXTSUPPLYREGU, 0x04, 0x08, 0xff), + /* + * 0x03, Vaux1Regu + * 0x0c, Vaux2Regu + */ + REG_INIT(AB9540_VAUX12REGU, 0x04, 0x09, 0x0f), + /* + * 0x0c, Vrf1Regu + * 0x03, Vaux3Regu + */ + REG_INIT(AB9540_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), + /* + * 0x3f, Vsmps1Sel1 + */ + REG_INIT(AB9540_VSMPS1SEL1, 0x04, 0x13, 0x3f), + /* + * 0x3f, Vsmps1Sel2 + */ + REG_INIT(AB9540_VSMPS1SEL2, 0x04, 0x14, 0x3f), + /* + * 0x3f, Vsmps1Sel3 + */ + REG_INIT(AB9540_VSMPS1SEL3, 0x04, 0x15, 0x3f), + /* + * 0x3f, Vsmps2Sel1 + */ + REG_INIT(AB9540_VSMPS2SEL1, 0x04, 0x17, 0x3f), + /* + * 0x3f, Vsmps2Sel2 + */ + REG_INIT(AB9540_VSMPS2SEL2, 0x04, 0x18, 0x3f), + /* + * 0x3f, Vsmps2Sel3 + */ + REG_INIT(AB9540_VSMPS2SEL3, 0x04, 0x19, 0x3f), + /* + * 0x7f, Vsmps3Sel1 + * NOTE! PRCMU register + */ + REG_INIT(AB9540_VSMPS3SEL1, 0x04, 0x1b, 0x7f), + /* + * 0x7f, Vsmps3Sel2 + * NOTE! PRCMU register + */ + REG_INIT(AB9540_VSMPS3SEL2, 0x04, 0x1c, 0x7f), + /* + * 0x0f, Vaux1Sel + */ + REG_INIT(AB9540_VAUX1SEL, 0x04, 0x1f, 0x0f), + /* + * 0x0f, Vaux2Sel + */ + REG_INIT(AB9540_VAUX2SEL, 0x04, 0x20, 0x0f), + /* + * 0x07, Vaux3Sel + * 0x30, Vrf1Sel + */ + REG_INIT(AB9540_VRF1VAUX3SEL, 0x04, 0x21, 0x37), + /* + * 0x01, VextSupply12LP + */ + REG_INIT(AB9540_REGUCTRL2SPARE, 0x04, 0x22, 0x01), + /* + * 0x03, Vaux4RequestCtrl + */ + REG_INIT(AB9540_VAUX4REQCTRL, 0x04, 0x2d, 0x03), + /* + * 0x03, Vaux4Regu + */ + REG_INIT(AB9540_VAUX4REGU, 0x04, 0x2e, 0x03), + /* + * 0x08, Vaux4Sel + */ + REG_INIT(AB9540_VAUX4SEL, 0x04, 0x2f, 0x0f), + /* + * 0x01, VpllDisch + * 0x02, Vrf1Disch + * 0x04, Vaux1Disch + * 0x08, Vaux2Disch + * 0x10, Vaux3Disch + * 0x20, Vintcore12Disch + * 0x40, VTVoutDisch + * 0x80, VaudioDisch + */ + REG_INIT(AB9540_REGUCTRLDISCH, 0x04, 0x43, 0xff), + /* + * 0x01, VsimDisch + * 0x02, VanaDisch + * 0x04, VdmicPullDownEna + * 0x08, VpllPullDownEna + * 0x10, VdmicDisch + */ + REG_INIT(AB9540_REGUCTRLDISCH2, 0x04, 0x44, 0x1f), + /* + * 0x01, Vaux4Disch + */ + REG_INIT(AB9540_REGUCTRLDISCH3, 0x04, 0x48, 0x01), +}; + +/* AB8540 register init */ +static struct ab8500_reg_init ab8540_reg_init[] = { + /* + * 0x01, VSimSycClkReq1Valid + * 0x02, VSimSycClkReq2Valid + * 0x04, VSimSycClkReq3Valid + * 0x08, VSimSycClkReq4Valid + * 0x10, VSimSycClkReq5Valid + * 0x20, VSimSycClkReq6Valid + * 0x40, VSimSycClkReq7Valid + * 0x80, VSimSycClkReq8Valid + */ + REG_INIT(AB8540_VSIMSYSCLKCTRL, 0x02, 0x33, 0xff), + /* + * 0x03, VarmRequestCtrl + * 0x0c, VapeRequestCtrl + * 0x30, Vsmps1RequestCtrl + * 0xc0, Vsmps2RequestCtrl + */ + REG_INIT(AB8540_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), + /* + * 0x03, Vsmps3RequestCtrl + * 0x0c, VpllRequestCtrl + * 0x30, VanaRequestCtrl + * 0xc0, VextSupply1RequestCtrl + */ + REG_INIT(AB8540_REGUREQUESTCTRL2, 0x03, 0x04, 0xff), + /* + * 0x03, VextSupply2RequestCtrl + * 0x0c, VextSupply3RequestCtrl + * 0x30, Vaux1RequestCtrl + * 0xc0, Vaux2RequestCtrl + */ + REG_INIT(AB8540_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), + /* + * 0x03, Vaux3RequestCtrl + * 0x04, SwHPReq + */ + REG_INIT(AB8540_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), + /* + * 0x01, Vsmps1SysClkReq1HPValid + * 0x02, Vsmps2SysClkReq1HPValid + * 0x04, Vsmps3SysClkReq1HPValid + * 0x08, VanaSysClkReq1HPValid + * 0x10, VpllSysClkReq1HPValid + * 0x20, Vaux1SysClkReq1HPValid + * 0x40, Vaux2SysClkReq1HPValid + * 0x80, Vaux3SysClkReq1HPValid + */ + REG_INIT(AB8540_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), + /* + * 0x01, VapeSysClkReq1HPValid + * 0x02, VarmSysClkReq1HPValid + * 0x04, VbbSysClkReq1HPValid + * 0x10, VextSupply1SysClkReq1HPValid + * 0x20, VextSupply2SysClkReq1HPValid + * 0x40, VextSupply3SysClkReq1HPValid + */ + REG_INIT(AB8540_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x77), + /* + * 0x01, Vsmps1HwHPReq1Valid + * 0x02, Vsmps2HwHPReq1Valid + * 0x04, Vsmps3HwHPReq1Valid + * 0x08, VanaHwHPReq1Valid + * 0x10, VpllHwHPReq1Valid + * 0x20, Vaux1HwHPReq1Valid + * 0x40, Vaux2HwHPReq1Valid + * 0x80, Vaux3HwHPReq1Valid + */ + REG_INIT(AB8540_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), + /* + * 0x01, VextSupply1HwHPReq1Valid + * 0x02, VextSupply2HwHPReq1Valid + * 0x04, VextSupply3HwHPReq1Valid + */ + REG_INIT(AB8540_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07), + /* + * 0x01, Vsmps1HwHPReq2Valid + * 0x02, Vsmps2HwHPReq2Valid + * 0x03, Vsmps3HwHPReq2Valid + * 0x08, VanaHwHPReq2Valid + * 0x10, VpllHwHPReq2Valid + * 0x20, Vaux1HwHPReq2Valid + * 0x40, Vaux2HwHPReq2Valid + * 0x80, Vaux3HwHPReq2Valid + */ + REG_INIT(AB8540_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), + /* + * 0x01, VextSupply1HwHPReq2Valid + * 0x02, VextSupply2HwHPReq2Valid + * 0x04, VextSupply3HwHPReq2Valid + */ + REG_INIT(AB8540_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07), + /* + * 0x01, VapeSwHPReqValid + * 0x02, VarmSwHPReqValid + * 0x04, Vsmps1SwHPReqValid + * 0x08, Vsmps2SwHPReqValid + * 0x10, Vsmps3SwHPReqValid + * 0x20, VanaSwHPReqValid + * 0x40, VpllSwHPReqValid + * 0x80, Vaux1SwHPReqValid + */ + REG_INIT(AB8540_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), + /* + * 0x01, Vaux2SwHPReqValid + * 0x02, Vaux3SwHPReqValid + * 0x04, VextSupply1SwHPReqValid + * 0x08, VextSupply2SwHPReqValid + * 0x10, VextSupply3SwHPReqValid + */ + REG_INIT(AB8540_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f), + /* + * 0x02, SysClkReq2Valid1 + * ... + * 0x80, SysClkReq8Valid1 + */ + REG_INIT(AB8540_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xff), + /* + * 0x02, SysClkReq2Valid2 + * ... + * 0x80, SysClkReq8Valid2 + */ + REG_INIT(AB8540_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xff), + /* + * 0x01, Vaux4SwHPReqValid + * 0x02, Vaux4HwHPReq2Valid + * 0x04, Vaux4HwHPReq1Valid + * 0x08, Vaux4SysClkReq1HPValid + */ + REG_INIT(AB8540_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), + /* + * 0x01, Vaux5SwHPReqValid + * 0x02, Vaux5HwHPReq2Valid + * 0x04, Vaux5HwHPReq1Valid + * 0x08, Vaux5SysClkReq1HPValid + */ + REG_INIT(AB8540_REGUVAUX5REQVALID, 0x03, 0x12, 0x0f), + /* + * 0x01, Vaux6SwHPReqValid + * 0x02, Vaux6HwHPReq2Valid + * 0x04, Vaux6HwHPReq1Valid + * 0x08, Vaux6SysClkReq1HPValid + */ + REG_INIT(AB8540_REGUVAUX6REQVALID, 0x03, 0x13, 0x0f), + /* + * 0x01, VclkbSwHPReqValid + * 0x02, VclkbHwHPReq2Valid + * 0x04, VclkbHwHPReq1Valid + * 0x08, VclkbSysClkReq1HPValid + */ + REG_INIT(AB8540_REGUVCLKBREQVALID, 0x03, 0x14, 0x0f), + /* + * 0x01, Vrf1SwHPReqValid + * 0x02, Vrf1HwHPReq2Valid + * 0x04, Vrf1HwHPReq1Valid + * 0x08, Vrf1SysClkReq1HPValid + */ + REG_INIT(AB8540_REGUVRF1REQVALID, 0x03, 0x15, 0x0f), + /* + * 0x02, VTVoutEna + * 0x04, Vintcore12Ena + * 0x38, Vintcore12Sel + * 0x40, Vintcore12LP + * 0x80, VTVoutLP + */ + REG_INIT(AB8540_REGUMISC1, 0x03, 0x80, 0xfe), + /* + * 0x02, VaudioEna + * 0x04, VdmicEna + * 0x08, Vamic1Ena + * 0x10, Vamic2Ena + * 0x20, Vamic12LP + * 0xC0, VdmicSel + */ + REG_INIT(AB8540_VAUDIOSUPPLY, 0x03, 0x83, 0xfe), + /* + * 0x01, Vamic1_dzout + * 0x02, Vamic2_dzout + */ + REG_INIT(AB8540_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), + /* + * 0x07, VHSICSel + * 0x08, VHSICOffState + * 0x10, VHSIEna + * 0x20, VHSICLP + */ + REG_INIT(AB8540_VHSIC, 0x03, 0x87, 0x3f), + /* + * 0x07, VSDIOSel + * 0x08, VSDIOOffState + * 0x10, VSDIOEna + * 0x20, VSDIOLP + */ + REG_INIT(AB8540_VSDIO, 0x03, 0x88, 0x3f), + /* + * 0x03, Vsmps1Regu + * 0x0c, Vsmps1SelCtrl + * 0x10, Vsmps1AutoMode + * 0x20, Vsmps1PWMMode + */ + REG_INIT(AB8540_VSMPS1REGU, 0x04, 0x03, 0x3f), + /* + * 0x03, Vsmps2Regu + * 0x0c, Vsmps2SelCtrl + * 0x10, Vsmps2AutoMode + * 0x20, Vsmps2PWMMode + */ + REG_INIT(AB8540_VSMPS2REGU, 0x04, 0x04, 0x3f), + /* + * 0x03, Vsmps3Regu + * 0x0c, Vsmps3SelCtrl + * 0x10, Vsmps3AutoMode + * 0x20, Vsmps3PWMMode + * NOTE! PRCMU register + */ + REG_INIT(AB8540_VSMPS3REGU, 0x04, 0x05, 0x0f), + /* + * 0x03, VpllRegu + * 0x0c, VanaRegu + */ + REG_INIT(AB8540_VPLLVANAREGU, 0x04, 0x06, 0x0f), + /* + * 0x03, VextSupply1Regu + * 0x0c, VextSupply2Regu + * 0x30, VextSupply3Regu + * 0x40, ExtSupply2Bypass + * 0x80, ExtSupply3Bypass + */ + REG_INIT(AB8540_EXTSUPPLYREGU, 0x04, 0x08, 0xff), + /* + * 0x03, Vaux1Regu + * 0x0c, Vaux2Regu + */ + REG_INIT(AB8540_VAUX12REGU, 0x04, 0x09, 0x0f), + /* + * 0x0c, VRF1Regu + * 0x03, Vaux3Regu + */ + REG_INIT(AB8540_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), + /* + * 0x3f, Vsmps1Sel1 + */ + REG_INIT(AB8540_VSMPS1SEL1, 0x04, 0x13, 0x3f), + /* + * 0x3f, Vsmps1Sel2 + */ + REG_INIT(AB8540_VSMPS1SEL2, 0x04, 0x14, 0x3f), + /* + * 0x3f, Vsmps1Sel3 + */ + REG_INIT(AB8540_VSMPS1SEL3, 0x04, 0x15, 0x3f), + /* + * 0x3f, Vsmps2Sel1 + */ + REG_INIT(AB8540_VSMPS2SEL1, 0x04, 0x17, 0x3f), + /* + * 0x3f, Vsmps2Sel2 + */ + REG_INIT(AB8540_VSMPS2SEL2, 0x04, 0x18, 0x3f), + /* + * 0x3f, Vsmps2Sel3 + */ + REG_INIT(AB8540_VSMPS2SEL3, 0x04, 0x19, 0x3f), + /* + * 0x7f, Vsmps3Sel1 + * NOTE! PRCMU register + */ + REG_INIT(AB8540_VSMPS3SEL1, 0x04, 0x1b, 0x7f), + /* + * 0x7f, Vsmps3Sel2 + * NOTE! PRCMU register + */ + REG_INIT(AB8540_VSMPS3SEL2, 0x04, 0x1c, 0x7f), + /* + * 0x0f, Vaux1Sel + */ + REG_INIT(AB8540_VAUX1SEL, 0x04, 0x1f, 0x0f), + /* + * 0x0f, Vaux2Sel + */ + REG_INIT(AB8540_VAUX2SEL, 0x04, 0x20, 0x0f), + /* + * 0x07, Vaux3Sel + * 0x70, Vrf1Sel + */ + REG_INIT(AB8540_VRF1VAUX3SEL, 0x04, 0x21, 0x77), + /* + * 0x01, VextSupply12LP + */ + REG_INIT(AB8540_REGUCTRL2SPARE, 0x04, 0x22, 0x01), + /* + * 0x07, Vanasel + * 0x30, Vpllsel + */ + REG_INIT(AB8540_VANAVPLLSEL, 0x04, 0x29, 0x37), + /* + * 0x03, Vaux4RequestCtrl + */ + REG_INIT(AB8540_VAUX4REQCTRL, 0x04, 0x2d, 0x03), + /* + * 0x03, Vaux4Regu + */ + REG_INIT(AB8540_VAUX4REGU, 0x04, 0x2e, 0x03), + /* + * 0x0f, Vaux4Sel + */ + REG_INIT(AB8540_VAUX4SEL, 0x04, 0x2f, 0x0f), + /* + * 0x03, Vaux5RequestCtrl + */ + REG_INIT(AB8540_VAUX5REQCTRL, 0x04, 0x31, 0x03), + /* + * 0x03, Vaux5Regu + */ + REG_INIT(AB8540_VAUX5REGU, 0x04, 0x32, 0x03), + /* + * 0x3f, Vaux5Sel + */ + REG_INIT(AB8540_VAUX5SEL, 0x04, 0x33, 0x3f), + /* + * 0x03, Vaux6RequestCtrl + */ + REG_INIT(AB8540_VAUX6REQCTRL, 0x04, 0x34, 0x03), + /* + * 0x03, Vaux6Regu + */ + REG_INIT(AB8540_VAUX6REGU, 0x04, 0x35, 0x03), + /* + * 0x3f, Vaux6Sel + */ + REG_INIT(AB8540_VAUX6SEL, 0x04, 0x36, 0x3f), + /* + * 0x03, VCLKBRequestCtrl + */ + REG_INIT(AB8540_VCLKBREQCTRL, 0x04, 0x37, 0x03), + /* + * 0x03, VCLKBRegu + */ + REG_INIT(AB8540_VCLKBREGU, 0x04, 0x38, 0x03), + /* + * 0x07, VCLKBSel + */ + REG_INIT(AB8540_VCLKBSEL, 0x04, 0x39, 0x07), + /* + * 0x03, Vrf1RequestCtrl + */ + REG_INIT(AB8540_VRF1REQCTRL, 0x04, 0x3a, 0x03), + /* + * 0x01, VpllDisch + * 0x02, Vrf1Disch + * 0x04, Vaux1Disch + * 0x08, Vaux2Disch + * 0x10, Vaux3Disch + * 0x20, Vintcore12Disch + * 0x40, VTVoutDisch + * 0x80, VaudioDisch + */ + REG_INIT(AB8540_REGUCTRLDISCH, 0x04, 0x43, 0xff), + /* + * 0x02, VanaDisch + * 0x04, VdmicPullDownEna + * 0x08, VpllPullDownEna + * 0x10, VdmicDisch + */ + REG_INIT(AB8540_REGUCTRLDISCH2, 0x04, 0x44, 0x1e), + /* + * 0x01, Vaux4Disch + */ + REG_INIT(AB8540_REGUCTRLDISCH3, 0x04, 0x48, 0x01), + /* + * 0x01, Vaux5Disch + * 0x02, Vaux6Disch + * 0x04, VCLKBDisch + */ + REG_INIT(AB8540_REGUCTRLDISCH4, 0x04, 0x49, 0x07), +}; + +static struct of_regulator_match ab8500_regulator_match[] = { + { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB8500_LDO_AUX1, }, + { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB8500_LDO_AUX2, }, + { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB8500_LDO_AUX3, }, + { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB8500_LDO_INTCORE, }, + { .name = "ab8500_ldo_tvout", .driver_data = (void *) AB8500_LDO_TVOUT, }, + { .name = "ab8500_ldo_audio", .driver_data = (void *) AB8500_LDO_AUDIO, }, + { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB8500_LDO_ANAMIC1, }, + { .name = "ab8500_ldo_amamic2", .driver_data = (void *) AB8500_LDO_ANAMIC2, }, + { .name = "ab8500_ldo_dmic", .driver_data = (void *) AB8500_LDO_DMIC, }, + { .name = "ab8500_ldo_ana", .driver_data = (void *) AB8500_LDO_ANA, }, +}; + +static struct of_regulator_match ab8505_regulator_match[] = { + { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB8505_LDO_AUX1, }, + { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB8505_LDO_AUX2, }, + { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB8505_LDO_AUX3, }, + { .name = "ab8500_ldo_aux4", .driver_data = (void *) AB8505_LDO_AUX4, }, + { .name = "ab8500_ldo_aux5", .driver_data = (void *) AB8505_LDO_AUX5, }, + { .name = "ab8500_ldo_aux6", .driver_data = (void *) AB8505_LDO_AUX6, }, + { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB8505_LDO_INTCORE, }, + { .name = "ab8500_ldo_adc", .driver_data = (void *) AB8505_LDO_ADC, }, + { .name = "ab8500_ldo_audio", .driver_data = (void *) AB8505_LDO_AUDIO, }, + { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB8505_LDO_ANAMIC1, }, + { .name = "ab8500_ldo_amamic2", .driver_data = (void *) AB8505_LDO_ANAMIC2, }, + { .name = "ab8500_ldo_aux8", .driver_data = (void *) AB8505_LDO_AUX8, }, + { .name = "ab8500_ldo_ana", .driver_data = (void *) AB8505_LDO_ANA, }, +}; + +static struct of_regulator_match ab8540_regulator_match[] = { + { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB8540_LDO_AUX1, }, + { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB8540_LDO_AUX2, }, + { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB8540_LDO_AUX3, }, + { .name = "ab8500_ldo_aux4", .driver_data = (void *) AB8540_LDO_AUX4, }, + { .name = "ab8500_ldo_aux5", .driver_data = (void *) AB8540_LDO_AUX5, }, + { .name = "ab8500_ldo_aux6", .driver_data = (void *) AB8540_LDO_AUX6, }, + { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB8540_LDO_INTCORE, }, + { .name = "ab8500_ldo_tvout", .driver_data = (void *) AB8540_LDO_TVOUT, }, + { .name = "ab8500_ldo_audio", .driver_data = (void *) AB8540_LDO_AUDIO, }, + { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB8540_LDO_ANAMIC1, }, + { .name = "ab8500_ldo_amamic2", .driver_data = (void *) AB8540_LDO_ANAMIC2, }, + { .name = "ab8500_ldo_dmic", .driver_data = (void *) AB8540_LDO_DMIC, }, + { .name = "ab8500_ldo_ana", .driver_data = (void *) AB8540_LDO_ANA, }, + { .name = "ab8500_ldo_sdio", .driver_data = (void *) AB8540_LDO_SDIO, }, +}; + +static struct of_regulator_match ab9540_regulator_match[] = { + { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB9540_LDO_AUX1, }, + { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB9540_LDO_AUX2, }, + { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB9540_LDO_AUX3, }, + { .name = "ab8500_ldo_aux4", .driver_data = (void *) AB9540_LDO_AUX4, }, + { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB9540_LDO_INTCORE, }, + { .name = "ab8500_ldo_tvout", .driver_data = (void *) AB9540_LDO_TVOUT, }, + { .name = "ab8500_ldo_audio", .driver_data = (void *) AB9540_LDO_AUDIO, }, + { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB9540_LDO_ANAMIC1, }, + { .name = "ab8500_ldo_amamic2", .driver_data = (void *) AB9540_LDO_ANAMIC2, }, + { .name = "ab8500_ldo_dmic", .driver_data = (void *) AB9540_LDO_DMIC, }, + { .name = "ab8500_ldo_ana", .driver_data = (void *) AB9540_LDO_ANA, }, +}; + +static struct { + struct ab8500_regulator_info *info; + int info_size; + struct ab8500_reg_init *init; + int init_size; + struct of_regulator_match *match; + int match_size; +} abx500_regulator; + +static void abx500_get_regulator_info(struct ab8500 *ab8500) +{ + if (is_ab9540(ab8500)) { + abx500_regulator.info = ab9540_regulator_info; + abx500_regulator.info_size = ARRAY_SIZE(ab9540_regulator_info); + abx500_regulator.init = ab9540_reg_init; + abx500_regulator.init_size = AB9540_NUM_REGULATOR_REGISTERS; + abx500_regulator.match = ab9540_regulator_match; + abx500_regulator.match_size = ARRAY_SIZE(ab9540_regulator_match); + } else if (is_ab8505(ab8500)) { + abx500_regulator.info = ab8505_regulator_info; + abx500_regulator.info_size = ARRAY_SIZE(ab8505_regulator_info); + abx500_regulator.init = ab8505_reg_init; + abx500_regulator.init_size = AB8505_NUM_REGULATOR_REGISTERS; + abx500_regulator.match = ab8505_regulator_match; + abx500_regulator.match_size = ARRAY_SIZE(ab8505_regulator_match); + } else if (is_ab8540(ab8500)) { + abx500_regulator.info = ab8540_regulator_info; + abx500_regulator.info_size = ARRAY_SIZE(ab8540_regulator_info); + abx500_regulator.init = ab8540_reg_init; + abx500_regulator.init_size = AB8540_NUM_REGULATOR_REGISTERS; + abx500_regulator.match = ab8540_regulator_match; + abx500_regulator.match_size = ARRAY_SIZE(ab8540_regulator_match); + } else { + abx500_regulator.info = ab8500_regulator_info; + abx500_regulator.info_size = ARRAY_SIZE(ab8500_regulator_info); + abx500_regulator.init = ab8500_reg_init; + abx500_regulator.init_size = AB8500_NUM_REGULATOR_REGISTERS; + abx500_regulator.match = ab8500_regulator_match; + abx500_regulator.match_size = ARRAY_SIZE(ab8500_regulator_match); + } +} + +static int ab8500_regulator_init_registers(struct platform_device *pdev, + int id, int mask, int value) +{ + struct ab8500_reg_init *reg_init = abx500_regulator.init; + int err; + + BUG_ON(value & ~mask); + BUG_ON(mask & ~reg_init[id].mask); + + /* initialize register */ + err = abx500_mask_and_set_register_interruptible( + &pdev->dev, + reg_init[id].bank, + reg_init[id].addr, + mask, value); + if (err < 0) { + dev_err(&pdev->dev, + "Failed to initialize 0x%02x, 0x%02x.\n", + reg_init[id].bank, + reg_init[id].addr); + return err; + } + dev_vdbg(&pdev->dev, + " init: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + reg_init[id].bank, + reg_init[id].addr, + mask, value); + + return 0; +} + +static int ab8500_regulator_register(struct platform_device *pdev, + struct regulator_init_data *init_data, + int id, struct device_node *np) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_regulator_info *info = NULL; + struct regulator_config config = { }; + int err; + + /* assign per-regulator data */ + info = &abx500_regulator.info[id]; + info->dev = &pdev->dev; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = info; + config.of_node = np; + + /* fix for hardware before ab8500v2.0 */ + if (is_ab8500_1p1_or_earlier(ab8500)) { + if (info->desc.id == AB8500_LDO_AUX3) { + info->desc.n_voltages = + ARRAY_SIZE(ldo_vauxn_voltages); + info->desc.volt_table = ldo_vauxn_voltages; + info->voltage_mask = 0xf; + } + } + + /* register regulator with framework */ + info->regulator = regulator_register(&info->desc, &config); + if (IS_ERR(info->regulator)) { + err = PTR_ERR(info->regulator); + dev_err(&pdev->dev, "failed to register regulator %s\n", + info->desc.name); + /* when we fail, un-register all earlier regulators */ + while (--id >= 0) { + info = &abx500_regulator.info[id]; + regulator_unregister(info->regulator); + } + return err; + } + + return 0; +} + +static int +ab8500_regulator_of_probe(struct platform_device *pdev, + struct device_node *np) +{ + struct of_regulator_match *match = abx500_regulator.match; + int err, i; + + for (i = 0; i < abx500_regulator.info_size; i++) { + err = ab8500_regulator_register( + pdev, match[i].init_data, i, match[i].of_node); + if (err) + return err; + } + + return 0; +} + +static int ab8500_regulator_probe(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct device_node *np = pdev->dev.of_node; + struct ab8500_platform_data *ppdata; + struct ab8500_regulator_platform_data *pdata; + int i, err; + + if (!ab8500) { + dev_err(&pdev->dev, "null mfd parent\n"); + return -EINVAL; + } + + abx500_get_regulator_info(ab8500); + + if (np) { + err = of_regulator_match(&pdev->dev, np, + abx500_regulator.match, + abx500_regulator.match_size); + if (err < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", err); + return err; + } + + err = ab8500_regulator_of_probe(pdev, np); + return err; + } + + ppdata = dev_get_platdata(ab8500->dev); + if (!ppdata) { + dev_err(&pdev->dev, "null parent pdata\n"); + return -EINVAL; + } + + pdata = ppdata->regulator; + if (!pdata) { + dev_err(&pdev->dev, "null pdata\n"); + return -EINVAL; + } + + /* make sure the platform data has the correct size */ + if (pdata->num_regulator != abx500_regulator.info_size) { + dev_err(&pdev->dev, "Configuration error: size mismatch.\n"); + return -EINVAL; + } + + /* initialize debug (initial state is recorded with this call) */ + err = ab8500_regulator_debug_init(pdev); + if (err) + return err; + + /* initialize registers */ + for (i = 0; i < pdata->num_reg_init; i++) { + int id, mask, value; + + id = pdata->reg_init[i].id; + mask = pdata->reg_init[i].mask; + value = pdata->reg_init[i].value; + + /* check for configuration errors */ + BUG_ON(id >= abx500_regulator.init_size); + + err = ab8500_regulator_init_registers(pdev, id, mask, value); + if (err < 0) + return err; + } + + if (!is_ab8505(ab8500)) { + /* register external regulators (before Vaux1, 2 and 3) */ + err = ab8500_ext_regulator_init(pdev); + if (err) + return err; + } + + /* register all regulators */ + for (i = 0; i < abx500_regulator.info_size; i++) { + err = ab8500_regulator_register(pdev, &pdata->regulator[i], + i, NULL); + if (err < 0) { + if (!is_ab8505(ab8500)) + ab8500_ext_regulator_exit(pdev); + return err; + } + } + + return 0; +} + +static int ab8500_regulator_remove(struct platform_device *pdev) +{ + int i, err; + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + + for (i = 0; i < abx500_regulator.info_size; i++) { + struct ab8500_regulator_info *info = NULL; + info = &abx500_regulator.info[i]; + + dev_vdbg(rdev_get_dev(info->regulator), + "%s-remove\n", info->desc.name); + + regulator_unregister(info->regulator); + } + + /* remove external regulators (after Vaux1, 2 and 3) */ + if (!is_ab8505(ab8500)) + ab8500_ext_regulator_exit(pdev); + + /* remove regulator debug */ + err = ab8500_regulator_debug_exit(pdev); + if (err) + return err; + + return 0; +} + +static struct platform_driver ab8500_regulator_driver = { + .probe = ab8500_regulator_probe, + .remove = ab8500_regulator_remove, + .driver = { + .name = "ab8500-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&ab8500_regulator_driver); + if (ret != 0) + pr_err("Failed to register ab8500 regulator: %d\n", ret); + + return ret; +} +subsys_initcall(ab8500_regulator_init); + +static void __exit ab8500_regulator_exit(void) +{ + platform_driver_unregister(&ab8500_regulator_driver); +} +module_exit(ab8500_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer "); +MODULE_AUTHOR("Bengt Jonsson "); +MODULE_AUTHOR("Daniel Willerud "); +MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC"); +MODULE_ALIAS("platform:ab8500-regulator"); diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c new file mode 100644 index 0000000..6b981b5 --- /dev/null +++ b/drivers/regulator/ad5398.c @@ -0,0 +1,291 @@ +/* + * Voltage and current regulation for AD5398 and AD5821 + * + * Copyright 2010 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define AD5398_CURRENT_EN_MASK 0x8000 + +struct ad5398_chip_info { + struct i2c_client *client; + int min_uA; + int max_uA; + unsigned int current_level; + unsigned int current_mask; + unsigned int current_offset; + struct regulator_dev *rdev; +}; + +static int ad5398_calc_current(struct ad5398_chip_info *chip, + unsigned selector) +{ + unsigned range_uA = chip->max_uA - chip->min_uA; + + return chip->min_uA + (selector * range_uA / chip->current_level); +} + +static int ad5398_read_reg(struct i2c_client *client, unsigned short *data) +{ + unsigned short val; + int ret; + + ret = i2c_master_recv(client, (char *)&val, 2); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + *data = be16_to_cpu(val); + + return ret; +} + +static int ad5398_write_reg(struct i2c_client *client, const unsigned short data) +{ + unsigned short val; + int ret; + + val = cpu_to_be16(data); + ret = i2c_master_send(client, (char *)&val, 2); + if (ret < 0) + dev_err(&client->dev, "I2C write error\n"); + + return ret; +} + +static int ad5398_get_current_limit(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + ret = (data & chip->current_mask) >> chip->current_offset; + + return ad5398_calc_current(chip, ret); +} + +static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned range_uA = chip->max_uA - chip->min_uA; + unsigned selector; + unsigned short data; + int ret; + + if (min_uA < chip->min_uA) + min_uA = chip->min_uA; + if (max_uA > chip->max_uA) + max_uA = chip->max_uA; + + if (min_uA > chip->max_uA || max_uA < chip->min_uA) + return -EINVAL; + + selector = DIV_ROUND_UP((min_uA - chip->min_uA) * chip->current_level, + range_uA); + if (ad5398_calc_current(chip, selector) > max_uA) + return -EINVAL; + + dev_dbg(&client->dev, "changing current %duA\n", + ad5398_calc_current(chip, selector)); + + /* read chip enable bit */ + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + /* prepare register data */ + selector = (selector << chip->current_offset) & chip->current_mask; + data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK); + + /* write the new current value back as well as enable bit */ + ret = ad5398_write_reg(client, data); + + return ret; +} + +static int ad5398_is_enabled(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (data & AD5398_CURRENT_EN_MASK) + return 1; + else + return 0; +} + +static int ad5398_enable(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (data & AD5398_CURRENT_EN_MASK) + return 0; + + data |= AD5398_CURRENT_EN_MASK; + + ret = ad5398_write_reg(client, data); + + return ret; +} + +static int ad5398_disable(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (!(data & AD5398_CURRENT_EN_MASK)) + return 0; + + data &= ~AD5398_CURRENT_EN_MASK; + + ret = ad5398_write_reg(client, data); + + return ret; +} + +static struct regulator_ops ad5398_ops = { + .get_current_limit = ad5398_get_current_limit, + .set_current_limit = ad5398_set_current_limit, + .enable = ad5398_enable, + .disable = ad5398_disable, + .is_enabled = ad5398_is_enabled, +}; + +static const struct regulator_desc ad5398_reg = { + .name = "isink", + .id = 0, + .ops = &ad5398_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, +}; + +struct ad5398_current_data_format { + int current_bits; + int current_offset; + int min_uA; + int max_uA; +}; + +static const struct ad5398_current_data_format df_10_4_120 = {10, 4, 0, 120000}; + +static const struct i2c_device_id ad5398_id[] = { + { "ad5398", (kernel_ulong_t)&df_10_4_120 }, + { "ad5821", (kernel_ulong_t)&df_10_4_120 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad5398_id); + +static int ad5398_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regulator_init_data *init_data = client->dev.platform_data; + struct regulator_config config = { }; + struct ad5398_chip_info *chip; + const struct ad5398_current_data_format *df = + (struct ad5398_current_data_format *)id->driver_data; + int ret; + + if (!init_data) + return -EINVAL; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + config.dev = &client->dev; + config.init_data = init_data; + config.driver_data = chip; + + chip->client = client; + + chip->min_uA = df->min_uA; + chip->max_uA = df->max_uA; + chip->current_level = 1 << df->current_bits; + chip->current_offset = df->current_offset; + chip->current_mask = (chip->current_level - 1) << chip->current_offset; + + chip->rdev = regulator_register(&ad5398_reg, &config); + if (IS_ERR(chip->rdev)) { + ret = PTR_ERR(chip->rdev); + dev_err(&client->dev, "failed to register %s %s\n", + id->name, ad5398_reg.name); + goto err; + } + + i2c_set_clientdata(client, chip); + dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name); + return 0; + +err: + return ret; +} + +static int ad5398_remove(struct i2c_client *client) +{ + struct ad5398_chip_info *chip = i2c_get_clientdata(client); + + regulator_unregister(chip->rdev); + return 0; +} + +static struct i2c_driver ad5398_driver = { + .probe = ad5398_probe, + .remove = ad5398_remove, + .driver = { + .name = "ad5398", + }, + .id_table = ad5398_id, +}; + +static int __init ad5398_init(void) +{ + return i2c_add_driver(&ad5398_driver); +} +subsys_initcall(ad5398_init); + +static void __exit ad5398_exit(void) +{ + i2c_del_driver(&ad5398_driver); +} +module_exit(ad5398_exit); + +MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver"); +MODULE_AUTHOR("Sonic Zhang"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("i2c:ad5398-regulator"); diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c new file mode 100644 index 0000000..0d4a8cc --- /dev/null +++ b/drivers/regulator/anatop-regulator.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */ +#define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */ + +struct anatop_regulator { + const char *name; + u32 control_reg; + struct regmap *anatop; + int vol_bit_shift; + int vol_bit_width; + u32 delay_reg; + int delay_bit_shift; + int delay_bit_width; + int min_bit_val; + int min_voltage; + int max_voltage; + struct regulator_desc rdesc; + struct regulator_init_data *initdata; +}; + +static int anatop_regmap_set_voltage_sel(struct regulator_dev *reg, + unsigned selector) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + + if (!anatop_reg->control_reg) + return -ENOTSUPP; + + return regulator_set_voltage_sel_regmap(reg, selector); +} + +static int anatop_regmap_set_voltage_time_sel(struct regulator_dev *reg, + unsigned int old_sel, + unsigned int new_sel) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + u32 val; + int ret = 0; + + /* check whether need to care about LDO ramp up speed */ + if (anatop_reg->delay_bit_width && new_sel > old_sel) { + /* + * the delay for LDO ramp up time is + * based on the register setting, we need + * to calculate how many steps LDO need to + * ramp up, and how much delay needed. (us) + */ + regmap_read(anatop_reg->anatop, anatop_reg->delay_reg, &val); + val = (val >> anatop_reg->delay_bit_shift) & + ((1 << anatop_reg->delay_bit_width) - 1); + ret = (new_sel - old_sel) * (LDO_RAMP_UP_UNIT_IN_CYCLES << + val) / LDO_RAMP_UP_FREQ_IN_MHZ + 1; + } + + return ret; +} + +static int anatop_regmap_get_voltage_sel(struct regulator_dev *reg) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + + if (!anatop_reg->control_reg) + return -ENOTSUPP; + + return regulator_get_voltage_sel_regmap(reg); +} + +static struct regulator_ops anatop_rops = { + .set_voltage_sel = anatop_regmap_set_voltage_sel, + .set_voltage_time_sel = anatop_regmap_set_voltage_time_sel, + .get_voltage_sel = anatop_regmap_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static int anatop_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *anatop_np; + struct regulator_desc *rdesc; + struct regulator_dev *rdev; + struct anatop_regulator *sreg; + struct regulator_init_data *initdata; + struct regulator_config config = { }; + int ret = 0; + + initdata = of_get_regulator_init_data(dev, np); + sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); + if (!sreg) + return -ENOMEM; + sreg->initdata = initdata; + sreg->name = kstrdup(of_get_property(np, "regulator-name", NULL), + GFP_KERNEL); + rdesc = &sreg->rdesc; + memset(rdesc, 0, sizeof(*rdesc)); + rdesc->name = sreg->name; + rdesc->ops = &anatop_rops; + rdesc->type = REGULATOR_VOLTAGE; + rdesc->owner = THIS_MODULE; + + 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); + + ret = of_property_read_u32(np, "anatop-reg-offset", + &sreg->control_reg); + if (ret) { + dev_err(dev, "no anatop-reg-offset property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-vol-bit-width", + &sreg->vol_bit_width); + if (ret) { + dev_err(dev, "no anatop-vol-bit-width property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-vol-bit-shift", + &sreg->vol_bit_shift); + if (ret) { + dev_err(dev, "no anatop-vol-bit-shift property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-min-bit-val", + &sreg->min_bit_val); + if (ret) { + dev_err(dev, "no anatop-min-bit-val property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-min-voltage", + &sreg->min_voltage); + if (ret) { + dev_err(dev, "no anatop-min-voltage property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-max-voltage", + &sreg->max_voltage); + if (ret) { + dev_err(dev, "no anatop-max-voltage property set\n"); + goto anatop_probe_end; + } + + /* read LDO ramp up setting, only for core reg */ + of_property_read_u32(np, "anatop-delay-reg-offset", + &sreg->delay_reg); + of_property_read_u32(np, "anatop-delay-bit-width", + &sreg->delay_bit_width); + of_property_read_u32(np, "anatop-delay-bit-shift", + &sreg->delay_bit_shift); + + rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage) / 25000 + 1 + + sreg->min_bit_val; + rdesc->min_uV = sreg->min_voltage; + rdesc->uV_step = 25000; + rdesc->linear_min_sel = sreg->min_bit_val; + rdesc->vsel_reg = sreg->control_reg; + rdesc->vsel_mask = ((1 << sreg->vol_bit_width) - 1) << + sreg->vol_bit_shift; + + 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)) { + dev_err(dev, "failed to register %s\n", + rdesc->name); + ret = PTR_ERR(rdev); + goto anatop_probe_end; + } + + platform_set_drvdata(pdev, rdev); + +anatop_probe_end: + if (ret) + kfree(sreg->name); + + return ret; +} + +static int anatop_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct anatop_regulator *sreg = rdev_get_drvdata(rdev); + const char *name = sreg->name; + + regulator_unregister(rdev); + kfree(name); + + return 0; +} + +static struct of_device_id of_anatop_regulator_match_tbl[] = { + { .compatible = "fsl,anatop-regulator", }, + { /* end */ } +}; + +static struct platform_driver anatop_regulator_driver = { + .driver = { + .name = "anatop_regulator", + .owner = THIS_MODULE, + .of_match_table = of_anatop_regulator_match_tbl, + }, + .probe = anatop_regulator_probe, + .remove = anatop_regulator_remove, +}; + +static int __init anatop_regulator_init(void) +{ + return platform_driver_register(&anatop_regulator_driver); +} +postcore_initcall(anatop_regulator_init); + +static void __exit anatop_regulator_exit(void) +{ + platform_driver_unregister(&anatop_regulator_driver); +} +module_exit(anatop_regulator_exit); + +MODULE_AUTHOR("Nancy Chen , " + "Ying-Chun Liu (PaulLiu) "); +MODULE_DESCRIPTION("ANATOP Regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c new file mode 100644 index 0000000..81d8681 --- /dev/null +++ b/drivers/regulator/arizona-ldo1.c @@ -0,0 +1,266 @@ +/* + * arizona-ldo1.c -- LDO1 supply for Arizona devices + * + * Copyright 2012 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct arizona_ldo1 { + struct regulator_dev *regulator; + struct arizona *arizona; + + struct regulator_consumer_supply supply; + struct regulator_init_data init_data; +}; + +static int arizona_ldo1_hc_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + if (selector == rdev->desc->n_voltages - 1) + return 1800000; + else + return rdev->desc->min_uV + (rdev->desc->uV_step * selector); +} + +static int arizona_ldo1_hc_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int sel; + + sel = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step); + if (sel >= rdev->desc->n_voltages) + sel = rdev->desc->n_voltages - 1; + + return sel; +} + +static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev); + struct regmap *regmap = ldo->arizona->regmap; + unsigned int val; + int ret; + + if (sel == rdev->desc->n_voltages - 1) + val = ARIZONA_LDO1_HI_PWR; + else + val = 0; + + ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2, + ARIZONA_LDO1_HI_PWR, val); + if (ret != 0) + return ret; + + ret = regmap_update_bits(regmap, ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, val); + if (ret != 0) + return ret; + + if (val) + return 0; + + val = sel << ARIZONA_LDO1_VSEL_SHIFT; + + return regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_1, + ARIZONA_LDO1_VSEL_MASK, val); +} + +static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev); + struct regmap *regmap = ldo->arizona->regmap; + unsigned int val; + int ret; + + ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val); + if (ret != 0) + return ret; + + if (val & ARIZONA_LDO1_HI_PWR) + return rdev->desc->n_voltages - 1; + + ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_1, &val); + if (ret != 0) + return ret; + + return (val & ARIZONA_LDO1_VSEL_MASK) >> ARIZONA_LDO1_VSEL_SHIFT; +} + +static struct regulator_ops arizona_ldo1_hc_ops = { + .list_voltage = arizona_ldo1_hc_list_voltage, + .map_voltage = arizona_ldo1_hc_map_voltage, + .get_voltage_sel = arizona_ldo1_hc_get_voltage_sel, + .set_voltage_sel = arizona_ldo1_hc_set_voltage_sel, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, +}; + +static const struct regulator_desc arizona_ldo1_hc = { + .name = "LDO1", + .supply_name = "LDOVDD", + .type = REGULATOR_VOLTAGE, + .ops = &arizona_ldo1_hc_ops, + + .bypass_reg = ARIZONA_LDO1_CONTROL_1, + .bypass_mask = ARIZONA_LDO1_BYPASS, + .min_uV = 900000, + .uV_step = 50000, + .n_voltages = 8, + .enable_time = 1500, + + .owner = THIS_MODULE, +}; + +static struct regulator_ops arizona_ldo1_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, +}; + +static const struct regulator_desc arizona_ldo1 = { + .name = "LDO1", + .supply_name = "LDOVDD", + .type = REGULATOR_VOLTAGE, + .ops = &arizona_ldo1_ops, + + .vsel_reg = ARIZONA_LDO1_CONTROL_1, + .vsel_mask = ARIZONA_LDO1_VSEL_MASK, + .bypass_reg = ARIZONA_LDO1_CONTROL_1, + .bypass_mask = ARIZONA_LDO1_BYPASS, + .min_uV = 900000, + .uV_step = 50000, + .n_voltages = 7, + .enable_time = 500, + + .owner = THIS_MODULE, +}; + +static const struct regulator_init_data arizona_ldo1_dvfs = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1800000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = 1, +}; + +static const struct regulator_init_data arizona_ldo1_default = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, +}; + +static int arizona_ldo1_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + const struct regulator_desc *desc; + struct regulator_config config = { }; + struct arizona_ldo1 *ldo1; + int ret; + + ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); + if (ldo1 == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo1->arizona = arizona; + + /* + * Since the chip usually supplies itself we provide some + * default init_data for it. This will be overridden with + * platform data if provided. + */ + switch (arizona->type) { + case WM5102: + desc = &arizona_ldo1_hc; + ldo1->init_data = arizona_ldo1_dvfs; + break; + default: + desc = &arizona_ldo1; + ldo1->init_data = arizona_ldo1_default; + break; + } + + ldo1->init_data.consumer_supplies = &ldo1->supply; + ldo1->supply.supply = "DCVDD"; + ldo1->supply.dev_name = dev_name(arizona->dev); + + config.dev = arizona->dev; + config.driver_data = ldo1; + config.regmap = arizona->regmap; + config.ena_gpio = arizona->pdata.ldoena; + + if (arizona->pdata.ldo1) + config.init_data = arizona->pdata.ldo1; + else + config.init_data = &ldo1->init_data; + + ldo1->regulator = regulator_register(desc, &config); + if (IS_ERR(ldo1->regulator)) { + ret = PTR_ERR(ldo1->regulator); + dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, ldo1); + + return 0; +} + +static int arizona_ldo1_remove(struct platform_device *pdev) +{ + struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev); + + regulator_unregister(ldo1->regulator); + + return 0; +} + +static struct platform_driver arizona_ldo1_driver = { + .probe = arizona_ldo1_probe, + .remove = arizona_ldo1_remove, + .driver = { + .name = "arizona-ldo1", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(arizona_ldo1_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Arizona LDO1 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:arizona-ldo1"); diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c new file mode 100644 index 0000000..e87536b --- /dev/null +++ b/drivers/regulator/arizona-micsupp.c @@ -0,0 +1,265 @@ +/* + * arizona-micsupp.c -- Microphone supply for Arizona devices + * + * Copyright 2012 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define ARIZONA_MICSUPP_MAX_SELECTOR 0x1f + +struct arizona_micsupp { + struct regulator_dev *regulator; + struct arizona *arizona; + + struct regulator_consumer_supply supply; + struct regulator_init_data init_data; + + struct work_struct check_cp_work; +}; + +static int arizona_micsupp_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector > ARIZONA_MICSUPP_MAX_SELECTOR) + return -EINVAL; + + if (selector == ARIZONA_MICSUPP_MAX_SELECTOR) + return 3300000; + else + return (selector * 50000) + 1700000; +} + +static int arizona_micsupp_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + unsigned int voltage; + int selector; + + if (min_uV < 1700000) + min_uV = 1700000; + + if (min_uV > 3200000) + selector = ARIZONA_MICSUPP_MAX_SELECTOR; + else + selector = DIV_ROUND_UP(min_uV - 1700000, 50000); + + if (selector < 0) + return -EINVAL; + + voltage = arizona_micsupp_list_voltage(rdev, selector); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return selector; +} + +static void arizona_micsupp_check_cp(struct work_struct *work) +{ + struct arizona_micsupp *micsupp = + container_of(work, struct arizona_micsupp, check_cp_work); + struct snd_soc_dapm_context *dapm = micsupp->arizona->dapm; + struct arizona *arizona = micsupp->arizona; + struct regmap *regmap = arizona->regmap; + unsigned int reg; + int ret; + + ret = regmap_read(regmap, ARIZONA_MIC_CHARGE_PUMP_1, ®); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read CP state: %d\n", ret); + return; + } + + if (dapm) { + if ((reg & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) == + ARIZONA_CPMIC_ENA) + snd_soc_dapm_force_enable_pin(dapm, "MICSUPP"); + else + snd_soc_dapm_disable_pin(dapm, "MICSUPP"); + + snd_soc_dapm_sync(dapm); + } +} + +static int arizona_micsupp_enable(struct regulator_dev *rdev) +{ + struct arizona_micsupp *micsupp = rdev_get_drvdata(rdev); + int ret; + + ret = regulator_enable_regmap(rdev); + + if (ret == 0) + schedule_work(&micsupp->check_cp_work); + + return ret; +} + +static int arizona_micsupp_disable(struct regulator_dev *rdev) +{ + struct arizona_micsupp *micsupp = rdev_get_drvdata(rdev); + int ret; + + ret = regulator_disable_regmap(rdev); + if (ret == 0) + schedule_work(&micsupp->check_cp_work); + + return ret; +} + +static int arizona_micsupp_set_bypass(struct regulator_dev *rdev, bool ena) +{ + struct arizona_micsupp *micsupp = rdev_get_drvdata(rdev); + int ret; + + ret = regulator_set_bypass_regmap(rdev, ena); + if (ret == 0) + schedule_work(&micsupp->check_cp_work); + + return ret; +} + +static struct regulator_ops arizona_micsupp_ops = { + .enable = arizona_micsupp_enable, + .disable = arizona_micsupp_disable, + .is_enabled = regulator_is_enabled_regmap, + + .list_voltage = arizona_micsupp_list_voltage, + .map_voltage = arizona_micsupp_map_voltage, + + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = arizona_micsupp_set_bypass, +}; + +static const struct regulator_desc arizona_micsupp = { + .name = "MICVDD", + .supply_name = "CPVDD", + .type = REGULATOR_VOLTAGE, + .n_voltages = ARIZONA_MICSUPP_MAX_SELECTOR + 1, + .ops = &arizona_micsupp_ops, + + .vsel_reg = ARIZONA_LDO2_CONTROL_1, + .vsel_mask = ARIZONA_LDO2_VSEL_MASK, + .enable_reg = ARIZONA_MIC_CHARGE_PUMP_1, + .enable_mask = ARIZONA_CPMIC_ENA, + .bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1, + .bypass_mask = ARIZONA_CPMIC_BYPASS, + + .enable_time = 3000, + + .owner = THIS_MODULE, +}; + +static const struct regulator_init_data arizona_micsupp_default = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_BYPASS, + .min_uV = 1700000, + .max_uV = 3300000, + }, + + .num_consumer_supplies = 1, +}; + +static int arizona_micsupp_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct arizona_micsupp *micsupp; + int ret; + + micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL); + if (micsupp == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + micsupp->arizona = arizona; + INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp); + + /* + * Since the chip usually supplies itself we provide some + * default init_data for it. This will be overridden with + * platform data if provided. + */ + micsupp->init_data = arizona_micsupp_default; + micsupp->init_data.consumer_supplies = &micsupp->supply; + micsupp->supply.supply = "MICVDD"; + micsupp->supply.dev_name = dev_name(arizona->dev); + + config.dev = arizona->dev; + config.driver_data = micsupp; + config.regmap = arizona->regmap; + + if (arizona->pdata.micvdd) + config.init_data = arizona->pdata.micvdd; + else + config.init_data = &micsupp->init_data; + + /* Default to regulated mode until the API supports bypass */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1, + ARIZONA_CPMIC_BYPASS, 0); + + micsupp->regulator = regulator_register(&arizona_micsupp, &config); + if (IS_ERR(micsupp->regulator)) { + ret = PTR_ERR(micsupp->regulator); + dev_err(arizona->dev, "Failed to register mic supply: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, micsupp); + + return 0; +} + +static int arizona_micsupp_remove(struct platform_device *pdev) +{ + struct arizona_micsupp *micsupp = platform_get_drvdata(pdev); + + regulator_unregister(micsupp->regulator); + + return 0; +} + +static struct platform_driver arizona_micsupp_driver = { + .probe = arizona_micsupp_probe, + .remove = arizona_micsupp_remove, + .driver = { + .name = "arizona-micsupp", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(arizona_micsupp_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Arizona microphone supply driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:arizona-micsupp"); diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c new file mode 100644 index 0000000..3da6bd6 --- /dev/null +++ b/drivers/regulator/as3711-regulator.c @@ -0,0 +1,430 @@ +/* + * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct as3711_regulator_info { + struct regulator_desc desc; + unsigned int max_uV; +}; + +struct as3711_regulator { + struct as3711_regulator_info *reg_info; + struct regulator_dev *rdev; +}; + +static int as3711_list_voltage_sd(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + if (!selector) + return 0; + if (selector < 0x41) + return 600000 + selector * 12500; + if (selector < 0x71) + return 1400000 + (selector - 0x40) * 25000; + return 2600000 + (selector - 0x70) * 50000; +} + +static int as3711_list_voltage_aldo(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + if (selector < 0x10) + return 1200000 + selector * 50000; + return 1800000 + (selector - 0x10) * 100000; +} + +static int as3711_list_voltage_dldo(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages || + (selector > 0x10 && selector < 0x20)) + return -EINVAL; + + if (selector < 0x11) + return 900000 + selector * 50000; + return 1750000 + (selector - 0x20) * 50000; +} + +static int as3711_bound_check(struct regulator_dev *rdev, + int *min_uV, int *max_uV) +{ + struct as3711_regulator *reg = rdev_get_drvdata(rdev); + struct as3711_regulator_info *info = reg->reg_info; + + dev_dbg(&rdev->dev, "%s(), %d, %d, %d\n", __func__, + *min_uV, rdev->desc->min_uV, info->max_uV); + + if (*max_uV < *min_uV || + *min_uV > info->max_uV || rdev->desc->min_uV > *max_uV) + return -EINVAL; + + if (rdev->desc->n_voltages == 1) + return 0; + + if (*max_uV > info->max_uV) + *max_uV = info->max_uV; + + if (*min_uV < rdev->desc->min_uV) + *min_uV = rdev->desc->min_uV; + + return *min_uV; +} + +static int as3711_sel_check(int min, int max, int bottom, int step) +{ + int sel, voltage; + + /* Round up min, when dividing: keeps us within the range */ + sel = DIV_ROUND_UP(min - bottom, step); + voltage = sel * step + bottom; + pr_debug("%s(): select %d..%d in %d+N*%d: %d\n", __func__, + min, max, bottom, step, sel); + if (voltage > max) + return -EINVAL; + + return sel; +} + +static int as3711_map_voltage_sd(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret; + + ret = as3711_bound_check(rdev, &min_uV, &max_uV); + if (ret <= 0) + return ret; + + if (min_uV <= 1400000) + return as3711_sel_check(min_uV, max_uV, 600000, 12500); + + if (min_uV <= 2600000) + return as3711_sel_check(min_uV, max_uV, 1400000, 25000) + 0x40; + + return as3711_sel_check(min_uV, max_uV, 2600000, 50000) + 0x70; +} + +/* + * The regulator API supports 4 modes of operataion: FAST, NORMAL, IDLE and + * STANDBY. We map them in the following way to AS3711 SD1-4 DCDC modes: + * FAST: sdX_fast=1 + * NORMAL: low_noise=1 + * IDLE: low_noise=0 + */ + +static int as3711_set_mode_sd(struct regulator_dev *rdev, unsigned int mode) +{ + unsigned int fast_bit = rdev->desc->enable_mask, + low_noise_bit = fast_bit << 4; + u8 val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = fast_bit | low_noise_bit; + break; + case REGULATOR_MODE_NORMAL: + val = low_noise_bit; + break; + case REGULATOR_MODE_IDLE: + val = 0; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, AS3711_SD_CONTROL_1, + low_noise_bit | fast_bit, val); +} + +static unsigned int as3711_get_mode_sd(struct regulator_dev *rdev) +{ + unsigned int fast_bit = rdev->desc->enable_mask, + low_noise_bit = fast_bit << 4, mask = fast_bit | low_noise_bit; + unsigned int val; + int ret = regmap_read(rdev->regmap, AS3711_SD_CONTROL_1, &val); + + if (ret < 0) + return ret; + + if ((val & mask) == mask) + return REGULATOR_MODE_FAST; + + if ((val & mask) == low_noise_bit) + return REGULATOR_MODE_NORMAL; + + if (!(val & mask)) + return REGULATOR_MODE_IDLE; + + return -EINVAL; +} + +static int as3711_map_voltage_aldo(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret; + + ret = as3711_bound_check(rdev, &min_uV, &max_uV); + if (ret <= 0) + return ret; + + if (min_uV <= 1800000) + return as3711_sel_check(min_uV, max_uV, 1200000, 50000); + + return as3711_sel_check(min_uV, max_uV, 1800000, 100000) + 0x10; +} + +static int as3711_map_voltage_dldo(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret; + + ret = as3711_bound_check(rdev, &min_uV, &max_uV); + if (ret <= 0) + return ret; + + if (min_uV <= 1700000) + return as3711_sel_check(min_uV, max_uV, 900000, 50000); + + return as3711_sel_check(min_uV, max_uV, 1750000, 50000) + 0x20; +} + +static struct regulator_ops as3711_sd_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = as3711_list_voltage_sd, + .map_voltage = as3711_map_voltage_sd, + .get_mode = as3711_get_mode_sd, + .set_mode = as3711_set_mode_sd, +}; + +static struct regulator_ops as3711_aldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = as3711_list_voltage_aldo, + .map_voltage = as3711_map_voltage_aldo, +}; + +static struct regulator_ops as3711_dldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = as3711_list_voltage_dldo, + .map_voltage = as3711_map_voltage_dldo, +}; + +#define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \ + [AS3711_REGULATOR_ ## _id] = { \ + .desc = { \ + .name = "as3711-regulator-" # _id, \ + .id = AS3711_REGULATOR_ ## _id, \ + .n_voltages = (_vmask + 1), \ + .ops = &as3711_ ## _sfx ## _ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = AS3711_ ## _id ## _VOLTAGE, \ + .vsel_mask = _vmask << _vshift, \ + .enable_reg = AS3711_ ## _en_reg, \ + .enable_mask = BIT(_en_bit), \ + .min_uV = _min_uV, \ + }, \ + .max_uV = _max_uV, \ +} + +static struct as3711_regulator_info as3711_reg_info[] = { + AS3711_REG(SD_1, SD_CONTROL, 0, 0x7f, 0, 612500, 3350000, sd), + AS3711_REG(SD_2, SD_CONTROL, 1, 0x7f, 0, 612500, 3350000, sd), + AS3711_REG(SD_3, SD_CONTROL, 2, 0x7f, 0, 612500, 3350000, sd), + AS3711_REG(SD_4, SD_CONTROL, 3, 0x7f, 0, 612500, 3350000, sd), + AS3711_REG(LDO_1, LDO_1_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo), + AS3711_REG(LDO_2, LDO_2_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo), + AS3711_REG(LDO_3, LDO_3_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo), + AS3711_REG(LDO_4, LDO_4_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo), + AS3711_REG(LDO_5, LDO_5_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo), + AS3711_REG(LDO_6, LDO_6_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo), + AS3711_REG(LDO_7, LDO_7_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo), + AS3711_REG(LDO_8, LDO_8_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo), + /* StepUp output voltage depends on supplying regulator */ +}; + +#define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info) + +static struct of_regulator_match +as3711_regulator_matches[AS3711_REGULATOR_NUM] = { + [AS3711_REGULATOR_SD_1] = { .name = "sd1" }, + [AS3711_REGULATOR_SD_2] = { .name = "sd2" }, + [AS3711_REGULATOR_SD_3] = { .name = "sd3" }, + [AS3711_REGULATOR_SD_4] = { .name = "sd4" }, + [AS3711_REGULATOR_LDO_1] = { .name = "ldo1" }, + [AS3711_REGULATOR_LDO_2] = { .name = "ldo2" }, + [AS3711_REGULATOR_LDO_3] = { .name = "ldo3" }, + [AS3711_REGULATOR_LDO_4] = { .name = "ldo4" }, + [AS3711_REGULATOR_LDO_5] = { .name = "ldo5" }, + [AS3711_REGULATOR_LDO_6] = { .name = "ldo6" }, + [AS3711_REGULATOR_LDO_7] = { .name = "ldo7" }, + [AS3711_REGULATOR_LDO_8] = { .name = "ldo8" }, +}; + +static int as3711_regulator_parse_dt(struct device *dev, + struct device_node **of_node, const int count) +{ + struct as3711_regulator_pdata *pdata = dev_get_platdata(dev); + struct device_node *regulators = + of_find_node_by_name(dev->parent->of_node, "regulators"); + struct of_regulator_match *match; + int ret, i; + + if (!regulators) { + dev_err(dev, "regulator node not found\n"); + return -ENODEV; + } + + ret = of_regulator_match(dev->parent, regulators, + as3711_regulator_matches, count); + of_node_put(regulators); + if (ret < 0) { + dev_err(dev, "Error parsing regulator init data: %d\n", ret); + return ret; + } + + for (i = 0, match = as3711_regulator_matches; i < count; i++, match++) + if (match->of_node) { + pdata->init_data[i] = match->init_data; + of_node[i] = match->of_node; + } + + return 0; +} + +static int as3711_regulator_probe(struct platform_device *pdev) +{ + struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev); + struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent); + struct regulator_init_data *reg_data; + struct regulator_config config = {.dev = &pdev->dev,}; + struct as3711_regulator *reg = NULL; + struct as3711_regulator *regs; + struct device_node *of_node[AS3711_REGULATOR_NUM] = {}; + struct regulator_dev *rdev; + struct as3711_regulator_info *ri; + int ret; + int id; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data...\n"); + return -ENODEV; + } + + if (pdev->dev.parent->of_node) { + ret = as3711_regulator_parse_dt(&pdev->dev, of_node, AS3711_REGULATOR_NUM); + if (ret < 0) { + dev_err(&pdev->dev, "DT parsing failed: %d\n", ret); + return ret; + } + } + + regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM * + sizeof(struct as3711_regulator), GFP_KERNEL); + if (!regs) { + dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + return -ENOMEM; + } + + for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) { + reg_data = pdata->init_data[id]; + + /* No need to register if there is no regulator data */ + if (!reg_data) + continue; + + reg = ®s[id]; + reg->reg_info = ri; + + config.init_data = reg_data; + config.driver_data = reg; + config.regmap = as3711->regmap; + config.of_node = of_node[id]; + + rdev = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + ret = PTR_ERR(rdev); + goto eregreg; + } + reg->rdev = rdev; + } + platform_set_drvdata(pdev, regs); + return 0; + +eregreg: + while (--id >= 0) + regulator_unregister(regs[id].rdev); + + return ret; +} + +static int as3711_regulator_remove(struct platform_device *pdev) +{ + struct as3711_regulator *regs = platform_get_drvdata(pdev); + int id; + + for (id = 0; id < AS3711_REGULATOR_NUM; ++id) + regulator_unregister(regs[id].rdev); + return 0; +} + +static struct platform_driver as3711_regulator_driver = { + .driver = { + .name = "as3711-regulator", + .owner = THIS_MODULE, + }, + .probe = as3711_regulator_probe, + .remove = as3711_regulator_remove, +}; + +static int __init as3711_regulator_init(void) +{ + return platform_driver_register(&as3711_regulator_driver); +} +subsys_initcall(as3711_regulator_init); + +static void __exit as3711_regulator_exit(void) +{ + platform_driver_unregister(&as3711_regulator_driver); +} +module_exit(as3711_regulator_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_DESCRIPTION("AS3711 regulator driver"); +MODULE_ALIAS("platform:as3711-regulator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c new file mode 100644 index 0000000..48738e6 --- /dev/null +++ b/drivers/regulator/core.c @@ -0,0 +1,4038 @@ +/* + * core.c -- Voltage/Current Regulator framework. + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * Copyright 2008 SlimLogic Ltd. + * + * Author: Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +#include "dummy.h" + +#define rdev_crit(rdev, fmt, ...) \ + pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) +#define rdev_err(rdev, fmt, ...) \ + pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) +#define rdev_warn(rdev, fmt, ...) \ + pr_warn("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) +#define rdev_info(rdev, fmt, ...) \ + pr_info("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) +#define rdev_dbg(rdev, fmt, ...) \ + pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) + +static DEFINE_MUTEX(regulator_list_mutex); +static LIST_HEAD(regulator_list); +static LIST_HEAD(regulator_map_list); +static LIST_HEAD(regulator_ena_gpio_list); +static bool has_full_constraints; +static bool board_wants_dummy_regulator; + +static struct dentry *debugfs_root; + +/* + * struct regulator_map + * + * Used to provide symbolic supply names to devices. + */ +struct regulator_map { + struct list_head list; + const char *dev_name; /* The dev_name() for the consumer */ + const char *supply; + struct regulator_dev *regulator; +}; + +/* + * struct regulator_enable_gpio + * + * Management for shared enable GPIO pin + */ +struct regulator_enable_gpio { + struct list_head list; + int gpio; + u32 enable_count; /* a number of enabled shared GPIO */ + u32 request_count; /* a number of requested shared GPIO */ + unsigned int ena_gpio_invert:1; +}; + +/* + * struct regulator + * + * One for each consumer device. + */ +struct regulator { + struct device *dev; + struct list_head list; + unsigned int always_on:1; + unsigned int bypass:1; + int uA_load; + int min_uV; + int max_uV; + char *supply_name; + struct device_attribute dev_attr; + struct regulator_dev *rdev; + struct dentry *debugfs; +}; + +static int _regulator_is_enabled(struct regulator_dev *rdev); +static int _regulator_disable(struct regulator_dev *rdev); +static int _regulator_get_voltage(struct regulator_dev *rdev); +static int _regulator_get_current_limit(struct regulator_dev *rdev); +static unsigned int _regulator_get_mode(struct regulator_dev *rdev); +static void _notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data); +static int _regulator_do_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV); +static struct regulator *create_regulator(struct regulator_dev *rdev, + struct device *dev, + const char *supply_name); + +static const char *rdev_get_name(struct regulator_dev *rdev) +{ + if (rdev->constraints && rdev->constraints->name) + return rdev->constraints->name; + else if (rdev->desc->name) + return rdev->desc->name; + else + return ""; +} + +/** + * of_get_regulator - get a regulator device node based on supply name + * @dev: Device pointer for the consumer (of regulator) device + * @supply: regulator supply name + * + * Extract the regulator device node corresponding to the supply name. + * returns the device node corresponding to the regulator if found, else + * returns NULL. + */ +static struct device_node *of_get_regulator(struct device *dev, const char *supply) +{ + struct device_node *regnode = NULL; + char prop_name[32]; /* 32 is max size of property name */ + + dev_dbg(dev, "Looking up %s-supply from device tree\n", supply); + + snprintf(prop_name, 32, "%s-supply", supply); + regnode = of_parse_phandle(dev->of_node, prop_name, 0); + + if (!regnode) { + dev_dbg(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + return NULL; + } + return regnode; +} + +static int _regulator_can_change_status(struct regulator_dev *rdev) +{ + if (!rdev->constraints) + return 0; + + if (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_STATUS) + return 1; + else + return 0; +} + +/* Platform voltage constraint check */ +static int regulator_check_voltage(struct regulator_dev *rdev, + int *min_uV, int *max_uV) +{ + BUG_ON(*min_uV > *max_uV); + + if (!rdev->constraints) { + rdev_err(rdev, "no constraints\n"); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + rdev_err(rdev, "operation not allowed\n"); + return -EPERM; + } + + if (*max_uV > rdev->constraints->max_uV) + *max_uV = rdev->constraints->max_uV; + if (*min_uV < rdev->constraints->min_uV) + *min_uV = rdev->constraints->min_uV; + + if (*min_uV > *max_uV) { + rdev_err(rdev, "unsupportable voltage range: %d-%duV\n", + *min_uV, *max_uV); + return -EINVAL; + } + + return 0; +} + +/* Make sure we select a voltage that suits the needs of all + * regulator consumers + */ +static int regulator_check_consumers(struct regulator_dev *rdev, + int *min_uV, int *max_uV) +{ + struct regulator *regulator; + + list_for_each_entry(regulator, &rdev->consumer_list, list) { + /* + * Assume consumers that didn't say anything are OK + * with anything in the constraint range. + */ + if (!regulator->min_uV && !regulator->max_uV) + continue; + + if (*max_uV > regulator->max_uV) + *max_uV = regulator->max_uV; + if (*min_uV < regulator->min_uV) + *min_uV = regulator->min_uV; + } + + if (*min_uV > *max_uV) { + rdev_err(rdev, "Restricting voltage, %u-%uuV\n", + *min_uV, *max_uV); + return -EINVAL; + } + + return 0; +} + +/* current constraint check */ +static int regulator_check_current_limit(struct regulator_dev *rdev, + int *min_uA, int *max_uA) +{ + BUG_ON(*min_uA > *max_uA); + + if (!rdev->constraints) { + rdev_err(rdev, "no constraints\n"); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) { + rdev_err(rdev, "operation not allowed\n"); + return -EPERM; + } + + if (*max_uA > rdev->constraints->max_uA) + *max_uA = rdev->constraints->max_uA; + if (*min_uA < rdev->constraints->min_uA) + *min_uA = rdev->constraints->min_uA; + + if (*min_uA > *max_uA) { + rdev_err(rdev, "unsupportable current range: %d-%duA\n", + *min_uA, *max_uA); + return -EINVAL; + } + + return 0; +} + +/* operating mode constraint check */ +static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) +{ + switch (*mode) { + case REGULATOR_MODE_FAST: + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_IDLE: + case REGULATOR_MODE_STANDBY: + break; + default: + rdev_err(rdev, "invalid mode %x specified\n", *mode); + return -EINVAL; + } + + if (!rdev->constraints) { + rdev_err(rdev, "no constraints\n"); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) { + rdev_err(rdev, "operation not allowed\n"); + return -EPERM; + } + + /* The modes are bitmasks, the most power hungry modes having + * the lowest values. If the requested mode isn't supported + * try higher modes. */ + while (*mode) { + if (rdev->constraints->valid_modes_mask & *mode) + return 0; + *mode /= 2; + } + + return -EINVAL; +} + +/* dynamic regulator mode switching constraint check */ +static int regulator_check_drms(struct regulator_dev *rdev) +{ + if (!rdev->constraints) { + rdev_err(rdev, "no constraints\n"); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) { + rdev_err(rdev, "operation not allowed\n"); + return -EPERM; + } + return 0; +} + +static ssize_t regulator_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&rdev->mutex); + ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev)); + mutex_unlock(&rdev->mutex); + + return ret; +} +static DEVICE_ATTR(microvolts, 0444, regulator_uV_show, NULL); + +static ssize_t regulator_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev)); +} +static DEVICE_ATTR(microamps, 0444, regulator_uA_show, NULL); + +static ssize_t regulator_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", rdev_get_name(rdev)); +} + +static ssize_t regulator_print_opmode(char *buf, int mode) +{ + switch (mode) { + case REGULATOR_MODE_FAST: + return sprintf(buf, "fast\n"); + case REGULATOR_MODE_NORMAL: + return sprintf(buf, "normal\n"); + case REGULATOR_MODE_IDLE: + return sprintf(buf, "idle\n"); + case REGULATOR_MODE_STANDBY: + return sprintf(buf, "standby\n"); + } + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_opmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_opmode(buf, _regulator_get_mode(rdev)); +} +static DEVICE_ATTR(opmode, 0444, regulator_opmode_show, NULL); + +static ssize_t regulator_print_state(char *buf, int state) +{ + if (state > 0) + return sprintf(buf, "enabled\n"); + else if (state == 0) + return sprintf(buf, "disabled\n"); + else + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&rdev->mutex); + ret = regulator_print_state(buf, _regulator_is_enabled(rdev)); + mutex_unlock(&rdev->mutex); + + return ret; +} +static DEVICE_ATTR(state, 0444, regulator_state_show, NULL); + +static ssize_t regulator_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + int status; + char *label; + + status = rdev->desc->ops->get_status(rdev); + if (status < 0) + return status; + + switch (status) { + case REGULATOR_STATUS_OFF: + label = "off"; + break; + case REGULATOR_STATUS_ON: + label = "on"; + break; + case REGULATOR_STATUS_ERROR: + label = "error"; + break; + case REGULATOR_STATUS_FAST: + label = "fast"; + break; + case REGULATOR_STATUS_NORMAL: + label = "normal"; + break; + case REGULATOR_STATUS_IDLE: + label = "idle"; + break; + case REGULATOR_STATUS_STANDBY: + label = "standby"; + break; + case REGULATOR_STATUS_BYPASS: + label = "bypass"; + break; + case REGULATOR_STATUS_UNDEFINED: + label = "undefined"; + break; + default: + return -ERANGE; + } + + return sprintf(buf, "%s\n", label); +} +static DEVICE_ATTR(status, 0444, regulator_status_show, NULL); + +static ssize_t regulator_min_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->min_uA); +} +static DEVICE_ATTR(min_microamps, 0444, regulator_min_uA_show, NULL); + +static ssize_t regulator_max_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->max_uA); +} +static DEVICE_ATTR(max_microamps, 0444, regulator_max_uA_show, NULL); + +static ssize_t regulator_min_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->min_uV); +} +static DEVICE_ATTR(min_microvolts, 0444, regulator_min_uV_show, NULL); + +static ssize_t regulator_max_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->max_uV); +} +static DEVICE_ATTR(max_microvolts, 0444, regulator_max_uV_show, NULL); + +static ssize_t regulator_total_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + struct regulator *regulator; + int uA = 0; + + mutex_lock(&rdev->mutex); + list_for_each_entry(regulator, &rdev->consumer_list, list) + uA += regulator->uA_load; + mutex_unlock(&rdev->mutex); + return sprintf(buf, "%d\n", uA); +} +static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL); + +static ssize_t regulator_num_users_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", rdev->use_count); +} + +static ssize_t regulator_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + switch (rdev->desc->type) { + case REGULATOR_VOLTAGE: + return sprintf(buf, "voltage\n"); + case REGULATOR_CURRENT: + return sprintf(buf, "current\n"); + } + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_suspend_mem_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", rdev->constraints->state_mem.uV); +} +static DEVICE_ATTR(suspend_mem_microvolts, 0444, + regulator_suspend_mem_uV_show, NULL); + +static ssize_t regulator_suspend_disk_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", rdev->constraints->state_disk.uV); +} +static DEVICE_ATTR(suspend_disk_microvolts, 0444, + regulator_suspend_disk_uV_show, NULL); + +static ssize_t regulator_suspend_standby_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", rdev->constraints->state_standby.uV); +} +static DEVICE_ATTR(suspend_standby_microvolts, 0444, + regulator_suspend_standby_uV_show, NULL); + +static ssize_t regulator_suspend_mem_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_opmode(buf, + rdev->constraints->state_mem.mode); +} +static DEVICE_ATTR(suspend_mem_mode, 0444, + regulator_suspend_mem_mode_show, NULL); + +static ssize_t regulator_suspend_disk_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_opmode(buf, + rdev->constraints->state_disk.mode); +} +static DEVICE_ATTR(suspend_disk_mode, 0444, + regulator_suspend_disk_mode_show, NULL); + +static ssize_t regulator_suspend_standby_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_opmode(buf, + rdev->constraints->state_standby.mode); +} +static DEVICE_ATTR(suspend_standby_mode, 0444, + regulator_suspend_standby_mode_show, NULL); + +static ssize_t regulator_suspend_mem_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_state(buf, + rdev->constraints->state_mem.enabled); +} +static DEVICE_ATTR(suspend_mem_state, 0444, + regulator_suspend_mem_state_show, NULL); + +static ssize_t regulator_suspend_disk_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_state(buf, + rdev->constraints->state_disk.enabled); +} +static DEVICE_ATTR(suspend_disk_state, 0444, + regulator_suspend_disk_state_show, NULL); + +static ssize_t regulator_suspend_standby_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_state(buf, + rdev->constraints->state_standby.enabled); +} +static DEVICE_ATTR(suspend_standby_state, 0444, + regulator_suspend_standby_state_show, NULL); + +static ssize_t regulator_bypass_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + const char *report; + bool bypass; + int ret; + + ret = rdev->desc->ops->get_bypass(rdev, &bypass); + + if (ret != 0) + report = "unknown"; + else if (bypass) + report = "enabled"; + else + report = "disabled"; + + return sprintf(buf, "%s\n", report); +} +static DEVICE_ATTR(bypass, 0444, + regulator_bypass_show, NULL); + +/* + * These are the only attributes are present for all regulators. + * Other attributes are a function of regulator functionality. + */ +static struct device_attribute regulator_dev_attrs[] = { + __ATTR(name, 0444, regulator_name_show, NULL), + __ATTR(num_users, 0444, regulator_num_users_show, NULL), + __ATTR(type, 0444, regulator_type_show, NULL), + __ATTR_NULL, +}; + +static void regulator_dev_release(struct device *dev) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + kfree(rdev); +} + +static struct class regulator_class = { + .name = "regulator", + .dev_release = regulator_dev_release, + .dev_attrs = regulator_dev_attrs, +}; + +/* Calculate the new optimum regulator operating mode based on the new total + * consumer load. All locks held by caller */ +static void drms_uA_update(struct regulator_dev *rdev) +{ + struct regulator *sibling; + int current_uA = 0, output_uV, input_uV, err; + unsigned int mode; + + err = regulator_check_drms(rdev); + if (err < 0 || !rdev->desc->ops->get_optimum_mode || + (!rdev->desc->ops->get_voltage && + !rdev->desc->ops->get_voltage_sel) || + !rdev->desc->ops->set_mode) + return; + + /* get output voltage */ + output_uV = _regulator_get_voltage(rdev); + if (output_uV <= 0) + return; + + /* get input voltage */ + input_uV = 0; + if (rdev->supply) + input_uV = regulator_get_voltage(rdev->supply); + if (input_uV <= 0) + input_uV = rdev->constraints->input_uV; + if (input_uV <= 0) + return; + + /* calc total requested load */ + list_for_each_entry(sibling, &rdev->consumer_list, list) + current_uA += sibling->uA_load; + + /* now get the optimum mode for our new total regulator load */ + mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, + output_uV, current_uA); + + /* check the new mode is allowed */ + err = regulator_mode_constrain(rdev, &mode); + if (err == 0) + rdev->desc->ops->set_mode(rdev, mode); +} + +static int suspend_set_state(struct regulator_dev *rdev, + struct regulator_state *rstate) +{ + int ret = 0; + + /* If we have no suspend mode configration don't set anything; + * only warn if the driver implements set_suspend_voltage or + * set_suspend_mode callback. + */ + if (!rstate->enabled && !rstate->disabled) { + if (rdev->desc->ops->set_suspend_voltage || + rdev->desc->ops->set_suspend_mode) + rdev_warn(rdev, "No configuration\n"); + return 0; + } + + if (rstate->enabled && rstate->disabled) { + rdev_err(rdev, "invalid configuration\n"); + return -EINVAL; + } + + if (rstate->enabled && rdev->desc->ops->set_suspend_enable) + ret = rdev->desc->ops->set_suspend_enable(rdev); + else if (rstate->disabled && rdev->desc->ops->set_suspend_disable) + ret = rdev->desc->ops->set_suspend_disable(rdev); + else /* OK if set_suspend_enable or set_suspend_disable is NULL */ + ret = 0; + + if (ret < 0) { + rdev_err(rdev, "failed to enabled/disable\n"); + return ret; + } + + if (rdev->desc->ops->set_suspend_voltage && rstate->uV > 0) { + ret = rdev->desc->ops->set_suspend_voltage(rdev, rstate->uV); + if (ret < 0) { + rdev_err(rdev, "failed to set voltage\n"); + return ret; + } + } + + if (rdev->desc->ops->set_suspend_mode && rstate->mode > 0) { + ret = rdev->desc->ops->set_suspend_mode(rdev, rstate->mode); + if (ret < 0) { + rdev_err(rdev, "failed to set mode\n"); + return ret; + } + } + return ret; +} + +/* locks held by caller */ +static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state) +{ + if (!rdev->constraints) + return -EINVAL; + + switch (state) { + case PM_SUSPEND_STANDBY: + return suspend_set_state(rdev, + &rdev->constraints->state_standby); + case PM_SUSPEND_MEM: + return suspend_set_state(rdev, + &rdev->constraints->state_mem); + case PM_SUSPEND_MAX: + return suspend_set_state(rdev, + &rdev->constraints->state_disk); + default: + return -EINVAL; + } +} + +static void print_constraints(struct regulator_dev *rdev) +{ + struct regulation_constraints *constraints = rdev->constraints; + char buf[80] = ""; + int count = 0; + int ret; + + if (constraints->min_uV && constraints->max_uV) { + if (constraints->min_uV == constraints->max_uV) + count += sprintf(buf + count, "%d mV ", + constraints->min_uV / 1000); + else + count += sprintf(buf + count, "%d <--> %d mV ", + constraints->min_uV / 1000, + constraints->max_uV / 1000); + } + + if (!constraints->min_uV || + constraints->min_uV != constraints->max_uV) { + ret = _regulator_get_voltage(rdev); + if (ret > 0) + count += sprintf(buf + count, "at %d mV ", ret / 1000); + } + + if (constraints->uV_offset) + count += sprintf(buf, "%dmV offset ", + constraints->uV_offset / 1000); + + if (constraints->min_uA && constraints->max_uA) { + if (constraints->min_uA == constraints->max_uA) + count += sprintf(buf + count, "%d mA ", + constraints->min_uA / 1000); + else + count += sprintf(buf + count, "%d <--> %d mA ", + constraints->min_uA / 1000, + constraints->max_uA / 1000); + } + + if (!constraints->min_uA || + constraints->min_uA != constraints->max_uA) { + ret = _regulator_get_current_limit(rdev); + if (ret > 0) + count += sprintf(buf + count, "at %d mA ", ret / 1000); + } + + if (constraints->valid_modes_mask & REGULATOR_MODE_FAST) + count += sprintf(buf + count, "fast "); + if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL) + count += sprintf(buf + count, "normal "); + if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE) + count += sprintf(buf + count, "idle "); + if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY) + count += sprintf(buf + count, "standby"); + + if (!count) + sprintf(buf, "no parameters"); + + rdev_info(rdev, "%s\n", buf); + + if ((constraints->min_uV != constraints->max_uV) && + !(constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) + rdev_warn(rdev, + "Voltage range but no REGULATOR_CHANGE_VOLTAGE\n"); +} + +static int machine_constraints_voltage(struct regulator_dev *rdev, + struct regulation_constraints *constraints) +{ + struct regulator_ops *ops = rdev->desc->ops; + int ret; + + /* do we need to apply the constraint voltage */ + if (rdev->constraints->apply_uV && + rdev->constraints->min_uV == rdev->constraints->max_uV) { + ret = _regulator_do_set_voltage(rdev, + rdev->constraints->min_uV, + rdev->constraints->max_uV); + if (ret < 0) { + rdev_err(rdev, "failed to apply %duV constraint\n", + rdev->constraints->min_uV); + return ret; + } + } + + /* constrain machine-level voltage specs to fit + * the actual range supported by this regulator. + */ + if (ops->list_voltage && rdev->desc->n_voltages) { + int count = rdev->desc->n_voltages; + int i; + int min_uV = INT_MAX; + int max_uV = INT_MIN; + int cmin = constraints->min_uV; + int cmax = constraints->max_uV; + + /* it's safe to autoconfigure fixed-voltage supplies + and the constraints are used by list_voltage. */ + if (count == 1 && !cmin) { + cmin = 1; + cmax = INT_MAX; + constraints->min_uV = cmin; + constraints->max_uV = cmax; + } + + /* voltage constraints are optional */ + if ((cmin == 0) && (cmax == 0)) + return 0; + + /* else require explicit machine-level constraints */ + if (cmin <= 0 || cmax <= 0 || cmax < cmin) { + rdev_err(rdev, "invalid voltage constraints\n"); + return -EINVAL; + } + + /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ + for (i = 0; i < count; i++) { + int value; + + value = ops->list_voltage(rdev, i); + if (value <= 0) + continue; + + /* maybe adjust [min_uV..max_uV] */ + if (value >= cmin && value < min_uV) + min_uV = value; + if (value <= cmax && value > max_uV) + max_uV = value; + } + + /* final: [min_uV..max_uV] valid iff constraints valid */ + if (max_uV < min_uV) { + rdev_err(rdev, + "unsupportable voltage constraints %u-%uuV\n", + min_uV, max_uV); + return -EINVAL; + } + + /* use regulator's subset of machine constraints */ + if (constraints->min_uV < min_uV) { + rdev_dbg(rdev, "override min_uV, %d -> %d\n", + constraints->min_uV, min_uV); + constraints->min_uV = min_uV; + } + if (constraints->max_uV > max_uV) { + rdev_dbg(rdev, "override max_uV, %d -> %d\n", + constraints->max_uV, max_uV); + constraints->max_uV = max_uV; + } + } + + return 0; +} + +/** + * set_machine_constraints - sets regulator constraints + * @rdev: regulator source + * @constraints: constraints to apply + * + * Allows platform initialisation code to define and constrain + * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: + * Constraints *must* be set by platform code in order for some + * regulator operations to proceed i.e. set_voltage, set_current_limit, + * set_mode. + */ +static int set_machine_constraints(struct regulator_dev *rdev, + const struct regulation_constraints *constraints) +{ + int ret = 0; + struct regulator_ops *ops = rdev->desc->ops; + + if (constraints) + rdev->constraints = kmemdup(constraints, sizeof(*constraints), + GFP_KERNEL); + else + rdev->constraints = kzalloc(sizeof(*constraints), + GFP_KERNEL); + if (!rdev->constraints) + return -ENOMEM; + + ret = machine_constraints_voltage(rdev, rdev->constraints); + if (ret != 0) + goto out; + + /* do we need to setup our suspend state */ + if (rdev->constraints->initial_state) { + ret = suspend_prepare(rdev, rdev->constraints->initial_state); + if (ret < 0) { + rdev_err(rdev, "failed to set suspend state\n"); + goto out; + } + } + + if (rdev->constraints->initial_mode) { + if (!ops->set_mode) { + rdev_err(rdev, "no set_mode operation\n"); + ret = -EINVAL; + goto out; + } + + ret = ops->set_mode(rdev, rdev->constraints->initial_mode); + if (ret < 0) { + rdev_err(rdev, "failed to set initial mode: %d\n", ret); + goto out; + } + } + + /* If the constraints say the regulator should be on at this point + * and we have control then make sure it is enabled. + */ + if ((rdev->constraints->always_on || rdev->constraints->boot_on) && + ops->enable) { + ret = ops->enable(rdev); + if (ret < 0) { + rdev_err(rdev, "failed to enable\n"); + goto out; + } + } + + if (rdev->constraints->ramp_delay && ops->set_ramp_delay) { + ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); + if (ret < 0) { + rdev_err(rdev, "failed to set ramp_delay\n"); + goto out; + } + } + + print_constraints(rdev); + return 0; +out: + kfree(rdev->constraints); + rdev->constraints = NULL; + return ret; +} + +/** + * set_supply - set regulator supply regulator + * @rdev: regulator name + * @supply_rdev: supply regulator name + * + * Called by platform initialisation code to set the supply regulator for this + * regulator. This ensures that a regulators supply will also be enabled by the + * core if it's child is enabled. + */ +static int set_supply(struct regulator_dev *rdev, + struct regulator_dev *supply_rdev) +{ + int err; + + rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev)); + + rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY"); + if (rdev->supply == NULL) { + err = -ENOMEM; + return err; + } + supply_rdev->open_count++; + + return 0; +} + +/** + * set_consumer_device_supply - Bind a regulator to a symbolic supply + * @rdev: regulator source + * @consumer_dev_name: dev_name() string for device supply applies to + * @supply: symbolic name for supply + * + * Allows platform initialisation code to map physical regulator + * sources to symbolic names for supplies for use by devices. Devices + * should use these symbolic names to request regulators, avoiding the + * need to provide board-specific regulator names as platform data. + */ +static int set_consumer_device_supply(struct regulator_dev *rdev, + const char *consumer_dev_name, + const char *supply) +{ + struct regulator_map *node; + int has_dev; + + if (supply == NULL) + return -EINVAL; + + if (consumer_dev_name != NULL) + has_dev = 1; + else + has_dev = 0; + + list_for_each_entry(node, ®ulator_map_list, list) { + if (node->dev_name && consumer_dev_name) { + if (strcmp(node->dev_name, consumer_dev_name) != 0) + continue; + } else if (node->dev_name || consumer_dev_name) { + continue; + } + + if (strcmp(node->supply, supply) != 0) + continue; + + pr_debug("%s: %s/%s is '%s' supply; fail %s/%s\n", + consumer_dev_name, + dev_name(&node->regulator->dev), + node->regulator->desc->name, + supply, + dev_name(&rdev->dev), rdev_get_name(rdev)); + return -EBUSY; + } + + node = kzalloc(sizeof(struct regulator_map), GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + + node->regulator = rdev; + node->supply = supply; + + if (has_dev) { + node->dev_name = kstrdup(consumer_dev_name, GFP_KERNEL); + if (node->dev_name == NULL) { + kfree(node); + return -ENOMEM; + } + } + + list_add(&node->list, ®ulator_map_list); + return 0; +} + +static void unset_regulator_supplies(struct regulator_dev *rdev) +{ + struct regulator_map *node, *n; + + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { + if (rdev == node->regulator) { + list_del(&node->list); + kfree(node->dev_name); + kfree(node); + } + } +} + +#define REG_STR_SIZE 64 + +static struct regulator *create_regulator(struct regulator_dev *rdev, + struct device *dev, + const char *supply_name) +{ + struct regulator *regulator; + char buf[REG_STR_SIZE]; + int err, size; + + regulator = kzalloc(sizeof(*regulator), GFP_KERNEL); + if (regulator == NULL) + return NULL; + + mutex_lock(&rdev->mutex); + regulator->rdev = rdev; + list_add(®ulator->list, &rdev->consumer_list); + + if (dev) { + regulator->dev = dev; + + /* Add a link to the device sysfs entry */ + size = scnprintf(buf, REG_STR_SIZE, "%s-%s", + dev->kobj.name, supply_name); + if (size >= REG_STR_SIZE) + goto overflow_err; + + regulator->supply_name = kstrdup(buf, GFP_KERNEL); + if (regulator->supply_name == NULL) + goto overflow_err; + + err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj, + buf); + if (err) { + rdev_warn(rdev, "could not add device link %s err %d\n", + dev->kobj.name, err); + /* non-fatal */ + } + } else { + regulator->supply_name = kstrdup(supply_name, GFP_KERNEL); + if (regulator->supply_name == NULL) + goto overflow_err; + } + + regulator->debugfs = debugfs_create_dir(regulator->supply_name, + rdev->debugfs); + if (!regulator->debugfs) { + rdev_warn(rdev, "Failed to create debugfs directory\n"); + } else { + debugfs_create_u32("uA_load", 0444, regulator->debugfs, + ®ulator->uA_load); + debugfs_create_u32("min_uV", 0444, regulator->debugfs, + ®ulator->min_uV); + debugfs_create_u32("max_uV", 0444, regulator->debugfs, + ®ulator->max_uV); + } + + /* + * Check now if the regulator is an always on regulator - if + * it is then we don't need to do nearly so much work for + * enable/disable calls. + */ + if (!_regulator_can_change_status(rdev) && + _regulator_is_enabled(rdev)) + regulator->always_on = true; + + mutex_unlock(&rdev->mutex); + return regulator; +overflow_err: + list_del(®ulator->list); + kfree(regulator); + mutex_unlock(&rdev->mutex); + return NULL; +} + +static int _regulator_get_enable_time(struct regulator_dev *rdev) +{ + if (!rdev->desc->ops->enable_time) + return rdev->desc->enable_time; + return rdev->desc->ops->enable_time(rdev); +} + +static struct regulator_dev *regulator_dev_lookup(struct device *dev, + const char *supply, + int *ret) +{ + struct regulator_dev *r; + struct device_node *node; + struct regulator_map *map; + const char *devname = NULL; + + /* first do a dt based lookup */ + if (dev && dev->of_node) { + node = of_get_regulator(dev, supply); + if (node) { + list_for_each_entry(r, ®ulator_list, list) + if (r->dev.parent && + node == r->dev.of_node) + return r; + } else { + /* + * If we couldn't even get the node then it's + * not just that the device didn't register + * yet, there's no node and we'll never + * succeed. + */ + *ret = -ENODEV; + } + } + + /* if not found, try doing it non-dt way */ + if (dev) + devname = dev_name(dev); + + list_for_each_entry(r, ®ulator_list, list) + if (strcmp(rdev_get_name(r), supply) == 0) + return r; + + list_for_each_entry(map, ®ulator_map_list, list) { + /* If the mapping has a device set up it must match */ + if (map->dev_name && + (!devname || strcmp(map->dev_name, devname))) + continue; + + if (strcmp(map->supply, supply) == 0) + return map->regulator; + } + + + return NULL; +} + +/* Internal regulator request function */ +static struct regulator *_regulator_get(struct device *dev, const char *id, + int exclusive) +{ + struct regulator_dev *rdev; + struct regulator *regulator = ERR_PTR(-EPROBE_DEFER); + const char *devname = NULL; + int ret = 0; + + if (id == NULL) { + pr_err("get() with no identifier\n"); + return regulator; + } + + if (dev) + devname = dev_name(dev); + + mutex_lock(®ulator_list_mutex); + + rdev = regulator_dev_lookup(dev, id, &ret); + if (rdev) + goto found; + + /* + * If we have return value from dev_lookup fail, we do not expect to + * succeed, so, quit with appropriate error value + */ + if (ret) { + regulator = ERR_PTR(ret); + goto out; + } + + if (board_wants_dummy_regulator) { + rdev = dummy_regulator_rdev; + goto found; + } + +#ifdef CPTCFG_REGULATOR_DUMMY + if (!devname) + devname = "deviceless"; + + /* If the board didn't flag that it was fully constrained then + * substitute in a dummy regulator so consumers can continue. + */ + if (!has_full_constraints) { + pr_warn("%s supply %s not found, using dummy regulator\n", + devname, id); + rdev = dummy_regulator_rdev; + goto found; + } +#endif + + mutex_unlock(®ulator_list_mutex); + return regulator; + +found: + if (rdev->exclusive) { + regulator = ERR_PTR(-EPERM); + goto out; + } + + if (exclusive && rdev->open_count) { + regulator = ERR_PTR(-EBUSY); + goto out; + } + + if (!try_module_get(rdev->owner)) + goto out; + + regulator = create_regulator(rdev, dev, id); + if (regulator == NULL) { + regulator = ERR_PTR(-ENOMEM); + module_put(rdev->owner); + goto out; + } + + rdev->open_count++; + if (exclusive) { + rdev->exclusive = 1; + + ret = _regulator_is_enabled(rdev); + if (ret > 0) + rdev->use_count = 1; + else + rdev->use_count = 0; + } + +out: + mutex_unlock(®ulator_list_mutex); + + return regulator; +} + +/** + * regulator_get - lookup and obtain a reference to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 0); +} +EXPORT_SYMBOL_GPL(regulator_get); + +static void devm_regulator_release(struct device *dev, void *res) +{ + regulator_put(*(struct regulator **)res); +} + +/** + * devm_regulator_get - Resource managed regulator_get() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get(). Regulators returned from this function are + * automatically regulator_put() on driver detach. See regulator_get() for more + * information. + */ +struct regulator *devm_regulator_get(struct device *dev, const char *id) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regulator = regulator_get(dev, id); + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} +EXPORT_SYMBOL_GPL(devm_regulator_get); + +/** + * regulator_get_exclusive - obtain exclusive access to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. Other consumers will be + * unable to obtain this reference is held and the use count for the + * regulator will be initialised to reflect the current state of the + * regulator. + * + * This is intended for use by consumers which cannot tolerate shared + * use of the regulator such as those which need to force the + * regulator off for correct operation of the hardware they are + * controlling. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get_exclusive(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 1); +} +EXPORT_SYMBOL_GPL(regulator_get_exclusive); + +/* Locks held by regulator_put() */ +static void _regulator_put(struct regulator *regulator) +{ + struct regulator_dev *rdev; + + if (regulator == NULL || IS_ERR(regulator)) + return; + + rdev = regulator->rdev; + + debugfs_remove_recursive(regulator->debugfs); + + /* remove any sysfs entries */ + if (regulator->dev) + sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); + kfree(regulator->supply_name); + list_del(®ulator->list); + kfree(regulator); + + rdev->open_count--; + rdev->exclusive = 0; + + module_put(rdev->owner); +} + +/** + * regulator_put - "free" the regulator source + * @regulator: regulator source + * + * Note: drivers must ensure that all regulator_enable calls made on this + * regulator source are balanced by regulator_disable calls prior to calling + * this function. + */ +void regulator_put(struct regulator *regulator) +{ + mutex_lock(®ulator_list_mutex); + _regulator_put(regulator); + mutex_unlock(®ulator_list_mutex); +} +EXPORT_SYMBOL_GPL(regulator_put); + +static int devm_regulator_match(struct device *dev, void *res, void *data) +{ + struct regulator **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +/** + * devm_regulator_put - Resource managed regulator_put() + * @regulator: regulator to free + * + * Deallocate a regulator allocated with devm_regulator_get(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_regulator_put(struct regulator *regulator) +{ + int rc; + + rc = devres_release(regulator->dev, devm_regulator_release, + devm_regulator_match, regulator); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_put); + +/* Manage enable GPIO list. Same GPIO pin can be shared among regulators */ +static int regulator_ena_gpio_request(struct regulator_dev *rdev, + const struct regulator_config *config) +{ + struct regulator_enable_gpio *pin; + int ret; + + list_for_each_entry(pin, ®ulator_ena_gpio_list, list) { + if (pin->gpio == config->ena_gpio) { + rdev_dbg(rdev, "GPIO %d is already used\n", + config->ena_gpio); + goto update_ena_gpio_to_rdev; + } + } + + ret = gpio_request_one(config->ena_gpio, + GPIOF_DIR_OUT | config->ena_gpio_flags, + rdev_get_name(rdev)); + if (ret) + return ret; + + pin = kzalloc(sizeof(struct regulator_enable_gpio), GFP_KERNEL); + if (pin == NULL) { + gpio_free(config->ena_gpio); + return -ENOMEM; + } + + pin->gpio = config->ena_gpio; + pin->ena_gpio_invert = config->ena_gpio_invert; + list_add(&pin->list, ®ulator_ena_gpio_list); + +update_ena_gpio_to_rdev: + pin->request_count++; + rdev->ena_pin = pin; + return 0; +} + +static void regulator_ena_gpio_free(struct regulator_dev *rdev) +{ + struct regulator_enable_gpio *pin, *n; + + if (!rdev->ena_pin) + return; + + /* Free the GPIO only in case of no use */ + list_for_each_entry_safe(pin, n, ®ulator_ena_gpio_list, list) { + if (pin->gpio == rdev->ena_pin->gpio) { + if (pin->request_count <= 1) { + pin->request_count = 0; + gpio_free(pin->gpio); + list_del(&pin->list); + kfree(pin); + } else { + pin->request_count--; + } + } + } +} + +/** + * regulator_ena_gpio_ctrl - balance enable_count of each GPIO and actual GPIO pin control + * @rdev: regulator_dev structure + * @enable: enable GPIO at initial use? + * + * GPIO is enabled in case of initial use. (enable_count is 0) + * GPIO is disabled when it is not shared any more. (enable_count <= 1) + */ +static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable) +{ + struct regulator_enable_gpio *pin = rdev->ena_pin; + + if (!pin) + return -EINVAL; + + if (enable) { + /* Enable GPIO at initial use */ + if (pin->enable_count == 0) + gpio_set_value_cansleep(pin->gpio, + !pin->ena_gpio_invert); + + pin->enable_count++; + } else { + if (pin->enable_count > 1) { + pin->enable_count--; + return 0; + } + + /* Disable GPIO if not used */ + if (pin->enable_count <= 1) { + gpio_set_value_cansleep(pin->gpio, + pin->ena_gpio_invert); + pin->enable_count = 0; + } + } + + return 0; +} + +static int _regulator_do_enable(struct regulator_dev *rdev) +{ + int ret, delay; + + /* Query before enabling in case configuration dependent. */ + ret = _regulator_get_enable_time(rdev); + if (ret >= 0) { + delay = ret; + } else { + rdev_warn(rdev, "enable_time() failed: %d\n", ret); + delay = 0; + } + + trace_regulator_enable(rdev_get_name(rdev)); + + if (rdev->ena_pin) { + ret = regulator_ena_gpio_ctrl(rdev, true); + if (ret < 0) + return ret; + rdev->ena_gpio_state = 1; + } else if (rdev->desc->ops->enable) { + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + } else { + return -EINVAL; + } + + /* Allow the regulator to ramp; it would be useful to extend + * this for bulk operations so that the regulators can ramp + * together. */ + trace_regulator_enable_delay(rdev_get_name(rdev)); + + if (delay >= 1000) { + mdelay(delay / 1000); + udelay(delay % 1000); + } else if (delay) { + udelay(delay); + } + + trace_regulator_enable_complete(rdev_get_name(rdev)); + + return 0; +} + +/* locks held by regulator_enable() */ +static int _regulator_enable(struct regulator_dev *rdev) +{ + int ret; + + /* check voltage and requested load before enabling */ + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) + drms_uA_update(rdev); + + if (rdev->use_count == 0) { + /* The regulator may on if it's not switchable or left on */ + ret = _regulator_is_enabled(rdev); + if (ret == -EINVAL || ret == 0) { + if (!_regulator_can_change_status(rdev)) + return -EPERM; + + ret = _regulator_do_enable(rdev); + if (ret < 0) + return ret; + + } else if (ret < 0) { + rdev_err(rdev, "is_enabled() failed: %d\n", ret); + return ret; + } + /* Fallthrough on positive return values - already enabled */ + } + + rdev->use_count++; + + return 0; +} + +/** + * regulator_enable - enable regulator output + * @regulator: regulator source + * + * Request that the regulator be enabled with the regulator output at + * the predefined voltage or current value. Calls to regulator_enable() + * must be balanced with calls to regulator_disable(). + * + * NOTE: the output value can be set by other drivers, boot loader or may be + * hardwired in the regulator. + */ +int regulator_enable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; + + if (regulator->always_on) + return 0; + + if (rdev->supply) { + ret = regulator_enable(rdev->supply); + if (ret != 0) + return ret; + } + + mutex_lock(&rdev->mutex); + ret = _regulator_enable(rdev); + mutex_unlock(&rdev->mutex); + + if (ret != 0 && rdev->supply) + regulator_disable(rdev->supply); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_enable); + +static int _regulator_do_disable(struct regulator_dev *rdev) +{ + int ret; + + trace_regulator_disable(rdev_get_name(rdev)); + + if (rdev->ena_pin) { + ret = regulator_ena_gpio_ctrl(rdev, false); + if (ret < 0) + return ret; + rdev->ena_gpio_state = 0; + + } else if (rdev->desc->ops->disable) { + ret = rdev->desc->ops->disable(rdev); + if (ret != 0) + return ret; + } + + trace_regulator_disable_complete(rdev_get_name(rdev)); + + _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, + NULL); + return 0; +} + +/* locks held by regulator_disable() */ +static int _regulator_disable(struct regulator_dev *rdev) +{ + int ret = 0; + + if (WARN(rdev->use_count <= 0, + "unbalanced disables for %s\n", rdev_get_name(rdev))) + return -EIO; + + /* are we the last user and permitted to disable ? */ + if (rdev->use_count == 1 && + (rdev->constraints && !rdev->constraints->always_on)) { + + /* we are last user */ + if (_regulator_can_change_status(rdev)) { + ret = _regulator_do_disable(rdev); + if (ret < 0) { + rdev_err(rdev, "failed to disable\n"); + return ret; + } + } + + rdev->use_count = 0; + } else if (rdev->use_count > 1) { + + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & + REGULATOR_CHANGE_DRMS)) + drms_uA_update(rdev); + + rdev->use_count--; + } + + return ret; +} + +/** + * regulator_disable - disable regulator output + * @regulator: regulator source + * + * Disable the regulator output voltage or current. Calls to + * regulator_enable() must be balanced with calls to + * regulator_disable(). + * + * NOTE: this will only disable the regulator output if no other consumer + * devices have it enabled, the regulator device supports disabling and + * machine constraints permit this operation. + */ +int regulator_disable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; + + if (regulator->always_on) + return 0; + + mutex_lock(&rdev->mutex); + ret = _regulator_disable(rdev); + mutex_unlock(&rdev->mutex); + + if (ret == 0 && rdev->supply) + regulator_disable(rdev->supply); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_disable); + +/* locks held by regulator_force_disable() */ +static int _regulator_force_disable(struct regulator_dev *rdev) +{ + int ret = 0; + + /* force disable */ + if (rdev->desc->ops->disable) { + /* ah well, who wants to live forever... */ + ret = rdev->desc->ops->disable(rdev); + if (ret < 0) { + rdev_err(rdev, "failed to force disable\n"); + return ret; + } + /* notify other consumers that power has been forced off */ + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_DISABLE, NULL); + } + + return ret; +} + +/** + * regulator_force_disable - force disable regulator output + * @regulator: regulator source + * + * Forcibly disable the regulator output voltage or current. + * NOTE: this *will* disable the regulator output even if other consumer + * devices have it enabled. This should be used for situations when device + * damage will likely occur if the regulator is not disabled (e.g. over temp). + */ +int regulator_force_disable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + regulator->uA_load = 0; + ret = _regulator_force_disable(regulator->rdev); + mutex_unlock(&rdev->mutex); + + if (rdev->supply) + while (rdev->open_count--) + regulator_disable(rdev->supply); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_force_disable); + +static void regulator_disable_work(struct work_struct *work) +{ + struct regulator_dev *rdev = container_of(work, struct regulator_dev, + disable_work.work); + int count, i, ret; + + mutex_lock(&rdev->mutex); + + BUG_ON(!rdev->deferred_disables); + + count = rdev->deferred_disables; + rdev->deferred_disables = 0; + + for (i = 0; i < count; i++) { + ret = _regulator_disable(rdev); + if (ret != 0) + rdev_err(rdev, "Deferred disable failed: %d\n", ret); + } + + mutex_unlock(&rdev->mutex); + + if (rdev->supply) { + for (i = 0; i < count; i++) { + ret = regulator_disable(rdev->supply); + if (ret != 0) { + rdev_err(rdev, + "Supply disable failed: %d\n", ret); + } + } + } +} + +/** + * regulator_disable_deferred - disable regulator output with delay + * @regulator: regulator source + * @ms: miliseconds until the regulator is disabled + * + * Execute regulator_disable() on the regulator after a delay. This + * is intended for use with devices that require some time to quiesce. + * + * NOTE: this will only disable the regulator output if no other consumer + * devices have it enabled, the regulator device supports disabling and + * machine constraints permit this operation. + */ +int regulator_disable_deferred(struct regulator *regulator, int ms) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + if (regulator->always_on) + return 0; + + if (!ms) + return regulator_disable(regulator); + + mutex_lock(&rdev->mutex); + rdev->deferred_disables++; + mutex_unlock(&rdev->mutex); + + ret = schedule_delayed_work(&rdev->disable_work, + msecs_to_jiffies(ms)); + if (ret < 0) + return ret; + else + return 0; +} +EXPORT_SYMBOL_GPL(regulator_disable_deferred); + +/** + * regulator_is_enabled_regmap - standard is_enabled() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their is_enabled operation, saving some code. + */ +int regulator_is_enabled_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret != 0) + return ret; + + if (rdev->desc->enable_is_inverted) + return (val & rdev->desc->enable_mask) == 0; + else + return (val & rdev->desc->enable_mask) != 0; +} +EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); + +/** + * regulator_enable_regmap - standard enable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their enable() operation, saving some code. + */ +int regulator_enable_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + if (rdev->desc->enable_is_inverted) + val = 0; + else + val = rdev->desc->enable_mask; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_enable_regmap); + +/** + * regulator_disable_regmap - standard disable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their disable() operation, saving some code. + */ +int regulator_disable_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + if (rdev->desc->enable_is_inverted) + val = rdev->desc->enable_mask; + else + val = 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_disable_regmap); + +static int _regulator_is_enabled(struct regulator_dev *rdev) +{ + /* A GPIO control always takes precedence */ + if (rdev->ena_pin) + return rdev->ena_gpio_state; + + /* If we don't know then assume that the regulator is always on */ + if (!rdev->desc->ops->is_enabled) + return 1; + + return rdev->desc->ops->is_enabled(rdev); +} + +/** + * regulator_is_enabled - is the regulator output enabled + * @regulator: regulator source + * + * Returns positive if the regulator driver backing the source/client + * has requested that the device be enabled, zero if it hasn't, else a + * negative errno code. + * + * Note that the device backing this regulator handle can have multiple + * users, so it might be enabled even if regulator_enable() was never + * called for this particular source. + */ +int regulator_is_enabled(struct regulator *regulator) +{ + int ret; + + if (regulator->always_on) + return 1; + + mutex_lock(®ulator->rdev->mutex); + ret = _regulator_is_enabled(regulator->rdev); + mutex_unlock(®ulator->rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_is_enabled); + +/** + * regulator_can_change_voltage - check if regulator can change voltage + * @regulator: regulator source + * + * Returns positive if the regulator driver backing the source/client + * can change its voltage, false otherwise. Usefull for detecting fixed + * or dummy regulators and disabling voltage change logic in the client + * driver. + */ +int regulator_can_change_voltage(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (rdev->desc->n_voltages - rdev->desc->linear_min_sel > 1) + return 1; + + if (rdev->desc->continuous_voltage_range && + rdev->constraints->min_uV && rdev->constraints->max_uV && + rdev->constraints->min_uV != rdev->constraints->max_uV) + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_can_change_voltage); + +/** + * regulator_count_voltages - count regulator_list_voltage() selectors + * @regulator: regulator source + * + * Returns number of selectors, or negative errno. Selectors are + * numbered starting at zero, and typically correspond to bitfields + * in hardware registers. + */ +int regulator_count_voltages(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + return rdev->desc->n_voltages ? : -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_count_voltages); + +/** + * regulator_list_voltage_linear - List voltages with simple calculation + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with a simple linear mapping between voltages and + * selectors can set min_uV and uV_step in the regulator descriptor + * and then use this function as their list_voltage() operation, + */ +int regulator_list_voltage_linear(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; + + selector -= rdev->desc->linear_min_sel; + + return rdev->desc->min_uV + (rdev->desc->uV_step * selector); +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); + +/** + * regulator_list_voltage_table - List voltages with table based mapping + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with table based mapping between voltages and + * selectors can set volt_table in the regulator descriptor + * and then use this function as their list_voltage() operation. + */ +int regulator_list_voltage_table(struct regulator_dev *rdev, + unsigned int selector) +{ + if (!rdev->desc->volt_table) { + BUG_ON(!rdev->desc->volt_table); + return -EINVAL; + } + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + return rdev->desc->volt_table[selector]; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_table); + +/** + * regulator_list_voltage - enumerate supported voltages + * @regulator: regulator source + * @selector: identify voltage to list + * Context: can sleep + * + * Returns a voltage that can be passed to @regulator_set_voltage(), + * zero if this selector code can't be used on this system, or a + * negative errno. + */ +int regulator_list_voltage(struct regulator *regulator, unsigned selector) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator_ops *ops = rdev->desc->ops; + int ret; + + if (!ops->list_voltage || selector >= rdev->desc->n_voltages) + return -EINVAL; + + mutex_lock(&rdev->mutex); + ret = ops->list_voltage(rdev, selector); + mutex_unlock(&rdev->mutex); + + if (ret > 0) { + if (ret < rdev->constraints->min_uV) + ret = 0; + else if (ret > rdev->constraints->max_uV) + ret = 0; + } + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage); + +/** + * regulator_is_supported_voltage - check if a voltage range can be supported + * + * @regulator: Regulator to check. + * @min_uV: Minimum required voltage in uV. + * @max_uV: Maximum required voltage in uV. + * + * Returns a boolean or a negative error code. + */ +int regulator_is_supported_voltage(struct regulator *regulator, + int min_uV, int max_uV) +{ + struct regulator_dev *rdev = regulator->rdev; + int i, voltages, ret; + + /* If we can't change voltage check the current voltage */ + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + ret = regulator_get_voltage(regulator); + if (ret >= 0) + return (min_uV <= ret && ret <= max_uV); + else + return ret; + } + + /* Any voltage within constrains range is fine? */ + if (rdev->desc->continuous_voltage_range) + return min_uV >= rdev->constraints->min_uV && + max_uV <= rdev->constraints->max_uV; + + ret = regulator_count_voltages(regulator); + if (ret < 0) + return ret; + voltages = ret; + + for (i = 0; i < voltages; i++) { + ret = regulator_list_voltage(regulator, i); + + if (ret >= min_uV && ret <= max_uV) + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_is_supported_voltage); + +/** + * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their get_voltage_vsel operation, saving some code. + */ +int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} +EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap); + +/** + * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * @sel: Selector to set + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their set_voltage_vsel operation, saving some code. + */ +int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel) +{ + int ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, + rdev->desc->vsel_mask, sel); + if (ret) + return ret; + + if (rdev->desc->apply_bit) + ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg, + rdev->desc->apply_bit, + rdev->desc->apply_bit); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap); + +/** + * regulator_map_voltage_iterate - map_voltage() based on list_voltage() + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers implementing set_voltage_sel() and list_voltage() can use + * this as their map_voltage() operation. It will find a suitable + * voltage by calling list_voltage() until it gets something in bounds + * for the requested voltages. + */ +int regulator_map_voltage_iterate(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int best_val = INT_MAX; + int selector = 0; + int i, ret; + + /* Find the smallest voltage that falls within the specified + * range. + */ + for (i = 0; i < rdev->desc->n_voltages; i++) { + ret = rdev->desc->ops->list_voltage(rdev, i); + if (ret < 0) + continue; + + if (ret < best_val && ret >= min_uV && ret <= max_uV) { + best_val = ret; + selector = i; + } + } + + if (best_val != INT_MAX) + return selector; + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate); + +/** + * regulator_map_voltage_ascend - map_voltage() for ascendant voltage list + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers that have ascendant voltage list can use this as their + * map_voltage() operation. + */ +int regulator_map_voltage_ascend(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int i, ret; + + for (i = 0; i < rdev->desc->n_voltages; i++) { + ret = rdev->desc->ops->list_voltage(rdev, i); + if (ret < 0) + continue; + + if (ret > max_uV) + break; + + if (ret >= min_uV && ret <= max_uV) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_ascend); + +/** + * regulator_map_voltage_linear - map_voltage() for simple linear mappings + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers providing min_uV and uV_step in their regulator_desc can + * use this as their map_voltage() operation. + */ +int regulator_map_voltage_linear(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret, voltage; + + /* Allow uV_step to be 0 for fixed voltage */ + if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) { + if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV) + return 0; + else + return -EINVAL; + } + + if (!rdev->desc->uV_step) { + BUG_ON(!rdev->desc->uV_step); + return -EINVAL; + } + + if (min_uV < rdev->desc->min_uV) + min_uV = rdev->desc->min_uV; + + ret = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step); + if (ret < 0) + return ret; + + ret += rdev->desc->linear_min_sel; + + /* Map back into a voltage to verify we're still in bounds */ + voltage = rdev->desc->ops->list_voltage(rdev, ret); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_linear); + +static int _regulator_do_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int ret; + int delay = 0; + int best_val = 0; + unsigned int selector; + int old_selector = -1; + + trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); + + min_uV += rdev->constraints->uV_offset; + max_uV += rdev->constraints->uV_offset; + + /* + * If we can't obtain the old selector there is not enough + * info to call set_voltage_time_sel(). + */ + if (_regulator_is_enabled(rdev) && + rdev->desc->ops->set_voltage_time_sel && + rdev->desc->ops->get_voltage_sel) { + old_selector = rdev->desc->ops->get_voltage_sel(rdev); + if (old_selector < 0) + return old_selector; + } + + if (rdev->desc->ops->set_voltage) { + ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV, + &selector); + + if (ret >= 0) { + if (rdev->desc->ops->list_voltage) + best_val = rdev->desc->ops->list_voltage(rdev, + selector); + else + best_val = _regulator_get_voltage(rdev); + } + + } else if (rdev->desc->ops->set_voltage_sel) { + if (rdev->desc->ops->map_voltage) { + ret = rdev->desc->ops->map_voltage(rdev, min_uV, + max_uV); + } else { + if (rdev->desc->ops->list_voltage == + regulator_list_voltage_linear) + ret = regulator_map_voltage_linear(rdev, + min_uV, max_uV); + else + ret = regulator_map_voltage_iterate(rdev, + min_uV, max_uV); + } + + if (ret >= 0) { + best_val = rdev->desc->ops->list_voltage(rdev, ret); + if (min_uV <= best_val && max_uV >= best_val) { + selector = ret; + if (old_selector == selector) + ret = 0; + else + ret = rdev->desc->ops->set_voltage_sel( + rdev, ret); + } else { + ret = -EINVAL; + } + } + } else { + ret = -EINVAL; + } + + /* Call set_voltage_time_sel if successfully obtained old_selector */ + if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 && + old_selector != selector && rdev->desc->ops->set_voltage_time_sel) { + + delay = rdev->desc->ops->set_voltage_time_sel(rdev, + old_selector, selector); + if (delay < 0) { + rdev_warn(rdev, "set_voltage_time_sel() failed: %d\n", + delay); + delay = 0; + } + + /* Insert any necessary delays */ + if (delay >= 1000) { + mdelay(delay / 1000); + udelay(delay % 1000); + } else if (delay) { + udelay(delay); + } + } + + if (ret == 0 && best_val >= 0) { + unsigned long data = best_val; + + _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, + (void *)data); + } + + trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val); + + return ret; +} + +/** + * regulator_set_voltage - set regulator output voltage + * @regulator: regulator source + * @min_uV: Minimum required voltage in uV + * @max_uV: Maximum acceptable voltage in uV + * + * Sets a voltage regulator to the desired output voltage. This can be set + * during any regulator state. IOW, regulator can be disabled or enabled. + * + * If the regulator is enabled then the voltage will change to the new value + * immediately otherwise if the regulator is disabled the regulator will + * output at the new voltage when enabled. + * + * NOTE: If the regulator is shared between several devices then the lowest + * request voltage that meets the system constraints will be used. + * Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; + int old_min_uV, old_max_uV; + + mutex_lock(&rdev->mutex); + + /* If we're setting the same range as last time the change + * should be a noop (some cpufreq implementations use the same + * voltage for multiple frequencies, for example). + */ + if (regulator->min_uV == min_uV && regulator->max_uV == max_uV) + goto out; + + /* sanity check */ + if (!rdev->desc->ops->set_voltage && + !rdev->desc->ops->set_voltage_sel) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = regulator_check_voltage(rdev, &min_uV, &max_uV); + if (ret < 0) + goto out; + + /* restore original values in case of error */ + old_min_uV = regulator->min_uV; + old_max_uV = regulator->max_uV; + regulator->min_uV = min_uV; + regulator->max_uV = max_uV; + + ret = regulator_check_consumers(rdev, &min_uV, &max_uV); + if (ret < 0) + goto out2; + + ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); + if (ret < 0) + goto out2; + +out: + mutex_unlock(&rdev->mutex); + return ret; +out2: + regulator->min_uV = old_min_uV; + regulator->max_uV = old_max_uV; + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_voltage); + +/** + * regulator_set_voltage_time - get raise/fall time + * @regulator: regulator source + * @old_uV: starting voltage in microvolts + * @new_uV: target voltage in microvolts + * + * Provided with the starting and ending voltage, this function attempts to + * calculate the time in microseconds required to rise or fall to this new + * voltage. + */ +int regulator_set_voltage_time(struct regulator *regulator, + int old_uV, int new_uV) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator_ops *ops = rdev->desc->ops; + int old_sel = -1; + int new_sel = -1; + int voltage; + int i; + + /* Currently requires operations to do this */ + if (!ops->list_voltage || !ops->set_voltage_time_sel + || !rdev->desc->n_voltages) + return -EINVAL; + + for (i = 0; i < rdev->desc->n_voltages; i++) { + /* We only look for exact voltage matches here */ + voltage = regulator_list_voltage(regulator, i); + if (voltage < 0) + return -EINVAL; + if (voltage == 0) + continue; + if (voltage == old_uV) + old_sel = i; + if (voltage == new_uV) + new_sel = i; + } + + if (old_sel < 0 || new_sel < 0) + return -EINVAL; + + return ops->set_voltage_time_sel(rdev, old_sel, new_sel); +} +EXPORT_SYMBOL_GPL(regulator_set_voltage_time); + +/** + * regulator_set_voltage_time_sel - get raise/fall time + * @rdev: regulator source device + * @old_selector: selector for starting voltage + * @new_selector: selector for target voltage + * + * Provided with the starting and target voltage selectors, this function + * returns time in microseconds required to rise or fall to this new voltage + * + * Drivers providing ramp_delay in regulation_constraints can use this as their + * set_voltage_time_sel() operation. + */ +int regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + unsigned int ramp_delay = 0; + int old_volt, new_volt; + + if (rdev->constraints->ramp_delay) + ramp_delay = rdev->constraints->ramp_delay; + else if (rdev->desc->ramp_delay) + ramp_delay = rdev->desc->ramp_delay; + + if (ramp_delay == 0) { + rdev_warn(rdev, "ramp_delay not set\n"); + return 0; + } + + /* sanity check */ + if (!rdev->desc->ops->list_voltage) + return -EINVAL; + + old_volt = rdev->desc->ops->list_voltage(rdev, old_selector); + new_volt = rdev->desc->ops->list_voltage(rdev, new_selector); + + return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); +} +EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel); + +/** + * regulator_sync_voltage - re-apply last regulator output voltage + * @regulator: regulator source + * + * Re-apply the last configured voltage. This is intended to be used + * where some external control source the consumer is cooperating with + * has caused the configured voltage to change. + */ +int regulator_sync_voltage(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret, min_uV, max_uV; + + mutex_lock(&rdev->mutex); + + if (!rdev->desc->ops->set_voltage && + !rdev->desc->ops->set_voltage_sel) { + ret = -EINVAL; + goto out; + } + + /* This is only going to work if we've had a voltage configured. */ + if (!regulator->min_uV && !regulator->max_uV) { + ret = -EINVAL; + goto out; + } + + min_uV = regulator->min_uV; + max_uV = regulator->max_uV; + + /* This should be a paranoia check... */ + ret = regulator_check_voltage(rdev, &min_uV, &max_uV); + if (ret < 0) + goto out; + + ret = regulator_check_consumers(rdev, &min_uV, &max_uV); + if (ret < 0) + goto out; + + ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); + +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_sync_voltage); + +static int _regulator_get_voltage(struct regulator_dev *rdev) +{ + int sel, ret; + + if (rdev->desc->ops->get_voltage_sel) { + sel = rdev->desc->ops->get_voltage_sel(rdev); + if (sel < 0) + return sel; + ret = rdev->desc->ops->list_voltage(rdev, sel); + } else if (rdev->desc->ops->get_voltage) { + ret = rdev->desc->ops->get_voltage(rdev); + } else if (rdev->desc->ops->list_voltage) { + ret = rdev->desc->ops->list_voltage(rdev, 0); + } else { + return -EINVAL; + } + + if (ret < 0) + return ret; + return ret - rdev->constraints->uV_offset; +} + +/** + * regulator_get_voltage - get regulator output voltage + * @regulator: regulator source + * + * This returns the current regulator voltage in uV. + * + * NOTE: If the regulator is disabled it will return the voltage value. This + * function should not be used to determine regulator state. + */ +int regulator_get_voltage(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->rdev->mutex); + + ret = _regulator_get_voltage(regulator->rdev); + + mutex_unlock(®ulator->rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_get_voltage); + +/** + * regulator_set_current_limit - set regulator output current limit + * @regulator: regulator source + * @min_uA: Minimum supported current in uA + * @max_uA: Maximum supported current in uA + * + * Sets current sink to the desired output current. This can be set during + * any regulator state. IOW, regulator can be disabled or enabled. + * + * If the regulator is enabled then the current will change to the new value + * immediately otherwise if the regulator is disabled the regulator will + * output at the new current when enabled. + * + * NOTE: Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->set_current_limit) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = regulator_check_current_limit(rdev, &min_uA, &max_uA); + if (ret < 0) + goto out; + + ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA); +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_current_limit); + +static int _regulator_get_current_limit(struct regulator_dev *rdev) +{ + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->get_current_limit) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->get_current_limit(rdev); +out: + mutex_unlock(&rdev->mutex); + return ret; +} + +/** + * regulator_get_current_limit - get regulator output current + * @regulator: regulator source + * + * This returns the current supplied by the specified current sink in uA. + * + * NOTE: If the regulator is disabled it will return the current value. This + * function should not be used to determine regulator state. + */ +int regulator_get_current_limit(struct regulator *regulator) +{ + return _regulator_get_current_limit(regulator->rdev); +} +EXPORT_SYMBOL_GPL(regulator_get_current_limit); + +/** + * regulator_set_mode - set regulator operating mode + * @regulator: regulator source + * @mode: operating mode - one of the REGULATOR_MODE constants + * + * Set regulator operating mode to increase regulator efficiency or improve + * regulation performance. + * + * NOTE: Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_mode(struct regulator *regulator, unsigned int mode) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + int regulator_curr_mode; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->set_mode) { + ret = -EINVAL; + goto out; + } + + /* return if the same mode is requested */ + if (rdev->desc->ops->get_mode) { + regulator_curr_mode = rdev->desc->ops->get_mode(rdev); + if (regulator_curr_mode == mode) { + ret = 0; + goto out; + } + } + + /* constraints check */ + ret = regulator_mode_constrain(rdev, &mode); + if (ret < 0) + goto out; + + ret = rdev->desc->ops->set_mode(rdev, mode); +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_mode); + +static unsigned int _regulator_get_mode(struct regulator_dev *rdev) +{ + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->get_mode) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->get_mode(rdev); +out: + mutex_unlock(&rdev->mutex); + return ret; +} + +/** + * regulator_get_mode - get regulator operating mode + * @regulator: regulator source + * + * Get the current regulator operating mode. + */ +unsigned int regulator_get_mode(struct regulator *regulator) +{ + return _regulator_get_mode(regulator->rdev); +} +EXPORT_SYMBOL_GPL(regulator_get_mode); + +/** + * regulator_set_optimum_mode - set regulator optimum operating mode + * @regulator: regulator source + * @uA_load: load current + * + * Notifies the regulator core of a new device load. This is then used by + * DRMS (if enabled by constraints) to set the most efficient regulator + * operating mode for the new regulator loading. + * + * Consumer devices notify their supply regulator of the maximum power + * they will require (can be taken from device datasheet in the power + * consumption tables) when they change operational status and hence power + * state. Examples of operational state changes that can affect power + * consumption are :- + * + * o Device is opened / closed. + * o Device I/O is about to begin or has just finished. + * o Device is idling in between work. + * + * This information is also exported via sysfs to userspace. + * + * DRMS will sum the total requested load on the regulator and change + * to the most efficient operating mode if platform constraints allow. + * + * Returns the new regulator mode or error. + */ +int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator *consumer; + int ret, output_uV, input_uV = 0, total_uA_load = 0; + unsigned int mode; + + if (rdev->supply) + input_uV = regulator_get_voltage(rdev->supply); + + mutex_lock(&rdev->mutex); + + /* + * first check to see if we can set modes at all, otherwise just + * tell the consumer everything is OK. + */ + regulator->uA_load = uA_load; + ret = regulator_check_drms(rdev); + if (ret < 0) { + ret = 0; + goto out; + } + + if (!rdev->desc->ops->get_optimum_mode) + goto out; + + /* + * we can actually do this so any errors are indicators of + * potential real failure. + */ + ret = -EINVAL; + + if (!rdev->desc->ops->set_mode) + goto out; + + /* get output voltage */ + output_uV = _regulator_get_voltage(rdev); + if (output_uV <= 0) { + rdev_err(rdev, "invalid output voltage found\n"); + goto out; + } + + /* No supply? Use constraint voltage */ + if (input_uV <= 0) + input_uV = rdev->constraints->input_uV; + if (input_uV <= 0) { + rdev_err(rdev, "invalid input voltage found\n"); + goto out; + } + + /* calc total requested load for this regulator */ + list_for_each_entry(consumer, &rdev->consumer_list, list) + total_uA_load += consumer->uA_load; + + mode = rdev->desc->ops->get_optimum_mode(rdev, + input_uV, output_uV, + total_uA_load); + ret = regulator_mode_constrain(rdev, &mode); + if (ret < 0) { + rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", + total_uA_load, input_uV, output_uV); + goto out; + } + + ret = rdev->desc->ops->set_mode(rdev, mode); + if (ret < 0) { + rdev_err(rdev, "failed to set optimum mode %x\n", mode); + goto out; + } + ret = mode; +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); + +/** + * regulator_set_bypass_regmap - Default set_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: state to set. + */ +int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) +{ + unsigned int val; + + if (enable) + val = rdev->desc->bypass_mask; + else + val = 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg, + rdev->desc->bypass_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); + +/** + * regulator_get_bypass_regmap - Default get_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: current state. + */ +int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val); + if (ret != 0) + return ret; + + *enable = val & rdev->desc->bypass_mask; + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap); + +/** + * regulator_allow_bypass - allow the regulator to go into bypass mode + * + * @regulator: Regulator to configure + * @enable: enable or disable bypass mode + * + * Allow the regulator to go into bypass mode if all other consumers + * for the regulator also enable bypass mode and the machine + * constraints allow this. Bypass mode means that the regulator is + * simply passing the input directly to the output with no regulation. + */ +int regulator_allow_bypass(struct regulator *regulator, bool enable) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; + + if (!rdev->desc->ops->set_bypass) + return 0; + + if (rdev->constraints && + !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS)) + return 0; + + mutex_lock(&rdev->mutex); + + if (enable && !regulator->bypass) { + rdev->bypass_count++; + + if (rdev->bypass_count == rdev->open_count) { + ret = rdev->desc->ops->set_bypass(rdev, enable); + if (ret != 0) + rdev->bypass_count--; + } + + } else if (!enable && regulator->bypass) { + rdev->bypass_count--; + + if (rdev->bypass_count != rdev->open_count) { + ret = rdev->desc->ops->set_bypass(rdev, enable); + if (ret != 0) + rdev->bypass_count++; + } + } + + if (ret == 0) + regulator->bypass = enable; + + mutex_unlock(&rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_allow_bypass); + +/** + * regulator_register_notifier - register regulator event notifier + * @regulator: regulator source + * @nb: notifier block + * + * Register notifier block to receive regulator events. + */ +int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(®ulator->rdev->notifier, + nb); +} +EXPORT_SYMBOL_GPL(regulator_register_notifier); + +/** + * regulator_unregister_notifier - unregister regulator event notifier + * @regulator: regulator source + * @nb: notifier block + * + * Unregister regulator event notifier block. + */ +int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(®ulator->rdev->notifier, + nb); +} +EXPORT_SYMBOL_GPL(regulator_unregister_notifier); + +/* notify regulator consumers and downstream regulator consumers. + * Note mutex must be held by caller. + */ +static void _notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data) +{ + /* call rdev chain first */ + blocking_notifier_call_chain(&rdev->notifier, event, data); +} + +/** + * regulator_bulk_get - get multiple regulator consumers + * + * @dev: Device to supply + * @num_consumers: Number of consumers to register + * @consumers: Configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation. If any of the regulators cannot be + * acquired then any regulators that were allocated will be freed + * before returning to the caller. + */ +int regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; + + for (i = 0; i < num_consumers; i++) { + consumers[i].consumer = regulator_get(dev, + consumers[i].supply); + if (IS_ERR(consumers[i].consumer)) { + ret = PTR_ERR(consumers[i].consumer); + dev_err(dev, "Failed to get supply '%s': %d\n", + consumers[i].supply, ret); + consumers[i].consumer = NULL; + goto err; + } + } + + return 0; + +err: + while (--i >= 0) + regulator_put(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_get); + +/** + * devm_regulator_bulk_get - managed get multiple regulator consumers + * + * @dev: Device to supply + * @num_consumers: Number of consumers to register + * @consumers: Configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation with management, the regulators will + * automatically be freed when the device is unbound. If any of the + * regulators cannot be acquired then any regulators that were + * allocated will be freed before returning to the caller. + */ +int devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; + + for (i = 0; i < num_consumers; i++) { + consumers[i].consumer = devm_regulator_get(dev, + consumers[i].supply); + if (IS_ERR(consumers[i].consumer)) { + ret = PTR_ERR(consumers[i].consumer); + dev_err(dev, "Failed to get supply '%s': %d\n", + consumers[i].supply, ret); + consumers[i].consumer = NULL; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < num_consumers && consumers[i].consumer; i++) + devm_regulator_put(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); + +static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) +{ + struct regulator_bulk_data *bulk = data; + + bulk->ret = regulator_enable(bulk->consumer); +} + +/** + * regulator_bulk_enable - enable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to enable multiple regulator + * clients in a single API call. If any consumers cannot be enabled + * then any others that were enabled will be disabled again prior to + * return. + */ +int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + ASYNC_DOMAIN_EXCLUSIVE(async_domain); + int i; + int ret = 0; + + for (i = 0; i < num_consumers; i++) { + if (consumers[i].consumer->always_on) + consumers[i].ret = 0; + else + async_schedule_domain(regulator_bulk_enable_async, + &consumers[i], &async_domain); + } + + async_synchronize_full_domain(&async_domain); + + /* If any consumer failed we need to unwind any that succeeded */ + for (i = 0; i < num_consumers; i++) { + if (consumers[i].ret != 0) { + ret = consumers[i].ret; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < num_consumers; i++) { + if (consumers[i].ret < 0) + pr_err("Failed to enable %s: %d\n", consumers[i].supply, + consumers[i].ret); + else + regulator_disable(consumers[i].consumer); + } + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_enable); + +/** + * regulator_bulk_disable - disable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to disable multiple regulator + * clients in a single API call. If any consumers cannot be disabled + * then any others that were disabled will be enabled again prior to + * return. + */ +int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret, r; + + for (i = num_consumers - 1; i >= 0; --i) { + ret = regulator_disable(consumers[i].consumer); + if (ret != 0) + goto err; + } + + return 0; + +err: + pr_err("Failed to disable %s: %d\n", consumers[i].supply, ret); + for (++i; i < num_consumers; ++i) { + r = regulator_enable(consumers[i].consumer); + if (r != 0) + pr_err("Failed to reename %s: %d\n", + consumers[i].supply, r); + } + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_disable); + +/** + * regulator_bulk_force_disable - force disable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to forcibly disable multiple regulator + * clients in a single API call. + * NOTE: This should be used for situations when device damage will + * likely occur if the regulators are not disabled (e.g. over temp). + * Although regulator_force_disable function call for some consumers can + * return error numbers, the function is called for all consumers. + */ +int regulator_bulk_force_disable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].ret = + regulator_force_disable(consumers[i].consumer); + + for (i = 0; i < num_consumers; i++) { + if (consumers[i].ret != 0) { + ret = consumers[i].ret; + goto out; + } + } + + return 0; +out: + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_force_disable); + +/** + * regulator_bulk_free - free multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * + * This convenience API allows consumers to free multiple regulator + * clients in a single API call. + */ +void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + + for (i = 0; i < num_consumers; i++) { + regulator_put(consumers[i].consumer); + consumers[i].consumer = NULL; + } +} +EXPORT_SYMBOL_GPL(regulator_bulk_free); + +/** + * regulator_notifier_call_chain - call regulator event notifier + * @rdev: regulator source + * @event: notifier block + * @data: callback-specific data. + * + * Called by regulator drivers to notify clients a regulator event has + * occurred. We also notify regulator clients downstream. + * Note lock must be held by caller. + */ +int regulator_notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data) +{ + _notifier_call_chain(rdev, event, data); + return NOTIFY_DONE; + +} +EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); + +/** + * regulator_mode_to_status - convert a regulator mode into a status + * + * @mode: Mode to convert + * + * Convert a regulator mode into a status. + */ +int regulator_mode_to_status(unsigned int mode) +{ + switch (mode) { + case REGULATOR_MODE_FAST: + return REGULATOR_STATUS_FAST; + case REGULATOR_MODE_NORMAL: + return REGULATOR_STATUS_NORMAL; + case REGULATOR_MODE_IDLE: + return REGULATOR_STATUS_IDLE; + case REGULATOR_MODE_STANDBY: + return REGULATOR_STATUS_STANDBY; + default: + return REGULATOR_STATUS_UNDEFINED; + } +} +EXPORT_SYMBOL_GPL(regulator_mode_to_status); + +/* + * To avoid cluttering sysfs (and memory) with useless state, only + * create attributes that can be meaningfully displayed. + */ +static int add_regulator_attributes(struct regulator_dev *rdev) +{ + struct device *dev = &rdev->dev; + struct regulator_ops *ops = rdev->desc->ops; + int status = 0; + + /* some attributes need specific methods to be displayed */ + if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) || + (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) || + (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0)) { + status = device_create_file(dev, &dev_attr_microvolts); + if (status < 0) + return status; + } + if (ops->get_current_limit) { + status = device_create_file(dev, &dev_attr_microamps); + if (status < 0) + return status; + } + if (ops->get_mode) { + status = device_create_file(dev, &dev_attr_opmode); + if (status < 0) + return status; + } + if (rdev->ena_pin || ops->is_enabled) { + status = device_create_file(dev, &dev_attr_state); + if (status < 0) + return status; + } + if (ops->get_status) { + status = device_create_file(dev, &dev_attr_status); + if (status < 0) + return status; + } + if (ops->get_bypass) { + status = device_create_file(dev, &dev_attr_bypass); + if (status < 0) + return status; + } + + /* some attributes are type-specific */ + if (rdev->desc->type == REGULATOR_CURRENT) { + status = device_create_file(dev, &dev_attr_requested_microamps); + if (status < 0) + return status; + } + + /* all the other attributes exist to support constraints; + * don't show them if there are no constraints, or if the + * relevant supporting methods are missing. + */ + if (!rdev->constraints) + return status; + + /* constraints need specific supporting methods */ + if (ops->set_voltage || ops->set_voltage_sel) { + status = device_create_file(dev, &dev_attr_min_microvolts); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_max_microvolts); + if (status < 0) + return status; + } + if (ops->set_current_limit) { + status = device_create_file(dev, &dev_attr_min_microamps); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_max_microamps); + if (status < 0) + return status; + } + + status = device_create_file(dev, &dev_attr_suspend_standby_state); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_suspend_mem_state); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_suspend_disk_state); + if (status < 0) + return status; + + if (ops->set_suspend_voltage) { + status = device_create_file(dev, + &dev_attr_suspend_standby_microvolts); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_mem_microvolts); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_disk_microvolts); + if (status < 0) + return status; + } + + if (ops->set_suspend_mode) { + status = device_create_file(dev, + &dev_attr_suspend_standby_mode); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_mem_mode); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_disk_mode); + if (status < 0) + return status; + } + + return status; +} + +static void rdev_init_debugfs(struct regulator_dev *rdev) +{ + rdev->debugfs = debugfs_create_dir(rdev_get_name(rdev), debugfs_root); + if (!rdev->debugfs) { + rdev_warn(rdev, "Failed to create debugfs directory\n"); + return; + } + + debugfs_create_u32("use_count", 0444, rdev->debugfs, + &rdev->use_count); + debugfs_create_u32("open_count", 0444, rdev->debugfs, + &rdev->open_count); + debugfs_create_u32("bypass_count", 0444, rdev->debugfs, + &rdev->bypass_count); +} + +/** + * regulator_register - register regulator + * @regulator_desc: regulator to register + * @config: runtime configuration for regulator + * + * Called by regulator drivers to register a regulator. + * Returns a valid pointer to struct regulator_dev on success + * or an ERR_PTR() on error. + */ +struct regulator_dev * +regulator_register(const struct regulator_desc *regulator_desc, + const struct regulator_config *config) +{ + const struct regulation_constraints *constraints = NULL; + const struct regulator_init_data *init_data; + static atomic_t regulator_no = ATOMIC_INIT(0); + struct regulator_dev *rdev; + struct device *dev; + int ret, i; + const char *supply = NULL; + + if (regulator_desc == NULL || config == NULL) + return ERR_PTR(-EINVAL); + + dev = config->dev; + WARN_ON(!dev); + + if (regulator_desc->name == NULL || regulator_desc->ops == NULL) + return ERR_PTR(-EINVAL); + + if (regulator_desc->type != REGULATOR_VOLTAGE && + regulator_desc->type != REGULATOR_CURRENT) + return ERR_PTR(-EINVAL); + + /* Only one of each should be implemented */ + WARN_ON(regulator_desc->ops->get_voltage && + regulator_desc->ops->get_voltage_sel); + WARN_ON(regulator_desc->ops->set_voltage && + regulator_desc->ops->set_voltage_sel); + + /* If we're using selectors we must implement list_voltage. */ + if (regulator_desc->ops->get_voltage_sel && + !regulator_desc->ops->list_voltage) { + return ERR_PTR(-EINVAL); + } + if (regulator_desc->ops->set_voltage_sel && + !regulator_desc->ops->list_voltage) { + return ERR_PTR(-EINVAL); + } + + init_data = config->init_data; + + rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); + if (rdev == NULL) + return ERR_PTR(-ENOMEM); + + mutex_lock(®ulator_list_mutex); + + mutex_init(&rdev->mutex); + rdev->reg_data = config->driver_data; + rdev->owner = regulator_desc->owner; + rdev->desc = regulator_desc; + if (config->regmap) + rdev->regmap = config->regmap; + else if (dev_get_regmap(dev, NULL)) + rdev->regmap = dev_get_regmap(dev, NULL); + else if (dev->parent) + rdev->regmap = dev_get_regmap(dev->parent, NULL); + INIT_LIST_HEAD(&rdev->consumer_list); + INIT_LIST_HEAD(&rdev->list); + BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); + INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work); + + /* preform any regulator specific init */ + if (init_data && init_data->regulator_init) { + ret = init_data->regulator_init(rdev->reg_data); + if (ret < 0) + goto clean; + } + + /* register with sysfs */ + rdev->dev.class = ®ulator_class; + rdev->dev.of_node = config->of_node; + rdev->dev.parent = dev; + dev_set_name(&rdev->dev, "regulator.%d", + atomic_inc_return(®ulator_no) - 1); + ret = device_register(&rdev->dev); + if (ret != 0) { + put_device(&rdev->dev); + goto clean; + } + + dev_set_drvdata(&rdev->dev, rdev); + + if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { + ret = regulator_ena_gpio_request(rdev, config); + if (ret != 0) { + rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", + config->ena_gpio, ret); + goto wash; + } + + if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) + rdev->ena_gpio_state = 1; + + if (config->ena_gpio_invert) + rdev->ena_gpio_state = !rdev->ena_gpio_state; + } + + /* set regulator constraints */ + if (init_data) + constraints = &init_data->constraints; + + ret = set_machine_constraints(rdev, constraints); + if (ret < 0) + goto scrub; + + /* add attributes supported by this regulator */ + ret = add_regulator_attributes(rdev); + if (ret < 0) + goto scrub; + + if (init_data && init_data->supply_regulator) + supply = init_data->supply_regulator; + else if (regulator_desc->supply_name) + supply = regulator_desc->supply_name; + + if (supply) { + struct regulator_dev *r; + + r = regulator_dev_lookup(dev, supply, &ret); + + if (ret == -ENODEV) { + /* + * No supply was specified for this regulator and + * there will never be one. + */ + ret = 0; + goto add_dev; + } else if (!r) { + dev_err(dev, "Failed to find supply %s\n", supply); + ret = -EPROBE_DEFER; + goto scrub; + } + + ret = set_supply(rdev, r); + if (ret < 0) + goto scrub; + + /* Enable supply if rail is enabled */ + if (_regulator_is_enabled(rdev)) { + ret = regulator_enable(rdev->supply); + if (ret < 0) + goto scrub; + } + } + +add_dev: + /* add consumers devices */ + if (init_data) { + for (i = 0; i < init_data->num_consumer_supplies; i++) { + ret = set_consumer_device_supply(rdev, + init_data->consumer_supplies[i].dev_name, + init_data->consumer_supplies[i].supply); + if (ret < 0) { + dev_err(dev, "Failed to set supply %s\n", + init_data->consumer_supplies[i].supply); + goto unset_supplies; + } + } + } + + list_add(&rdev->list, ®ulator_list); + + rdev_init_debugfs(rdev); +out: + mutex_unlock(®ulator_list_mutex); + return rdev; + +unset_supplies: + unset_regulator_supplies(rdev); + +scrub: + if (rdev->supply) + _regulator_put(rdev->supply); + regulator_ena_gpio_free(rdev); + kfree(rdev->constraints); +wash: + device_unregister(&rdev->dev); + /* device core frees rdev */ + rdev = ERR_PTR(ret); + goto out; + +clean: + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; +} +EXPORT_SYMBOL_GPL(regulator_register); + +/** + * regulator_unregister - unregister regulator + * @rdev: regulator to unregister + * + * Called by regulator drivers to unregister a regulator. + */ +void regulator_unregister(struct regulator_dev *rdev) +{ + if (rdev == NULL) + return; + + if (rdev->supply) + regulator_put(rdev->supply); + mutex_lock(®ulator_list_mutex); + debugfs_remove_recursive(rdev->debugfs); + flush_work(&rdev->disable_work.work); + WARN_ON(rdev->open_count); + unset_regulator_supplies(rdev); + list_del(&rdev->list); + kfree(rdev->constraints); + regulator_ena_gpio_free(rdev); + device_unregister(&rdev->dev); + mutex_unlock(®ulator_list_mutex); +} +EXPORT_SYMBOL_GPL(regulator_unregister); + +/** + * regulator_suspend_prepare - prepare regulators for system wide suspend + * @state: system suspend state + * + * Configure each regulator with it's suspend operating parameters for state. + * This will usually be called by machine suspend code prior to supending. + */ +int regulator_suspend_prepare(suspend_state_t state) +{ + struct regulator_dev *rdev; + int ret = 0; + + /* ON is handled by regulator active state */ + if (state == PM_SUSPEND_ON) + return -EINVAL; + + mutex_lock(®ulator_list_mutex); + list_for_each_entry(rdev, ®ulator_list, list) { + + mutex_lock(&rdev->mutex); + ret = suspend_prepare(rdev, state); + mutex_unlock(&rdev->mutex); + + if (ret < 0) { + rdev_err(rdev, "failed to prepare\n"); + goto out; + } + } +out: + mutex_unlock(®ulator_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_suspend_prepare); + +/** + * regulator_suspend_finish - resume regulators from system wide suspend + * + * Turn on regulators that might be turned off by regulator_suspend_prepare + * and that should be turned on according to the regulators properties. + */ +int regulator_suspend_finish(void) +{ + struct regulator_dev *rdev; + int ret = 0, error; + + mutex_lock(®ulator_list_mutex); + list_for_each_entry(rdev, ®ulator_list, list) { + struct regulator_ops *ops = rdev->desc->ops; + + mutex_lock(&rdev->mutex); + if ((rdev->use_count > 0 || rdev->constraints->always_on) && + ops->enable) { + error = ops->enable(rdev); + if (error) + ret = error; + } else { + if (!has_full_constraints) + goto unlock; + if (!ops->disable) + goto unlock; + if (!_regulator_is_enabled(rdev)) + goto unlock; + + error = ops->disable(rdev); + if (error) + ret = error; + } +unlock: + mutex_unlock(&rdev->mutex); + } + mutex_unlock(®ulator_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_suspend_finish); + +/** + * regulator_has_full_constraints - the system has fully specified constraints + * + * Calling this function will cause the regulator API to disable all + * regulators which have a zero use count and don't have an always_on + * constraint in a late_initcall. + * + * The intention is that this will become the default behaviour in a + * future kernel release so users are encouraged to use this facility + * now. + */ +void regulator_has_full_constraints(void) +{ + has_full_constraints = 1; +} +EXPORT_SYMBOL_GPL(regulator_has_full_constraints); + +/** + * regulator_use_dummy_regulator - Provide a dummy regulator when none is found + * + * Calling this function will cause the regulator API to provide a + * dummy regulator to consumers if no physical regulator is found, + * allowing most consumers to proceed as though a regulator were + * configured. This allows systems such as those with software + * controllable regulators for the CPU core only to be brought up more + * readily. + */ +void regulator_use_dummy_regulator(void) +{ + board_wants_dummy_regulator = true; +} +EXPORT_SYMBOL_GPL(regulator_use_dummy_regulator); + +/** + * rdev_get_drvdata - get rdev regulator driver data + * @rdev: regulator + * + * Get rdev regulator driver private data. This call can be used in the + * regulator driver context. + */ +void *rdev_get_drvdata(struct regulator_dev *rdev) +{ + return rdev->reg_data; +} +EXPORT_SYMBOL_GPL(rdev_get_drvdata); + +/** + * regulator_get_drvdata - get regulator driver data + * @regulator: regulator + * + * Get regulator driver private data. This call can be used in the consumer + * driver context when non API regulator specific functions need to be called. + */ +void *regulator_get_drvdata(struct regulator *regulator) +{ + return regulator->rdev->reg_data; +} +EXPORT_SYMBOL_GPL(regulator_get_drvdata); + +/** + * regulator_set_drvdata - set regulator driver data + * @regulator: regulator + * @data: data + */ +void regulator_set_drvdata(struct regulator *regulator, void *data) +{ + regulator->rdev->reg_data = data; +} +EXPORT_SYMBOL_GPL(regulator_set_drvdata); + +/** + * regulator_get_id - get regulator ID + * @rdev: regulator + */ +int rdev_get_id(struct regulator_dev *rdev) +{ + return rdev->desc->id; +} +EXPORT_SYMBOL_GPL(rdev_get_id); + +struct device *rdev_get_dev(struct regulator_dev *rdev) +{ + return &rdev->dev; +} +EXPORT_SYMBOL_GPL(rdev_get_dev); + +void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data) +{ + return reg_init_data->driver_data; +} +EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); + +#ifdef CONFIG_DEBUG_FS +static ssize_t supply_map_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + ssize_t len, ret = 0; + struct regulator_map *map; + + if (!buf) + return -ENOMEM; + + list_for_each_entry(map, ®ulator_map_list, list) { + len = snprintf(buf + ret, PAGE_SIZE - ret, + "%s -> %s.%s\n", + rdev_get_name(map->regulator), map->dev_name, + map->supply); + if (len >= 0) + ret += len; + if (ret > PAGE_SIZE) { + ret = PAGE_SIZE; + break; + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} +#endif + +static const struct file_operations supply_map_fops = { +#ifdef CONFIG_DEBUG_FS + .read = supply_map_read_file, + .llseek = default_llseek, +#endif +}; + +static int __init regulator_init(void) +{ + int ret; + + ret = class_register(®ulator_class); + + debugfs_root = debugfs_create_dir("regulator", NULL); + if (!debugfs_root) + pr_warn("regulator: Failed to create debugfs directory\n"); + + debugfs_create_file("supply_map", 0444, debugfs_root, NULL, + &supply_map_fops); + + regulator_dummy_init(); + + return ret; +} + +/* init early to allow our consumers to complete system booting */ +core_initcall(regulator_init); + +static int __init regulator_init_complete(void) +{ + struct regulator_dev *rdev; + struct regulator_ops *ops; + struct regulation_constraints *c; + int enabled, ret; + + /* + * Since DT doesn't provide an idiomatic mechanism for + * enabling full constraints and since it's much more natural + * with DT to provide them just assume that a DT enabled + * system has full constraints. + */ + if (of_have_populated_dt()) + has_full_constraints = true; + + mutex_lock(®ulator_list_mutex); + + /* If we have a full configuration then disable any regulators + * which are not in use or always_on. This will become the + * default behaviour in the future. + */ + list_for_each_entry(rdev, ®ulator_list, list) { + ops = rdev->desc->ops; + c = rdev->constraints; + + if (!ops->disable || (c && c->always_on)) + continue; + + mutex_lock(&rdev->mutex); + + if (rdev->use_count) + goto unlock; + + /* If we can't read the status assume it's on. */ + if (ops->is_enabled) + enabled = ops->is_enabled(rdev); + else + enabled = 1; + + if (!enabled) + goto unlock; + + if (has_full_constraints) { + /* We log since this may kill the system if it + * goes wrong. */ + rdev_info(rdev, "disabling\n"); + ret = ops->disable(rdev); + if (ret != 0) { + rdev_err(rdev, "couldn't disable: %d\n", ret); + } + } else { + /* The intention is that in future we will + * assume that full constraints are provided + * so warn even if we aren't going to do + * anything here. + */ + rdev_warn(rdev, "incomplete constraints, leaving on\n"); + } + +unlock: + mutex_unlock(&rdev->mutex); + } + + mutex_unlock(®ulator_list_mutex); + + return 0; +} +late_initcall(regulator_init_complete); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c new file mode 100644 index 0000000..2afa573 --- /dev/null +++ b/drivers/regulator/da903x.c @@ -0,0 +1,535 @@ +/* + * Regulators driver for Dialog Semiconductor DA903x + * + * Copyright (C) 2006-2008 Marvell International Ltd. + * Copyright (C) 2008 Compulab Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* DA9030 Registers */ +#define DA9030_INVAL (-1) +#define DA9030_LDO1011 (0x10) +#define DA9030_LDO15 (0x11) +#define DA9030_LDO1416 (0x12) +#define DA9030_LDO1819 (0x13) +#define DA9030_LDO17 (0x14) +#define DA9030_BUCK2DVM1 (0x15) +#define DA9030_BUCK2DVM2 (0x16) +#define DA9030_RCTL11 (0x17) +#define DA9030_RCTL21 (0x18) +#define DA9030_LDO1 (0x90) +#define DA9030_LDO23 (0x91) +#define DA9030_LDO45 (0x92) +#define DA9030_LDO6 (0x93) +#define DA9030_LDO78 (0x94) +#define DA9030_LDO912 (0x95) +#define DA9030_BUCK (0x96) +#define DA9030_RCTL12 (0x97) +#define DA9030_RCTL22 (0x98) +#define DA9030_LDO_UNLOCK (0xa0) +#define DA9030_LDO_UNLOCK_MASK (0xe0) +#define DA9034_OVER1 (0x10) + +/* DA9034 Registers */ +#define DA9034_INVAL (-1) +#define DA9034_OVER2 (0x11) +#define DA9034_OVER3 (0x12) +#define DA9034_LDO643 (0x13) +#define DA9034_LDO987 (0x14) +#define DA9034_LDO1110 (0x15) +#define DA9034_LDO1312 (0x16) +#define DA9034_LDO1514 (0x17) +#define DA9034_VCC1 (0x20) +#define DA9034_ADTV1 (0x23) +#define DA9034_ADTV2 (0x24) +#define DA9034_AVRC (0x25) +#define DA9034_CDTV1 (0x26) +#define DA9034_CDTV2 (0x27) +#define DA9034_CVRC (0x28) +#define DA9034_SDTV1 (0x29) +#define DA9034_SDTV2 (0x2a) +#define DA9034_SVRC (0x2b) +#define DA9034_MDTV1 (0x32) +#define DA9034_MDTV2 (0x33) +#define DA9034_MVRC (0x34) + +/* DA9035 Registers. DA9034 Registers are comptabile to DA9035. */ +#define DA9035_OVER3 (0x12) +#define DA9035_VCC2 (0x1f) +#define DA9035_3DTV1 (0x2c) +#define DA9035_3DTV2 (0x2d) +#define DA9035_3VRC (0x2e) +#define DA9035_AUTOSKIP (0x2f) + +struct da903x_regulator_info { + struct regulator_desc desc; + + int max_uV; + int vol_reg; + int vol_shift; + int vol_nbits; + int update_reg; + int update_bit; + int enable_reg; + int enable_bit; +}; + +static inline struct device *to_da903x_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static inline int check_range(struct da903x_regulator_info *info, + int min_uV, int max_uV) +{ + if (min_uV < info->desc.min_uV || min_uV > info->max_uV) + return -EINVAL; + + return 0; +} + +/* DA9030/DA9034 common operations */ +static int da903x_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = to_da903x_dev(rdev); + uint8_t val, mask; + + if (rdev->desc->n_voltages == 1) + return -EINVAL; + + val = selector << info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + + return da903x_update(da9034_dev, info->vol_reg, val, mask); +} + +static int da903x_get_voltage_sel(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = to_da903x_dev(rdev); + uint8_t val, mask; + int ret; + + if (rdev->desc->n_voltages == 1) + return 0; + + ret = da903x_read(da9034_dev, info->vol_reg, &val); + if (ret) + return ret; + + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + val = (val & mask) >> info->vol_shift; + + return val; +} + +static int da903x_enable(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = to_da903x_dev(rdev); + + return da903x_set_bits(da9034_dev, info->enable_reg, + 1 << info->enable_bit); +} + +static int da903x_disable(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = to_da903x_dev(rdev); + + return da903x_clr_bits(da9034_dev, info->enable_reg, + 1 << info->enable_bit); +} + +static int da903x_is_enabled(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = to_da903x_dev(rdev); + uint8_t reg_val; + int ret; + + ret = da903x_read(da9034_dev, info->enable_reg, ®_val); + if (ret) + return ret; + + return !!(reg_val & (1 << info->enable_bit)); +} + +/* DA9030 specific operations */ +static int da9030_set_ldo1_15_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da903x_dev = to_da903x_dev(rdev); + uint8_t val, mask; + int ret; + + val = selector << info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + val |= DA9030_LDO_UNLOCK; /* have to set UNLOCK bits */ + mask |= DA9030_LDO_UNLOCK_MASK; + + /* write twice */ + ret = da903x_update(da903x_dev, info->vol_reg, val, mask); + if (ret) + return ret; + + return da903x_update(da903x_dev, info->vol_reg, val, mask); +} + +static int da9030_map_ldo14_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + int thresh, sel; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); + return -EINVAL; + } + + thresh = (info->max_uV + info->desc.min_uV) / 2; + if (min_uV < thresh) { + sel = DIV_ROUND_UP(thresh - min_uV, info->desc.uV_step); + sel |= 0x4; + } else { + sel = DIV_ROUND_UP(min_uV - thresh, info->desc.uV_step); + } + + return sel; +} + +static int da9030_list_ldo14_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + int volt; + + if (selector & 0x4) + volt = rdev->desc->min_uV + + rdev->desc->uV_step * (3 - (selector & ~0x4)); + else + volt = (info->max_uV + rdev->desc->min_uV) / 2 + + rdev->desc->uV_step * (selector & ~0x4); + + if (volt > info->max_uV) + return -EINVAL; + + return volt; +} + +/* DA9034 specific operations */ +static int da9034_set_dvc_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = to_da903x_dev(rdev); + uint8_t val, mask; + int ret; + + val = selector << info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + + ret = da903x_update(da9034_dev, info->vol_reg, val, mask); + if (ret) + return ret; + + ret = da903x_set_bits(da9034_dev, info->update_reg, + 1 << info->update_bit); + return ret; +} + +static int da9034_map_ldo12_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + int sel; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); + return -EINVAL; + } + + sel = DIV_ROUND_UP(min_uV - info->desc.min_uV, info->desc.uV_step); + sel = (sel >= 20) ? sel - 12 : ((sel > 7) ? 8 : sel); + + return sel; +} + +static int da9034_list_ldo12_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + int volt; + + if (selector >= 8) + volt = 2700000 + rdev->desc->uV_step * (selector - 8); + else + volt = rdev->desc->min_uV + rdev->desc->uV_step * selector; + + if (volt > info->max_uV) + return -EINVAL; + + return volt; +} + +static struct regulator_ops da903x_regulator_ldo_ops = { + .set_voltage_sel = da903x_set_voltage_sel, + .get_voltage_sel = da903x_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +/* NOTE: this is dedicated for the insane DA9030 LDO14 */ +static struct regulator_ops da9030_regulator_ldo14_ops = { + .set_voltage_sel = da903x_set_voltage_sel, + .get_voltage_sel = da903x_get_voltage_sel, + .list_voltage = da9030_list_ldo14_voltage, + .map_voltage = da9030_map_ldo14_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +/* NOTE: this is dedicated for the DA9030 LDO1 and LDO15 that have locks */ +static struct regulator_ops da9030_regulator_ldo1_15_ops = { + .set_voltage_sel = da9030_set_ldo1_15_voltage_sel, + .get_voltage_sel = da903x_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +static struct regulator_ops da9034_regulator_dvc_ops = { + .set_voltage_sel = da9034_set_dvc_voltage_sel, + .get_voltage_sel = da903x_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +/* NOTE: this is dedicated for the insane LDO12 */ +static struct regulator_ops da9034_regulator_ldo12_ops = { + .set_voltage_sel = da903x_set_voltage_sel, + .get_voltage_sel = da903x_get_voltage_sel, + .list_voltage = da9034_list_ldo12_voltage, + .map_voltage = da9034_map_ldo12_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +#define DA903x_LDO(_pmic, _id, min, max, step, vreg, shift, nbits, ereg, ebit) \ +{ \ + .desc = { \ + .name = "LDO" #_id, \ + .ops = &da903x_regulator_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _pmic##_ID_LDO##_id, \ + .n_voltages = (step) ? ((max - min) / step + 1) : 1, \ + .owner = THIS_MODULE, \ + .min_uV = (min) * 1000, \ + .uV_step = (step) * 1000, \ + }, \ + .max_uV = (max) * 1000, \ + .vol_reg = _pmic##_##vreg, \ + .vol_shift = (shift), \ + .vol_nbits = (nbits), \ + .enable_reg = _pmic##_##ereg, \ + .enable_bit = (ebit), \ +} + +#define DA903x_DVC(_pmic, _id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ +{ \ + .desc = { \ + .name = #_id, \ + .ops = &da9034_regulator_dvc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _pmic##_ID_##_id, \ + .n_voltages = (step) ? ((max - min) / step + 1) : 1, \ + .owner = THIS_MODULE, \ + .min_uV = (min) * 1000, \ + .uV_step = (step) * 1000, \ + }, \ + .max_uV = (max) * 1000, \ + .vol_reg = _pmic##_##vreg, \ + .vol_shift = (0), \ + .vol_nbits = (nbits), \ + .update_reg = _pmic##_##ureg, \ + .update_bit = (ubit), \ + .enable_reg = _pmic##_##ereg, \ + .enable_bit = (ebit), \ +} + +#define DA9034_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \ + DA903x_LDO(DA9034, _id, min, max, step, vreg, shift, nbits, ereg, ebit) + +#define DA9030_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \ + DA903x_LDO(DA9030, _id, min, max, step, vreg, shift, nbits, ereg, ebit) + +#define DA9030_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ + DA903x_DVC(DA9030, _id, min, max, step, vreg, nbits, ureg, ubit, \ + ereg, ebit) + +#define DA9034_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ + DA903x_DVC(DA9034, _id, min, max, step, vreg, nbits, ureg, ubit, \ + ereg, ebit) + +#define DA9035_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ + DA903x_DVC(DA9035, _id, min, max, step, vreg, nbits, ureg, ubit, \ + ereg, ebit) + +static struct da903x_regulator_info da903x_regulator_info[] = { + /* DA9030 */ + DA9030_DVC(BUCK2, 850, 1625, 25, BUCK2DVM1, 5, BUCK2DVM1, 7, RCTL11, 0), + + DA9030_LDO( 1, 1200, 3200, 100, LDO1, 0, 5, RCTL12, 1), + DA9030_LDO( 2, 1800, 3200, 100, LDO23, 0, 4, RCTL12, 2), + DA9030_LDO( 3, 1800, 3200, 100, LDO23, 4, 4, RCTL12, 3), + DA9030_LDO( 4, 1800, 3200, 100, LDO45, 0, 4, RCTL12, 4), + DA9030_LDO( 5, 1800, 3200, 100, LDO45, 4, 4, RCTL12, 5), + DA9030_LDO( 6, 1800, 3200, 100, LDO6, 0, 4, RCTL12, 6), + DA9030_LDO( 7, 1800, 3200, 100, LDO78, 0, 4, RCTL12, 7), + DA9030_LDO( 8, 1800, 3200, 100, LDO78, 4, 4, RCTL22, 0), + DA9030_LDO( 9, 1800, 3200, 100, LDO912, 0, 4, RCTL22, 1), + DA9030_LDO(10, 1800, 3200, 100, LDO1011, 0, 4, RCTL22, 2), + DA9030_LDO(11, 1800, 3200, 100, LDO1011, 4, 4, RCTL22, 3), + DA9030_LDO(12, 1800, 3200, 100, LDO912, 4, 4, RCTL22, 4), + DA9030_LDO(14, 2760, 2940, 30, LDO1416, 0, 3, RCTL11, 4), + DA9030_LDO(15, 1100, 2650, 50, LDO15, 0, 5, RCTL11, 5), + DA9030_LDO(16, 1100, 2650, 50, LDO1416, 3, 5, RCTL11, 6), + DA9030_LDO(17, 1800, 3200, 100, LDO17, 0, 4, RCTL11, 7), + DA9030_LDO(18, 1800, 3200, 100, LDO1819, 0, 4, RCTL21, 2), + DA9030_LDO(19, 1800, 3200, 100, LDO1819, 4, 4, RCTL21, 1), + DA9030_LDO(13, 2100, 2100, 0, INVAL, 0, 0, RCTL11, 3), /* fixed @2.1V */ + + /* DA9034 */ + DA9034_DVC(BUCK1, 725, 1500, 25, ADTV2, 5, VCC1, 0, OVER1, 0), + DA9034_DVC(BUCK2, 725, 1500, 25, CDTV2, 5, VCC1, 2, OVER1, 1), + DA9034_DVC(LDO2, 725, 1500, 25, SDTV2, 5, VCC1, 4, OVER1, 2), + DA9034_DVC(LDO1, 1700, 2075, 25, MDTV1, 4, VCC1, 6, OVER3, 4), + + DA9034_LDO( 3, 1800, 3300, 100, LDO643, 0, 4, OVER3, 5), + DA9034_LDO( 4, 1800, 2900,1100, LDO643, 4, 1, OVER3, 6), + DA9034_LDO( 6, 2500, 2850, 50, LDO643, 5, 3, OVER2, 0), + DA9034_LDO( 7, 2700, 3050, 50, LDO987, 0, 3, OVER2, 1), + DA9034_LDO( 8, 2700, 2850, 50, LDO987, 3, 2, OVER2, 2), + DA9034_LDO( 9, 2700, 3050, 50, LDO987, 5, 3, OVER2, 3), + DA9034_LDO(10, 2700, 3050, 50, LDO1110, 0, 3, OVER2, 4), + DA9034_LDO(11, 1800, 3300, 100, LDO1110, 4, 4, OVER2, 5), + DA9034_LDO(12, 1700, 3050, 50, LDO1312, 0, 4, OVER3, 6), + DA9034_LDO(13, 1800, 3300, 100, LDO1312, 4, 4, OVER2, 7), + DA9034_LDO(14, 1800, 3300, 100, LDO1514, 0, 4, OVER3, 0), + DA9034_LDO(15, 1800, 3300, 100, LDO1514, 4, 4, OVER3, 1), + DA9034_LDO(5, 3100, 3100, 0, INVAL, 0, 0, OVER3, 7), /* fixed @3.1V */ + + /* DA9035 */ + DA9035_DVC(BUCK3, 1800, 2200, 100, 3DTV1, 3, VCC2, 0, OVER3, 3), +}; + +static inline struct da903x_regulator_info *find_regulator_info(int id) +{ + struct da903x_regulator_info *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(da903x_regulator_info); i++) { + ri = &da903x_regulator_info[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +static int da903x_regulator_probe(struct platform_device *pdev) +{ + struct da903x_regulator_info *ri = NULL; + struct regulator_dev *rdev; + struct regulator_config config = { }; + + ri = find_regulator_info(pdev->id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + /* Workaround for the weird LDO12 voltage setting */ + if (ri->desc.id == DA9034_ID_LDO12) { + ri->desc.ops = &da9034_regulator_ldo12_ops; + ri->desc.n_voltages = 16; + } + + if (ri->desc.id == DA9030_ID_LDO14) + ri->desc.ops = &da9030_regulator_ldo14_ops; + + if (ri->desc.id == DA9030_ID_LDO1 || ri->desc.id == DA9030_ID_LDO15) + ri->desc.ops = &da9030_regulator_ldo1_15_ops; + + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = ri; + + rdev = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + return 0; +} + +static int da903x_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver da903x_regulator_driver = { + .driver = { + .name = "da903x-regulator", + .owner = THIS_MODULE, + }, + .probe = da903x_regulator_probe, + .remove = da903x_regulator_remove, +}; + +static int __init da903x_regulator_init(void) +{ + return platform_driver_register(&da903x_regulator_driver); +} +subsys_initcall(da903x_regulator_init); + +static void __exit da903x_regulator_exit(void) +{ + platform_driver_unregister(&da903x_regulator_driver); +} +module_exit(da903x_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Miao " + "Mike Rapoport "); +MODULE_DESCRIPTION("Regulator Driver for Dialog Semiconductor DA903X PMIC"); +MODULE_ALIAS("platform:da903x-regulator"); diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c new file mode 100644 index 0000000..96b569a --- /dev/null +++ b/drivers/regulator/da9052-regulator.c @@ -0,0 +1,437 @@ +/* +* da9052-regulator.c: Regulator driver for DA9052 +* +* Copyright(c) 2011 Dialog Semiconductor Ltd. +* +* Author: David Dajun Chen +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif + +#include +#include +#include + +/* Buck step size */ +#define DA9052_BUCK_PERI_3uV_STEP 100000 +#define DA9052_BUCK_PERI_REG_MAP_UPTO_3uV 24 +#define DA9052_CONST_3uV 3000000 + +#define DA9052_MIN_UA 0 +#define DA9052_MAX_UA 3 +#define DA9052_CURRENT_RANGE 4 + +/* Bit masks */ +#define DA9052_BUCK_ILIM_MASK_EVEN 0x0c +#define DA9052_BUCK_ILIM_MASK_ODD 0xc0 + +/* DA9052 REGULATOR IDs */ +#define DA9052_ID_BUCK1 0 +#define DA9052_ID_BUCK2 1 +#define DA9052_ID_BUCK3 2 +#define DA9052_ID_BUCK4 3 +#define DA9052_ID_LDO1 4 +#define DA9052_ID_LDO2 5 +#define DA9052_ID_LDO3 6 +#define DA9052_ID_LDO4 7 +#define DA9052_ID_LDO5 8 +#define DA9052_ID_LDO6 9 +#define DA9052_ID_LDO7 10 +#define DA9052_ID_LDO8 11 +#define DA9052_ID_LDO9 12 +#define DA9052_ID_LDO10 13 + +static const u32 da9052_current_limits[3][4] = { + {700000, 800000, 1000000, 1200000}, /* DA9052-BC BUCKs */ + {1600000, 2000000, 2400000, 3000000}, /* DA9053-AA/Bx BUCK-CORE */ + {800000, 1000000, 1200000, 1500000}, /* DA9053-AA/Bx BUCK-PRO, + * BUCK-MEM and BUCK-PERI + */ +}; + +struct da9052_regulator_info { + struct regulator_desc reg_desc; + int step_uV; + int min_uV; + int max_uV; +}; + +struct da9052_regulator { + struct da9052 *da9052; + struct da9052_regulator_info *info; + struct regulator_dev *rdev; +}; + +static int verify_range(struct da9052_regulator_info *info, + int min_uV, int max_uV) +{ + if (min_uV > info->max_uV || max_uV < info->min_uV) + return -EINVAL; + + return 0; +} + +static int da9052_dcdc_get_current_limit(struct regulator_dev *rdev) +{ + struct da9052_regulator *regulator = rdev_get_drvdata(rdev); + int offset = rdev_get_id(rdev); + int ret, row = 2; + + ret = da9052_reg_read(regulator->da9052, DA9052_BUCKA_REG + offset/2); + if (ret < 0) + return ret; + + /* Determine the even or odd position of the buck current limit + * register field + */ + if (offset % 2 == 0) + ret = (ret & DA9052_BUCK_ILIM_MASK_EVEN) >> 2; + else + ret = (ret & DA9052_BUCK_ILIM_MASK_ODD) >> 6; + + /* Select the appropriate current limit range */ + if (regulator->da9052->chip_id == DA9052) + row = 0; + else if (offset == 0) + row = 1; + + return da9052_current_limits[row][ret]; +} + +static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct da9052_regulator *regulator = rdev_get_drvdata(rdev); + int offset = rdev_get_id(rdev); + int reg_val = 0; + int i, row = 2; + + /* Select the appropriate current limit range */ + if (regulator->da9052->chip_id == DA9052) + row = 0; + else if (offset == 0) + row = 1; + + for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) { + if ((min_uA <= da9052_current_limits[row][i]) && + (da9052_current_limits[row][i] <= max_uA)) { + reg_val = i; + break; + } + } + + if (i < 0) + return -EINVAL; + + /* Determine the even or odd position of the buck current limit + * register field + */ + if (offset % 2 == 0) + return da9052_reg_update(regulator->da9052, + DA9052_BUCKA_REG + offset/2, + DA9052_BUCK_ILIM_MASK_EVEN, + reg_val << 2); + else + return da9052_reg_update(regulator->da9052, + DA9052_BUCKA_REG + offset/2, + DA9052_BUCK_ILIM_MASK_ODD, + reg_val << 6); +} + +static int da9052_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct da9052_regulator *regulator = rdev_get_drvdata(rdev); + struct da9052_regulator_info *info = regulator->info; + int id = rdev_get_id(rdev); + int volt_uV; + + if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052) + && (selector >= DA9052_BUCK_PERI_REG_MAP_UPTO_3uV)) { + volt_uV = ((DA9052_BUCK_PERI_REG_MAP_UPTO_3uV * info->step_uV) + + info->min_uV); + volt_uV += (selector - DA9052_BUCK_PERI_REG_MAP_UPTO_3uV) + * (DA9052_BUCK_PERI_3uV_STEP); + } else { + volt_uV = (selector * info->step_uV) + info->min_uV; + } + + if (volt_uV > info->max_uV) + return -EINVAL; + + return volt_uV; +} + +static int da9052_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da9052_regulator *regulator = rdev_get_drvdata(rdev); + struct da9052_regulator_info *info = regulator->info; + int id = rdev_get_id(rdev); + int ret, sel; + + ret = verify_range(info, min_uV, max_uV); + if (ret < 0) + return ret; + + if (min_uV < info->min_uV) + min_uV = info->min_uV; + + if ((id == DA9052_ID_BUCK4) && (regulator->da9052->chip_id == DA9052) + && (min_uV >= DA9052_CONST_3uV)) { + sel = DA9052_BUCK_PERI_REG_MAP_UPTO_3uV + + DIV_ROUND_UP(min_uV - DA9052_CONST_3uV, + DA9052_BUCK_PERI_3uV_STEP); + } else { + sel = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV); + } + + ret = da9052_list_voltage(rdev, sel); + if (ret < 0) + return ret; + + return sel; +} + +static struct regulator_ops da9052_dcdc_ops = { + .get_current_limit = da9052_dcdc_get_current_limit, + .set_current_limit = da9052_dcdc_set_current_limit, + + .list_voltage = da9052_list_voltage, + .map_voltage = da9052_map_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +static struct regulator_ops da9052_ldo_ops = { + .list_voltage = da9052_list_voltage, + .map_voltage = da9052_map_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +#define DA9052_LDO(_id, step, min, max, sbits, ebits, abits) \ +{\ + .reg_desc = {\ + .name = #_id,\ + .ops = &da9052_ldo_ops,\ + .type = REGULATOR_VOLTAGE,\ + .id = DA9052_ID_##_id,\ + .n_voltages = (max - min) / step + 1, \ + .owner = THIS_MODULE,\ + .vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ + .vsel_mask = (1 << (sbits)) - 1,\ + .apply_reg = DA9052_SUPPLY_REG, \ + .apply_bit = (abits), \ + .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ + .enable_mask = 1 << (ebits),\ + },\ + .min_uV = (min) * 1000,\ + .max_uV = (max) * 1000,\ + .step_uV = (step) * 1000,\ +} + +#define DA9052_DCDC(_id, step, min, max, sbits, ebits, abits) \ +{\ + .reg_desc = {\ + .name = #_id,\ + .ops = &da9052_dcdc_ops,\ + .type = REGULATOR_VOLTAGE,\ + .id = DA9052_ID_##_id,\ + .n_voltages = (max - min) / step + 1, \ + .owner = THIS_MODULE,\ + .vsel_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ + .vsel_mask = (1 << (sbits)) - 1,\ + .apply_reg = DA9052_SUPPLY_REG, \ + .apply_bit = (abits), \ + .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \ + .enable_mask = 1 << (ebits),\ + },\ + .min_uV = (min) * 1000,\ + .max_uV = (max) * 1000,\ + .step_uV = (step) * 1000,\ +} + +static struct da9052_regulator_info da9052_regulator_info[] = { + DA9052_DCDC(BUCK1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO), + DA9052_DCDC(BUCK2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO), + DA9052_DCDC(BUCK3, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO), + DA9052_DCDC(BUCK4, 50, 1800, 3600, 5, 6, 0), + DA9052_LDO(LDO1, 50, 600, 1800, 5, 6, 0), + DA9052_LDO(LDO2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO), + DA9052_LDO(LDO3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO), + DA9052_LDO(LDO4, 25, 1725, 3300, 6, 6, 0), + DA9052_LDO(LDO5, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO6, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO7, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO8, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO9, 50, 1250, 3650, 6, 6, 0), + DA9052_LDO(LDO10, 50, 1200, 3600, 6, 6, 0), +}; + +static struct da9052_regulator_info da9053_regulator_info[] = { + DA9052_DCDC(BUCK1, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBCOREGO), + DA9052_DCDC(BUCK2, 25, 500, 2075, 6, 6, DA9052_SUPPLY_VBPROGO), + DA9052_DCDC(BUCK3, 25, 925, 2500, 6, 6, DA9052_SUPPLY_VBMEMGO), + DA9052_DCDC(BUCK4, 25, 925, 2500, 6, 6, 0), + DA9052_LDO(LDO1, 50, 600, 1800, 5, 6, 0), + DA9052_LDO(LDO2, 25, 600, 1800, 6, 6, DA9052_SUPPLY_VLDO2GO), + DA9052_LDO(LDO3, 25, 1725, 3300, 6, 6, DA9052_SUPPLY_VLDO3GO), + DA9052_LDO(LDO4, 25, 1725, 3300, 6, 6, 0), + DA9052_LDO(LDO5, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO6, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO7, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO8, 50, 1200, 3600, 6, 6, 0), + DA9052_LDO(LDO9, 50, 1250, 3650, 6, 6, 0), + DA9052_LDO(LDO10, 50, 1200, 3600, 6, 6, 0), +}; + +static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id, + int id) +{ + struct da9052_regulator_info *info; + int i; + + switch (chip_id) { + case DA9052: + for (i = 0; i < ARRAY_SIZE(da9052_regulator_info); i++) { + info = &da9052_regulator_info[i]; + if (info->reg_desc.id == id) + return info; + } + break; + case DA9053_AA: + case DA9053_BA: + case DA9053_BB: + for (i = 0; i < ARRAY_SIZE(da9053_regulator_info); i++) { + info = &da9053_regulator_info[i]; + if (info->reg_desc.id == id) + return info; + } + break; + } + + return NULL; +} + +static int da9052_regulator_probe(struct platform_device *pdev) +{ + struct regulator_config config = { }; + struct da9052_regulator *regulator; + struct da9052 *da9052; + struct da9052_pdata *pdata; + + regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9052_regulator), + GFP_KERNEL); + if (!regulator) + return -ENOMEM; + + da9052 = dev_get_drvdata(pdev->dev.parent); + pdata = da9052->dev->platform_data; + regulator->da9052 = da9052; + + regulator->info = find_regulator_info(regulator->da9052->chip_id, + pdev->id); + if (regulator->info == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + config.dev = &pdev->dev; + config.driver_data = regulator; + config.regmap = da9052->regmap; + if (pdata && pdata->regulators) { + config.init_data = pdata->regulators[pdev->id]; + } else { +#ifdef CONFIG_OF + struct device_node *nproot, *np; + + nproot = of_node_get(da9052->dev->of_node); + if (!nproot) + return -ENODEV; + + nproot = of_find_node_by_name(nproot, "regulators"); + if (!nproot) + return -ENODEV; + + for_each_child_of_node(nproot, np) { + if (!of_node_cmp(np->name, + regulator->info->reg_desc.name)) { + config.init_data = of_get_regulator_init_data( + &pdev->dev, np); + config.of_node = np; + break; + } + } + of_node_put(nproot); +#endif + } + + regulator->rdev = regulator_register(®ulator->info->reg_desc, + &config); + if (IS_ERR(regulator->rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + regulator->info->reg_desc.name); + return PTR_ERR(regulator->rdev); + } + + platform_set_drvdata(pdev, regulator); + + return 0; +} + +static int da9052_regulator_remove(struct platform_device *pdev) +{ + struct da9052_regulator *regulator = platform_get_drvdata(pdev); + + regulator_unregister(regulator->rdev); + return 0; +} + +static struct platform_driver da9052_regulator_driver = { + .probe = da9052_regulator_probe, + .remove = da9052_regulator_remove, + .driver = { + .name = "da9052-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init da9052_regulator_init(void) +{ + return platform_driver_register(&da9052_regulator_driver); +} +subsys_initcall(da9052_regulator_init); + +static void __exit da9052_regulator_exit(void) +{ + platform_driver_unregister(&da9052_regulator_driver); +} +module_exit(da9052_regulator_exit); + +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9052 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-regulator"); diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c new file mode 100644 index 0000000..3022109 --- /dev/null +++ b/drivers/regulator/da9055-regulator.c @@ -0,0 +1,638 @@ +/* +* Regulator driver for DA9055 PMIC +* +* Copyright(c) 2012 Dialog Semiconductor Ltd. +* +* Author: David Dajun Chen +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DA9055_MIN_UA 0 +#define DA9055_MAX_UA 3 + +#define DA9055_LDO_MODE_SYNC 0 +#define DA9055_LDO_MODE_SLEEP 1 + +#define DA9055_BUCK_MODE_SLEEP 1 +#define DA9055_BUCK_MODE_SYNC 2 +#define DA9055_BUCK_MODE_AUTO 3 + +/* DA9055 REGULATOR IDs */ +#define DA9055_ID_BUCK1 0 +#define DA9055_ID_BUCK2 1 +#define DA9055_ID_LDO1 2 +#define DA9055_ID_LDO2 3 +#define DA9055_ID_LDO3 4 +#define DA9055_ID_LDO4 5 +#define DA9055_ID_LDO5 6 +#define DA9055_ID_LDO6 7 + +/* DA9055 BUCK current limit */ +static const int da9055_current_limits[] = { 500000, 600000, 700000, 800000 }; + +struct da9055_conf_reg { + int reg; + int sel_mask; + int en_mask; +}; + +struct da9055_volt_reg { + int reg_a; + int reg_b; + int sl_shift; + int v_mask; +}; + +struct da9055_mode_reg { + int reg; + int mask; + int shift; +}; + +struct da9055_regulator_info { + struct regulator_desc reg_desc; + struct da9055_conf_reg conf; + struct da9055_volt_reg volt; + struct da9055_mode_reg mode; +}; + +struct da9055_regulator { + struct da9055 *da9055; + struct da9055_regulator_info *info; + struct regulator_dev *rdev; + enum gpio_select reg_rselect; +}; + +static unsigned int da9055_buck_get_mode(struct regulator_dev *rdev) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int ret, mode = 0; + + ret = da9055_reg_read(regulator->da9055, info->mode.reg); + if (ret < 0) + return ret; + + switch ((ret & info->mode.mask) >> info->mode.shift) { + case DA9055_BUCK_MODE_SYNC: + mode = REGULATOR_MODE_FAST; + break; + case DA9055_BUCK_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case DA9055_BUCK_MODE_SLEEP: + mode = REGULATOR_MODE_STANDBY; + break; + } + + return mode; +} + +static int da9055_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int val = 0; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = DA9055_BUCK_MODE_SYNC << info->mode.shift; + break; + case REGULATOR_MODE_NORMAL: + val = DA9055_BUCK_MODE_AUTO << info->mode.shift; + break; + case REGULATOR_MODE_STANDBY: + val = DA9055_BUCK_MODE_SLEEP << info->mode.shift; + break; + } + + return da9055_reg_update(regulator->da9055, info->mode.reg, + info->mode.mask, val); +} + +static unsigned int da9055_ldo_get_mode(struct regulator_dev *rdev) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int ret; + + ret = da9055_reg_read(regulator->da9055, info->volt.reg_b); + if (ret < 0) + return ret; + + if (ret >> info->volt.sl_shift) + return REGULATOR_MODE_STANDBY; + else + return REGULATOR_MODE_NORMAL; +} + +static int da9055_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + struct da9055_volt_reg volt = info->volt; + int val = 0; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_FAST: + val = DA9055_LDO_MODE_SYNC; + break; + case REGULATOR_MODE_STANDBY: + val = DA9055_LDO_MODE_SLEEP; + break; + } + + return da9055_reg_update(regulator->da9055, volt.reg_b, + 1 << volt.sl_shift, + val << volt.sl_shift); +} + +static int da9055_buck_get_current_limit(struct regulator_dev *rdev) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int ret; + + ret = da9055_reg_read(regulator->da9055, DA9055_REG_BUCK_LIM); + if (ret < 0) + return ret; + + ret &= info->mode.mask; + return da9055_current_limits[ret >> info->mode.shift]; +} + +static int da9055_buck_set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int i; + + for (i = ARRAY_SIZE(da9055_current_limits) - 1; i >= 0; i--) { + if ((min_uA <= da9055_current_limits[i]) && + (da9055_current_limits[i] <= max_uA)) + return da9055_reg_update(regulator->da9055, + DA9055_REG_BUCK_LIM, + info->mode.mask, + i << info->mode.shift); + } + + return -EINVAL; +} + +static int da9055_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + struct da9055_volt_reg volt = info->volt; + int ret, sel; + + /* + * There are two voltage register set A & B for voltage ramping but + * either one of then can be active therefore we first determine + * the active register set. + */ + ret = da9055_reg_read(regulator->da9055, info->conf.reg); + if (ret < 0) + return ret; + + ret &= info->conf.sel_mask; + + /* Get the voltage for the active register set A/B */ + if (ret == DA9055_REGUALTOR_SET_A) + ret = da9055_reg_read(regulator->da9055, volt.reg_a); + else + ret = da9055_reg_read(regulator->da9055, volt.reg_b); + + if (ret < 0) + return ret; + + sel = (ret & volt.v_mask); + return sel; +} + +static int da9055_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int ret; + + /* + * Regulator register set A/B is not selected through GPIO therefore + * we use default register set A for voltage ramping. + */ + if (regulator->reg_rselect == NO_GPIO) { + /* Select register set A */ + ret = da9055_reg_update(regulator->da9055, info->conf.reg, + info->conf.sel_mask, DA9055_SEL_REG_A); + if (ret < 0) + return ret; + + /* Set the voltage */ + return da9055_reg_update(regulator->da9055, info->volt.reg_a, + info->volt.v_mask, selector); + } + + /* + * Here regulator register set A/B is selected through GPIO. + * Therefore we first determine the selected register set A/B and + * then set the desired voltage for that register set A/B. + */ + ret = da9055_reg_read(regulator->da9055, info->conf.reg); + if (ret < 0) + return ret; + + ret &= info->conf.sel_mask; + + /* Set the voltage */ + if (ret == DA9055_REGUALTOR_SET_A) + return da9055_reg_update(regulator->da9055, info->volt.reg_a, + info->volt.v_mask, selector); + else + return da9055_reg_update(regulator->da9055, info->volt.reg_b, + info->volt.v_mask, selector); +} + +static int da9055_regulator_set_suspend_voltage(struct regulator_dev *rdev, + int uV) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + int ret; + + /* Select register set B for suspend voltage ramping. */ + if (regulator->reg_rselect == NO_GPIO) { + ret = da9055_reg_update(regulator->da9055, info->conf.reg, + info->conf.sel_mask, DA9055_SEL_REG_B); + if (ret < 0) + return ret; + } + + ret = regulator_map_voltage_linear(rdev, uV, uV); + if (ret < 0) + return ret; + + return da9055_reg_update(regulator->da9055, info->volt.reg_b, + info->volt.v_mask, ret); +} + +static int da9055_suspend_enable(struct regulator_dev *rdev) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + + /* Select register set B for voltage ramping. */ + if (regulator->reg_rselect == NO_GPIO) + return da9055_reg_update(regulator->da9055, info->conf.reg, + info->conf.sel_mask, DA9055_SEL_REG_B); + else + return 0; +} + +static int da9055_suspend_disable(struct regulator_dev *rdev) +{ + struct da9055_regulator *regulator = rdev_get_drvdata(rdev); + struct da9055_regulator_info *info = regulator->info; + + /* Diselect register set B. */ + if (regulator->reg_rselect == NO_GPIO) + return da9055_reg_update(regulator->da9055, info->conf.reg, + info->conf.sel_mask, DA9055_SEL_REG_A); + else + return 0; +} + +static struct regulator_ops da9055_buck_ops = { + .get_mode = da9055_buck_get_mode, + .set_mode = da9055_buck_set_mode, + + .get_current_limit = da9055_buck_get_current_limit, + .set_current_limit = da9055_buck_set_current_limit, + + .get_voltage_sel = da9055_regulator_get_voltage_sel, + .set_voltage_sel = da9055_regulator_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + + .set_suspend_voltage = da9055_regulator_set_suspend_voltage, + .set_suspend_enable = da9055_suspend_enable, + .set_suspend_disable = da9055_suspend_disable, + .set_suspend_mode = da9055_buck_set_mode, +}; + +static struct regulator_ops da9055_ldo_ops = { + .get_mode = da9055_ldo_get_mode, + .set_mode = da9055_ldo_set_mode, + + .get_voltage_sel = da9055_regulator_get_voltage_sel, + .set_voltage_sel = da9055_regulator_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + + .set_suspend_voltage = da9055_regulator_set_suspend_voltage, + .set_suspend_enable = da9055_suspend_enable, + .set_suspend_disable = da9055_suspend_disable, + .set_suspend_mode = da9055_ldo_set_mode, + +}; + +#define DA9055_LDO(_id, step, min, max, vbits, voffset) \ +{\ + .reg_desc = {\ + .name = #_id,\ + .ops = &da9055_ldo_ops,\ + .type = REGULATOR_VOLTAGE,\ + .id = DA9055_ID_##_id,\ + .n_voltages = (max - min) / step + 1 + (voffset), \ + .enable_reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \ + .enable_mask = 1, \ + .min_uV = (min) * 1000,\ + .uV_step = (step) * 1000,\ + .linear_min_sel = (voffset),\ + .owner = THIS_MODULE,\ + },\ + .conf = {\ + .reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \ + .sel_mask = (1 << 4),\ + .en_mask = 1,\ + },\ + .volt = {\ + .reg_a = DA9055_REG_VBCORE_A + DA9055_ID_##_id, \ + .reg_b = DA9055_REG_VBCORE_B + DA9055_ID_##_id, \ + .sl_shift = 7,\ + .v_mask = (1 << (vbits)) - 1,\ + },\ +} + +#define DA9055_BUCK(_id, step, min, max, vbits, voffset, mbits, sbits) \ +{\ + .reg_desc = {\ + .name = #_id,\ + .ops = &da9055_buck_ops,\ + .type = REGULATOR_VOLTAGE,\ + .id = DA9055_ID_##_id,\ + .n_voltages = (max - min) / step + 1 + (voffset), \ + .enable_reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \ + .enable_mask = 1,\ + .min_uV = (min) * 1000,\ + .uV_step = (step) * 1000,\ + .linear_min_sel = (voffset),\ + .owner = THIS_MODULE,\ + },\ + .conf = {\ + .reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \ + .sel_mask = (1 << 4),\ + .en_mask = 1,\ + },\ + .volt = {\ + .reg_a = DA9055_REG_VBCORE_A + DA9055_ID_##_id, \ + .reg_b = DA9055_REG_VBCORE_B + DA9055_ID_##_id, \ + .sl_shift = 7,\ + .v_mask = (1 << (vbits)) - 1,\ + },\ + .mode = {\ + .reg = DA9055_REG_BCORE_MODE,\ + .mask = (mbits),\ + .shift = (sbits),\ + },\ +} + +static struct da9055_regulator_info da9055_regulator_info[] = { + DA9055_BUCK(BUCK1, 25, 725, 2075, 6, 9, 0xc, 2), + DA9055_BUCK(BUCK2, 25, 925, 2500, 6, 0, 3, 0), + DA9055_LDO(LDO1, 50, 900, 3300, 6, 2), + DA9055_LDO(LDO2, 50, 900, 3300, 6, 3), + DA9055_LDO(LDO3, 50, 900, 3300, 6, 2), + DA9055_LDO(LDO4, 50, 900, 3300, 6, 2), + DA9055_LDO(LDO5, 50, 900, 2750, 6, 2), + DA9055_LDO(LDO6, 20, 900, 3300, 7, 0), +}; + +/* + * Configures regulator to be controlled either through GPIO 1 or 2. + * GPIO can control regulator state and/or select the regulator register + * set A/B for voltage ramping. + */ +static int da9055_gpio_init(struct da9055_regulator *regulator, + struct regulator_config *config, + struct da9055_pdata *pdata, int id) +{ + struct da9055_regulator_info *info = regulator->info; + int ret = 0; + + if (pdata->gpio_ren && pdata->gpio_ren[id]) { + char name[18]; + int gpio_mux = pdata->gpio_ren[id]; + + config->ena_gpio = pdata->ena_gpio[id]; + config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + config->ena_gpio_invert = 1; + + /* + * GPI pin is muxed with regulator to control the + * regulator state. + */ + sprintf(name, "DA9055 GPI %d", gpio_mux); + ret = devm_gpio_request_one(config->dev, gpio_mux, GPIOF_DIR_IN, + name); + if (ret < 0) + goto err; + + /* + * Let the regulator know that its state is controlled + * through GPI. + */ + ret = da9055_reg_update(regulator->da9055, info->conf.reg, + DA9055_E_GPI_MASK, + pdata->reg_ren[id] + << DA9055_E_GPI_SHIFT); + if (ret < 0) + goto err; + } + + if (pdata->gpio_rsel && pdata->gpio_rsel[id]) { + char name[18]; + int gpio_mux = pdata->gpio_rsel[id]; + + regulator->reg_rselect = pdata->reg_rsel[id]; + + /* + * GPI pin is muxed with regulator to select the + * regulator register set A/B for voltage ramping. + */ + sprintf(name, "DA9055 GPI %d", gpio_mux); + ret = devm_gpio_request_one(config->dev, gpio_mux, GPIOF_DIR_IN, + name); + if (ret < 0) + goto err; + + /* + * Let the regulator know that its register set A/B + * will be selected through GPI for voltage ramping. + */ + ret = da9055_reg_update(regulator->da9055, info->conf.reg, + DA9055_V_GPI_MASK, + pdata->reg_rsel[id] + << DA9055_V_GPI_SHIFT); + } + +err: + return ret; +} + +static irqreturn_t da9055_ldo5_6_oc_irq(int irq, void *data) +{ + struct da9055_regulator *regulator = data; + + regulator_notifier_call_chain(regulator->rdev, + REGULATOR_EVENT_OVER_CURRENT, NULL); + + return IRQ_HANDLED; +} + +static inline struct da9055_regulator_info *find_regulator_info(int id) +{ + struct da9055_regulator_info *info; + int i; + + for (i = 0; i < ARRAY_SIZE(da9055_regulator_info); i++) { + info = &da9055_regulator_info[i]; + if (info->reg_desc.id == id) + return info; + } + + return NULL; +} + +static int da9055_regulator_probe(struct platform_device *pdev) +{ + struct regulator_config config = { }; + struct da9055_regulator *regulator; + struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent); + struct da9055_pdata *pdata = da9055->dev->platform_data; + int ret, irq; + + if (pdata == NULL || pdata->regulators[pdev->id] == NULL) + return -ENODEV; + + regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9055_regulator), + GFP_KERNEL); + if (!regulator) + return -ENOMEM; + + regulator->info = find_regulator_info(pdev->id); + if (regulator->info == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + regulator->da9055 = da9055; + config.dev = &pdev->dev; + config.driver_data = regulator; + config.regmap = da9055->regmap; + + if (pdata && pdata->regulators) + config.init_data = pdata->regulators[pdev->id]; + + ret = da9055_gpio_init(regulator, &config, pdata, pdev->id); + if (ret < 0) + return ret; + + regulator->rdev = regulator_register(®ulator->info->reg_desc, + &config); + if (IS_ERR(regulator->rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + regulator->info->reg_desc.name); + ret = PTR_ERR(regulator->rdev); + return ret; + } + + /* Only LDO 5 and 6 has got the over current interrupt */ + if (pdev->id == DA9055_ID_LDO5 || pdev->id == DA9055_ID_LDO6) { + irq = platform_get_irq_byname(pdev, "REGULATOR"); + irq = regmap_irq_get_virq(da9055->irq_data, irq); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + da9055_ldo5_6_oc_irq, + IRQF_TRIGGER_HIGH | + IRQF_ONESHOT | + IRQF_PROBE_SHARED, + pdev->name, regulator); + if (ret != 0) { + if (ret != -EBUSY) { + dev_err(&pdev->dev, + "Failed to request Regulator IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + } + } + + platform_set_drvdata(pdev, regulator); + + return 0; + +err_regulator: + regulator_unregister(regulator->rdev); + return ret; +} + +static int da9055_regulator_remove(struct platform_device *pdev) +{ + struct da9055_regulator *regulator = platform_get_drvdata(pdev); + + regulator_unregister(regulator->rdev); + + return 0; +} + +static struct platform_driver da9055_regulator_driver = { + .probe = da9055_regulator_probe, + .remove = da9055_regulator_remove, + .driver = { + .name = "da9055-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init da9055_regulator_init(void) +{ + return platform_driver_register(&da9055_regulator_driver); +} +subsys_initcall(da9055_regulator_init); + +static void __exit da9055_regulator_exit(void) +{ + platform_driver_unregister(&da9055_regulator_driver); +} +module_exit(da9055_regulator_exit); + +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9055 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-regulator"); diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c new file mode 100644 index 0000000..a53c11a --- /dev/null +++ b/drivers/regulator/db8500-prcmu.c @@ -0,0 +1,574 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Authors: Sundar Iyer for ST-Ericsson + * Bengt Jonsson for ST-Ericsson + * + * Power domain regulators on DB8500 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dbx500-prcmu.h" + +static int db8500_regulator_enable(struct regulator_dev *rdev) +{ + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) + return -EINVAL; + + dev_vdbg(rdev_get_dev(rdev), "regulator-%s-enable\n", + info->desc.name); + + if (!info->is_enabled) { + info->is_enabled = true; + if (!info->exclude_from_power_state) + power_state_active_enable(); + } + + return 0; +} + +static int db8500_regulator_disable(struct regulator_dev *rdev) +{ + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); + int ret = 0; + + if (info == NULL) + return -EINVAL; + + dev_vdbg(rdev_get_dev(rdev), "regulator-%s-disable\n", + info->desc.name); + + if (info->is_enabled) { + info->is_enabled = false; + if (!info->exclude_from_power_state) + ret = power_state_active_disable(); + } + + return ret; +} + +static int db8500_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) + return -EINVAL; + + dev_vdbg(rdev_get_dev(rdev), "regulator-%s-is_enabled (is_enabled):" + " %i\n", info->desc.name, info->is_enabled); + + return info->is_enabled; +} + +/* db8500 regulator operations */ +static struct regulator_ops db8500_regulator_ops = { + .enable = db8500_regulator_enable, + .disable = db8500_regulator_disable, + .is_enabled = db8500_regulator_is_enabled, +}; + +/* + * EPOD control + */ +static bool epod_on[NUM_EPOD_ID]; +static bool epod_ramret[NUM_EPOD_ID]; + +static int enable_epod(u16 epod_id, bool ramret) +{ + int ret; + + if (ramret) { + if (!epod_on[epod_id]) { + ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); + if (ret < 0) + return ret; + } + epod_ramret[epod_id] = true; + } else { + ret = prcmu_set_epod(epod_id, EPOD_STATE_ON); + if (ret < 0) + return ret; + epod_on[epod_id] = true; + } + + return 0; +} + +static int disable_epod(u16 epod_id, bool ramret) +{ + int ret; + + if (ramret) { + if (!epod_on[epod_id]) { + ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF); + if (ret < 0) + return ret; + } + epod_ramret[epod_id] = false; + } else { + if (epod_ramret[epod_id]) { + ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); + if (ret < 0) + return ret; + } else { + ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF); + if (ret < 0) + return ret; + } + epod_on[epod_id] = false; + } + + return 0; +} + +/* + * Regulator switch + */ +static int db8500_regulator_switch_enable(struct regulator_dev *rdev) +{ + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + if (info == NULL) + return -EINVAL; + + dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-enable\n", + info->desc.name); + + ret = enable_epod(info->epod_id, info->is_ramret); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "regulator-switch-%s-enable: prcmu call failed\n", + info->desc.name); + goto out; + } + + info->is_enabled = true; +out: + return ret; +} + +static int db8500_regulator_switch_disable(struct regulator_dev *rdev) +{ + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + if (info == NULL) + return -EINVAL; + + dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-disable\n", + info->desc.name); + + ret = disable_epod(info->epod_id, info->is_ramret); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "regulator_switch-%s-disable: prcmu call failed\n", + info->desc.name); + goto out; + } + + info->is_enabled = 0; +out: + return ret; +} + +static int db8500_regulator_switch_is_enabled(struct regulator_dev *rdev) +{ + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) + return -EINVAL; + + dev_vdbg(rdev_get_dev(rdev), + "regulator-switch-%s-is_enabled (is_enabled): %i\n", + info->desc.name, info->is_enabled); + + return info->is_enabled; +} + +static struct regulator_ops db8500_regulator_switch_ops = { + .enable = db8500_regulator_switch_enable, + .disable = db8500_regulator_switch_disable, + .is_enabled = db8500_regulator_switch_is_enabled, +}; + +/* + * Regulator information + */ +static struct dbx500_regulator_info +dbx500_regulator_info[DB8500_NUM_REGULATORS] = { + [DB8500_REGULATOR_VAPE] = { + .desc = { + .name = "db8500-vape", + .id = DB8500_REGULATOR_VAPE, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VARM] = { + .desc = { + .name = "db8500-varm", + .id = DB8500_REGULATOR_VARM, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VMODEM] = { + .desc = { + .name = "db8500-vmodem", + .id = DB8500_REGULATOR_VMODEM, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VPLL] = { + .desc = { + .name = "db8500-vpll", + .id = DB8500_REGULATOR_VPLL, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VSMPS1] = { + .desc = { + .name = "db8500-vsmps1", + .id = DB8500_REGULATOR_VSMPS1, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VSMPS2] = { + .desc = { + .name = "db8500-vsmps2", + .id = DB8500_REGULATOR_VSMPS2, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .exclude_from_power_state = true, + }, + [DB8500_REGULATOR_VSMPS3] = { + .desc = { + .name = "db8500-vsmps3", + .id = DB8500_REGULATOR_VSMPS3, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VRF1] = { + .desc = { + .name = "db8500-vrf1", + .id = DB8500_REGULATOR_VRF1, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_SWITCH_SVAMMDSP] = { + .desc = { + .name = "db8500-sva-mmdsp", + .id = DB8500_REGULATOR_SWITCH_SVAMMDSP, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SVAMMDSP, + }, + [DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = { + .desc = { + .name = "db8500-sva-mmdsp-ret", + .id = DB8500_REGULATOR_SWITCH_SVAMMDSPRET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SVAMMDSP, + .is_ramret = true, + }, + [DB8500_REGULATOR_SWITCH_SVAPIPE] = { + .desc = { + .name = "db8500-sva-pipe", + .id = DB8500_REGULATOR_SWITCH_SVAPIPE, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SVAPIPE, + }, + [DB8500_REGULATOR_SWITCH_SIAMMDSP] = { + .desc = { + .name = "db8500-sia-mmdsp", + .id = DB8500_REGULATOR_SWITCH_SIAMMDSP, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SIAMMDSP, + }, + [DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = { + .desc = { + .name = "db8500-sia-mmdsp-ret", + .id = DB8500_REGULATOR_SWITCH_SIAMMDSPRET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SIAMMDSP, + .is_ramret = true, + }, + [DB8500_REGULATOR_SWITCH_SIAPIPE] = { + .desc = { + .name = "db8500-sia-pipe", + .id = DB8500_REGULATOR_SWITCH_SIAPIPE, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SIAPIPE, + }, + [DB8500_REGULATOR_SWITCH_SGA] = { + .desc = { + .name = "db8500-sga", + .id = DB8500_REGULATOR_SWITCH_SGA, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SGA, + }, + [DB8500_REGULATOR_SWITCH_B2R2_MCDE] = { + .desc = { + .name = "db8500-b2r2-mcde", + .id = DB8500_REGULATOR_SWITCH_B2R2_MCDE, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_B2R2_MCDE, + }, + [DB8500_REGULATOR_SWITCH_ESRAM12] = { + .desc = { + .name = "db8500-esram12", + .id = DB8500_REGULATOR_SWITCH_ESRAM12, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM12, + .is_enabled = true, + }, + [DB8500_REGULATOR_SWITCH_ESRAM12RET] = { + .desc = { + .name = "db8500-esram12-ret", + .id = DB8500_REGULATOR_SWITCH_ESRAM12RET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM12, + .is_ramret = true, + }, + [DB8500_REGULATOR_SWITCH_ESRAM34] = { + .desc = { + .name = "db8500-esram34", + .id = DB8500_REGULATOR_SWITCH_ESRAM34, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM34, + .is_enabled = true, + }, + [DB8500_REGULATOR_SWITCH_ESRAM34RET] = { + .desc = { + .name = "db8500-esram34-ret", + .id = DB8500_REGULATOR_SWITCH_ESRAM34RET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM34, + .is_ramret = true, + }, +}; + +static int db8500_regulator_register(struct platform_device *pdev, + struct regulator_init_data *init_data, + int id, + struct device_node *np) +{ + struct dbx500_regulator_info *info; + struct regulator_config config = { }; + int err; + + /* assign per-regulator data */ + info = &dbx500_regulator_info[id]; + info->dev = &pdev->dev; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = info; + config.of_node = np; + + /* register with the regulator framework */ + info->rdev = regulator_register(&info->desc, &config); + if (IS_ERR(info->rdev)) { + err = PTR_ERR(info->rdev); + dev_err(&pdev->dev, "failed to register %s: err %i\n", + info->desc.name, err); + + /* if failing, unregister all earlier regulators */ + while (--id >= 0) { + info = &dbx500_regulator_info[id]; + regulator_unregister(info->rdev); + } + return err; + } + + dev_dbg(rdev_get_dev(info->rdev), + "regulator-%s-probed\n", info->desc.name); + + return 0; +} + +static struct of_regulator_match db8500_regulator_matches[] = { + { .name = "db8500_vape", .driver_data = (void *) DB8500_REGULATOR_VAPE, }, + { .name = "db8500_varm", .driver_data = (void *) DB8500_REGULATOR_VARM, }, + { .name = "db8500_vmodem", .driver_data = (void *) DB8500_REGULATOR_VMODEM, }, + { .name = "db8500_vpll", .driver_data = (void *) DB8500_REGULATOR_VPLL, }, + { .name = "db8500_vsmps1", .driver_data = (void *) DB8500_REGULATOR_VSMPS1, }, + { .name = "db8500_vsmps2", .driver_data = (void *) DB8500_REGULATOR_VSMPS2, }, + { .name = "db8500_vsmps3", .driver_data = (void *) DB8500_REGULATOR_VSMPS3, }, + { .name = "db8500_vrf1", .driver_data = (void *) DB8500_REGULATOR_VRF1, }, + { .name = "db8500_sva_mmdsp", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAMMDSP, }, + { .name = "db8500_sva_mmdsp_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAMMDSPRET, }, + { .name = "db8500_sva_pipe", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAPIPE, }, + { .name = "db8500_sia_mmdsp", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAMMDSP, }, + { .name = "db8500_sia_mmdsp_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAMMDSPRET, }, + { .name = "db8500_sia_pipe", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAPIPE, }, + { .name = "db8500_sga", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SGA, }, + { .name = "db8500_b2r2_mcde", .driver_data = (void *) DB8500_REGULATOR_SWITCH_B2R2_MCDE, }, + { .name = "db8500_esram12", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM12, }, + { .name = "db8500_esram12_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM12RET, }, + { .name = "db8500_esram34", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34, }, + { .name = "db8500_esram34_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34RET, }, +}; + +static int +db8500_regulator_of_probe(struct platform_device *pdev, + struct device_node *np) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) { + err = db8500_regulator_register( + pdev, db8500_regulator_matches[i].init_data, + i, db8500_regulator_matches[i].of_node); + if (err) + return err; + } + + return 0; +} + +static int db8500_regulator_probe(struct platform_device *pdev) +{ + struct regulator_init_data *db8500_init_data = + dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + int i, err; + + /* register all regulators */ + if (np) { + err = of_regulator_match(&pdev->dev, np, + db8500_regulator_matches, + ARRAY_SIZE(db8500_regulator_matches)); + if (err < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", err); + return err; + } + + err = db8500_regulator_of_probe(pdev, np); + if (err) + return err; + } else { + for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) { + err = db8500_regulator_register(pdev, + &db8500_init_data[i], + i, NULL); + if (err) + return err; + } + } + + err = ux500_regulator_debug_init(pdev, + dbx500_regulator_info, + ARRAY_SIZE(dbx500_regulator_info)); + return 0; +} + +static int db8500_regulator_remove(struct platform_device *pdev) +{ + int i; + + ux500_regulator_debug_exit(); + + for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) { + struct dbx500_regulator_info *info; + info = &dbx500_regulator_info[i]; + + dev_vdbg(rdev_get_dev(info->rdev), + "regulator-%s-remove\n", info->desc.name); + + regulator_unregister(info->rdev); + } + + return 0; +} + +static struct platform_driver db8500_regulator_driver = { + .driver = { + .name = "db8500-prcmu-regulators", + .owner = THIS_MODULE, + }, + .probe = db8500_regulator_probe, + .remove = db8500_regulator_remove, +}; + +static int __init db8500_regulator_init(void) +{ + return platform_driver_register(&db8500_regulator_driver); +} + +static void __exit db8500_regulator_exit(void) +{ + platform_driver_unregister(&db8500_regulator_driver); +} + +arch_initcall(db8500_regulator_init); +module_exit(db8500_regulator_exit); + +MODULE_AUTHOR("STMicroelectronics/ST-Ericsson"); +MODULE_DESCRIPTION("DB8500 regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c new file mode 100644 index 0000000..43267c5 --- /dev/null +++ b/drivers/regulator/dbx500-prcmu.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Authors: Sundar Iyer for ST-Ericsson + * Bengt Jonsson for ST-Ericsson + * + * UX500 common part of Power domain regulators + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dbx500-prcmu.h" + +/* + * power state reference count + */ +static int power_state_active_cnt; /* will initialize to zero */ +static DEFINE_SPINLOCK(power_state_active_lock); + +void power_state_active_enable(void) +{ + unsigned long flags; + + spin_lock_irqsave(&power_state_active_lock, flags); + power_state_active_cnt++; + spin_unlock_irqrestore(&power_state_active_lock, flags); +} + +int power_state_active_disable(void) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&power_state_active_lock, flags); + if (power_state_active_cnt <= 0) { + pr_err("power state: unbalanced enable/disable calls\n"); + ret = -EINVAL; + goto out; + } + + power_state_active_cnt--; +out: + spin_unlock_irqrestore(&power_state_active_lock, flags); + return ret; +} + +#ifdef CPTCFG_REGULATOR_DEBUG + +static int power_state_active_get(void) +{ + unsigned long flags; + int cnt; + + spin_lock_irqsave(&power_state_active_lock, flags); + cnt = power_state_active_cnt; + spin_unlock_irqrestore(&power_state_active_lock, flags); + + return cnt; +} + +static struct ux500_regulator_debug { + struct dentry *dir; + struct dentry *status_file; + struct dentry *power_state_cnt_file; + struct dbx500_regulator_info *regulator_array; + int num_regulators; + u8 *state_before_suspend; + u8 *state_after_suspend; +} rdebug; + +void ux500_regulator_suspend_debug(void) +{ + int i; + for (i = 0; i < rdebug.num_regulators; i++) + rdebug.state_before_suspend[i] = + rdebug.regulator_array[i].is_enabled; +} + +void ux500_regulator_resume_debug(void) +{ + int i; + for (i = 0; i < rdebug.num_regulators; i++) + rdebug.state_after_suspend[i] = + rdebug.regulator_array[i].is_enabled; +} + +static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) +{ + struct device *dev = s->private; + int err; + + /* print power state count */ + err = seq_printf(s, "ux500-regulator power state count: %i\n", + power_state_active_get()); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + + return 0; +} + +static int ux500_regulator_power_state_cnt_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ux500_regulator_power_state_cnt_print, + inode->i_private); +} + +static const struct file_operations ux500_regulator_power_state_cnt_fops = { + .open = ux500_regulator_power_state_cnt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ux500_regulator_status_print(struct seq_file *s, void *p) +{ + struct device *dev = s->private; + int err; + int i; + + /* print dump header */ + err = seq_printf(s, "ux500-regulator status:\n"); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + + err = seq_printf(s, "%31s : %8s : %8s\n", "current", + "before", "after"); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + + for (i = 0; i < rdebug.num_regulators; i++) { + struct dbx500_regulator_info *info; + /* Access per-regulator data */ + info = &rdebug.regulator_array[i]; + + /* print status */ + err = seq_printf(s, "%20s : %8s : %8s : %8s\n", info->desc.name, + info->is_enabled ? "enabled" : "disabled", + rdebug.state_before_suspend[i] ? "enabled" : "disabled", + rdebug.state_after_suspend[i] ? "enabled" : "disabled"); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + } + + return 0; +} + +static int ux500_regulator_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, ux500_regulator_status_print, + inode->i_private); +} + +static const struct file_operations ux500_regulator_status_fops = { + .open = ux500_regulator_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +int __attribute__((weak)) dbx500_regulator_testcase( + struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + return 0; +} + +int +ux500_regulator_debug_init(struct platform_device *pdev, + struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + /* create directory */ + rdebug.dir = debugfs_create_dir("ux500-regulator", NULL); + if (!rdebug.dir) + goto exit_no_debugfs; + + /* create "status" file */ + rdebug.status_file = debugfs_create_file("status", + S_IRUGO, rdebug.dir, &pdev->dev, + &ux500_regulator_status_fops); + if (!rdebug.status_file) + goto exit_destroy_dir; + + /* create "power-state-count" file */ + rdebug.power_state_cnt_file = debugfs_create_file("power-state-count", + S_IRUGO, rdebug.dir, &pdev->dev, + &ux500_regulator_power_state_cnt_fops); + if (!rdebug.power_state_cnt_file) + goto exit_destroy_status; + + rdebug.regulator_array = regulator_info; + rdebug.num_regulators = num_regulators; + + rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL); + if (!rdebug.state_before_suspend) { + dev_err(&pdev->dev, + "could not allocate memory for saving state\n"); + goto exit_destroy_power_state; + } + + rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL); + if (!rdebug.state_after_suspend) { + dev_err(&pdev->dev, + "could not allocate memory for saving state\n"); + goto exit_free; + } + + dbx500_regulator_testcase(regulator_info, num_regulators); + return 0; + +exit_free: + kfree(rdebug.state_before_suspend); +exit_destroy_power_state: + debugfs_remove(rdebug.power_state_cnt_file); +exit_destroy_status: + debugfs_remove(rdebug.status_file); +exit_destroy_dir: + debugfs_remove(rdebug.dir); +exit_no_debugfs: + dev_err(&pdev->dev, "failed to create debugfs entries.\n"); + return -ENOMEM; +} + +int ux500_regulator_debug_exit(void) +{ + debugfs_remove_recursive(rdebug.dir); + kfree(rdebug.state_after_suspend); + kfree(rdebug.state_before_suspend); + + return 0; +} +#endif diff --git a/drivers/regulator/dbx500-prcmu.h b/drivers/regulator/dbx500-prcmu.h new file mode 100644 index 0000000..59dc714 --- /dev/null +++ b/drivers/regulator/dbx500-prcmu.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Bengt Jonsson for ST-Ericsson, + * Jonas Aaberg for ST-Ericsson + * + * License Terms: GNU General Public License v2 + * + */ + +#ifndef DBX500_REGULATOR_H +#define DBX500_REGULATOR_H + +#include + +/** + * struct dbx500_regulator_info - dbx500 regulator information + * @dev: device pointer + * @desc: regulator description + * @rdev: regulator device pointer + * @is_enabled: status of the regulator + * @epod_id: id for EPOD (power domain) + * @is_ramret: RAM retention switch for EPOD (power domain) + * + */ +struct dbx500_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + bool is_enabled; + u16 epod_id; + bool is_ramret; + bool exclude_from_power_state; +}; + +void power_state_active_enable(void); +int power_state_active_disable(void); + + +#ifdef CPTCFG_REGULATOR_DEBUG +int ux500_regulator_debug_init(struct platform_device *pdev, + struct dbx500_regulator_info *regulator_info, + int num_regulators); + +int ux500_regulator_debug_exit(void); +#else + +static inline int ux500_regulator_debug_init(struct platform_device *pdev, + struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + return 0; +} + +static inline int ux500_regulator_debug_exit(void) +{ + return 0; +} + +#endif +#endif diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c new file mode 100644 index 0000000..df9f425 --- /dev/null +++ b/drivers/regulator/dummy.c @@ -0,0 +1,90 @@ +/* + * dummy.c + * + * Copyright 2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#include +#include +#include +#include +#include + +#include "dummy.h" + +struct regulator_dev *dummy_regulator_rdev; + +static struct regulator_init_data dummy_initdata; + +static struct regulator_ops dummy_ops; + +static struct regulator_desc dummy_desc = { + .name = "regulator-dummy", + .id = -1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &dummy_ops, +}; + +static int dummy_regulator_probe(struct platform_device *pdev) +{ + struct regulator_config config = { }; + int ret; + + config.dev = &pdev->dev; + config.init_data = &dummy_initdata; + + dummy_regulator_rdev = regulator_register(&dummy_desc, &config); + if (IS_ERR(dummy_regulator_rdev)) { + ret = PTR_ERR(dummy_regulator_rdev); + pr_err("Failed to register regulator: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver dummy_regulator_driver = { + .probe = dummy_regulator_probe, + .driver = { + .name = "reg-dummy", + .owner = THIS_MODULE, + }, +}; + +static struct platform_device *dummy_pdev; + +void __init regulator_dummy_init(void) +{ + int ret; + + dummy_pdev = platform_device_alloc("reg-dummy", -1); + if (!dummy_pdev) { + pr_err("Failed to allocate dummy regulator device\n"); + return; + } + + ret = platform_device_add(dummy_pdev); + if (ret != 0) { + pr_err("Failed to register dummy regulator device: %d\n", ret); + platform_device_put(dummy_pdev); + return; + } + + ret = platform_driver_register(&dummy_regulator_driver); + if (ret != 0) { + pr_err("Failed to register dummy regulator driver: %d\n", ret); + platform_device_unregister(dummy_pdev); + } +} diff --git a/drivers/regulator/dummy.h b/drivers/regulator/dummy.h new file mode 100644 index 0000000..97a11b7 --- /dev/null +++ b/drivers/regulator/dummy.h @@ -0,0 +1,27 @@ +/* + * dummy.h + * + * Copyright 2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#ifndef _DUMMY_H +#define _DUMMY_H + +struct regulator_dev; + +extern struct regulator_dev *dummy_regulator_rdev; + +void __init regulator_dummy_init(void); + +#endif diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c new file mode 100644 index 0000000..f0e1ae5 --- /dev/null +++ b/drivers/regulator/fan53555.c @@ -0,0 +1,320 @@ +/* + * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver. + * + * Supported Part Numbers: + * FAN53555UC00X/01X/03X/04X/05X + * + * Copyright (c) 2012 Marvell Technology Ltd. + * Yunfan Zhang + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Voltage setting */ +#define FAN53555_VSEL0 0x00 +#define FAN53555_VSEL1 0x01 +/* Control register */ +#define FAN53555_CONTROL 0x02 +/* IC Type */ +#define FAN53555_ID1 0x03 +/* IC mask version */ +#define FAN53555_ID2 0x04 +/* Monitor register */ +#define FAN53555_MONITOR 0x05 + +/* VSEL bit definitions */ +#define VSEL_BUCK_EN (1 << 7) +#define VSEL_MODE (1 << 6) +#define VSEL_NSEL_MASK 0x3F +/* Chip ID and Verison */ +#define DIE_ID 0x0F /* ID1 */ +#define DIE_REV 0x0F /* ID2 */ +/* Control bit definitions */ +#define CTL_OUTPUT_DISCHG (1 << 7) +#define CTL_SLEW_MASK (0x7 << 4) +#define CTL_SLEW_SHIFT 4 +#define CTL_RESET (1 << 2) + +#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */ + +/* IC Type */ +enum { + FAN53555_CHIP_ID_00 = 0, + FAN53555_CHIP_ID_01, + FAN53555_CHIP_ID_02, + FAN53555_CHIP_ID_03, + FAN53555_CHIP_ID_04, + FAN53555_CHIP_ID_05, +}; + +struct fan53555_device_info { + struct regmap *regmap; + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regulator_init_data *regulator; + /* IC Type and Rev */ + int chip_id; + int chip_rev; + /* Voltage setting register */ + unsigned int vol_reg; + unsigned int sleep_reg; + /* Voltage range and step(linear) */ + unsigned int vsel_min; + unsigned int vsel_step; + /* Voltage slew rate limiting */ + unsigned int slew_rate; + /* Sleep voltage cache */ + unsigned int sleep_vol_cache; +}; + +static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct fan53555_device_info *di = rdev_get_drvdata(rdev); + int ret; + + if (di->sleep_vol_cache == uV) + return 0; + ret = regulator_map_voltage_linear(rdev, uV, uV); + if (ret < 0) + return -EINVAL; + ret = regmap_update_bits(di->regmap, di->sleep_reg, + VSEL_NSEL_MASK, ret); + if (ret < 0) + return -EINVAL; + /* Cache the sleep voltage setting. + * Might not be the real voltage which is rounded */ + di->sleep_vol_cache = uV; + + return 0; +} + +static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct fan53555_device_info *di = rdev_get_drvdata(rdev); + + switch (mode) { + case REGULATOR_MODE_FAST: + regmap_update_bits(di->regmap, di->vol_reg, + VSEL_MODE, VSEL_MODE); + break; + case REGULATOR_MODE_NORMAL: + regmap_update_bits(di->regmap, di->vol_reg, VSEL_MODE, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned int fan53555_get_mode(struct regulator_dev *rdev) +{ + struct fan53555_device_info *di = rdev_get_drvdata(rdev); + unsigned int val; + int ret = 0; + + ret = regmap_read(di->regmap, di->vol_reg, &val); + if (ret < 0) + return ret; + if (val & VSEL_MODE) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops fan53555_regulator_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .set_suspend_voltage = fan53555_set_suspend_voltage, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_mode = fan53555_set_mode, + .get_mode = fan53555_get_mode, +}; + +/* For 00,01,03,05 options: + * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V. + * For 04 option: + * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V. + * */ +static int fan53555_device_setup(struct fan53555_device_info *di, + struct fan53555_platform_data *pdata) +{ + unsigned int reg, data, mask; + + /* Setup voltage control register */ + switch (pdata->sleep_vsel_id) { + case FAN53555_VSEL_ID_0: + di->sleep_reg = FAN53555_VSEL0; + di->vol_reg = FAN53555_VSEL1; + break; + case FAN53555_VSEL_ID_1: + di->sleep_reg = FAN53555_VSEL1; + di->vol_reg = FAN53555_VSEL0; + break; + default: + dev_err(di->dev, "Invalid VSEL ID!\n"); + return -EINVAL; + } + /* Init voltage range and step */ + switch (di->chip_id) { + case FAN53555_CHIP_ID_00: + case FAN53555_CHIP_ID_01: + case FAN53555_CHIP_ID_03: + case FAN53555_CHIP_ID_05: + di->vsel_min = 600000; + di->vsel_step = 10000; + break; + case FAN53555_CHIP_ID_04: + di->vsel_min = 603000; + di->vsel_step = 12826; + break; + default: + dev_err(di->dev, + "Chip ID[%d]\n not supported!\n", di->chip_id); + return -EINVAL; + } + /* Init slew rate */ + if (pdata->slew_rate & 0x7) + di->slew_rate = pdata->slew_rate; + else + di->slew_rate = FAN53555_SLEW_RATE_64MV; + reg = FAN53555_CONTROL; + data = di->slew_rate << CTL_SLEW_SHIFT; + mask = CTL_SLEW_MASK; + return regmap_update_bits(di->regmap, reg, mask, data); +} + +static int fan53555_regulator_register(struct fan53555_device_info *di, + struct regulator_config *config) +{ + struct regulator_desc *rdesc = &di->desc; + + rdesc->name = "fan53555-reg"; + rdesc->ops = &fan53555_regulator_ops; + rdesc->type = REGULATOR_VOLTAGE; + rdesc->n_voltages = FAN53555_NVOLTAGES; + rdesc->enable_reg = di->vol_reg; + rdesc->enable_mask = VSEL_BUCK_EN; + rdesc->min_uV = di->vsel_min; + rdesc->uV_step = di->vsel_step; + rdesc->vsel_reg = di->vol_reg; + rdesc->vsel_mask = VSEL_NSEL_MASK; + rdesc->owner = THIS_MODULE; + + di->rdev = regulator_register(&di->desc, config); + return PTR_RET(di->rdev); + +} + +static struct regmap_config fan53555_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int fan53555_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fan53555_device_info *di; + struct fan53555_platform_data *pdata; + struct regulator_config config = { }; + unsigned int val; + int ret; + + pdata = client->dev.platform_data; + if (!pdata || !pdata->regulator) { + dev_err(&client->dev, "Platform data not found!\n"); + return -ENODEV; + } + + di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info), + GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "Failed to allocate device info data!\n"); + return -ENOMEM; + } + di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config); + if (IS_ERR(di->regmap)) { + dev_err(&client->dev, "Failed to allocate regmap!\n"); + return PTR_ERR(di->regmap); + } + di->dev = &client->dev; + di->regulator = pdata->regulator; + i2c_set_clientdata(client, di); + /* Get chip ID */ + ret = regmap_read(di->regmap, FAN53555_ID1, &val); + if (ret < 0) { + dev_err(&client->dev, "Failed to get chip ID!\n"); + return -ENODEV; + } + di->chip_id = val & DIE_ID; + /* Get chip revision */ + ret = regmap_read(di->regmap, FAN53555_ID2, &val); + if (ret < 0) { + dev_err(&client->dev, "Failed to get chip Rev!\n"); + return -ENODEV; + } + di->chip_rev = val & DIE_REV; + dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n", + di->chip_id, di->chip_rev); + /* Device init */ + ret = fan53555_device_setup(di, pdata); + if (ret < 0) { + dev_err(&client->dev, "Failed to setup device!\n"); + return ret; + } + /* Register regulator */ + config.dev = di->dev; + config.init_data = di->regulator; + config.regmap = di->regmap; + config.driver_data = di; + ret = fan53555_regulator_register(di, &config); + if (ret < 0) + dev_err(&client->dev, "Failed to register regulator!\n"); + return ret; + +} + +static int fan53555_regulator_remove(struct i2c_client *client) +{ + struct fan53555_device_info *di = i2c_get_clientdata(client); + + regulator_unregister(di->rdev); + return 0; +} + +static const struct i2c_device_id fan53555_id[] = { + {"fan53555", -1}, + { }, +}; + +static struct i2c_driver fan53555_regulator_driver = { + .driver = { + .name = "fan53555-regulator", + }, + .probe = fan53555_regulator_probe, + .remove = fan53555_regulator_remove, + .id_table = fan53555_id, +}; + +module_i2c_driver(fan53555_regulator_driver); + +MODULE_AUTHOR("Yunfan Zhang "); +MODULE_DESCRIPTION("FAN53555 regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c new file mode 100644 index 0000000..f9d0279 --- /dev/null +++ b/drivers/regulator/fixed-helper.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +struct fixed_regulator_data { + struct fixed_voltage_config cfg; + struct regulator_init_data init_data; + struct platform_device pdev; +}; + +static void regulator_fixed_release(struct device *dev) +{ + struct fixed_regulator_data *data = container_of(dev, + struct fixed_regulator_data, pdev.dev); + kfree(data->cfg.supply_name); + kfree(data); +} + +/** + * regulator_register_fixed_name - register a no-op fixed regulator + * @id: platform device id + * @name: name to be used for the regulator + * @supplies: consumers for this regulator + * @num_supplies: number of consumers + * @uv: voltage in microvolts + */ +struct platform_device *regulator_register_always_on(int id, const char *name, + struct regulator_consumer_supply *supplies, int num_supplies, int uv) +{ + struct fixed_regulator_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->cfg.supply_name = kstrdup(name, GFP_KERNEL); + if (!data->cfg.supply_name) { + kfree(data); + return NULL; + } + + data->cfg.microvolts = uv; + data->cfg.gpio = -EINVAL; + data->cfg.enabled_at_boot = 1; + data->cfg.init_data = &data->init_data; + + data->init_data.constraints.always_on = 1; + data->init_data.consumer_supplies = supplies; + data->init_data.num_consumer_supplies = num_supplies; + + data->pdev.name = "reg-fixed-voltage"; + data->pdev.id = id; + data->pdev.dev.platform_data = &data->cfg; + data->pdev.dev.release = regulator_fixed_release; + + platform_device_register(&data->pdev); + + return &data->pdev; +} diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c new file mode 100644 index 0000000..e5c03b5 --- /dev/null +++ b/drivers/regulator/fixed.c @@ -0,0 +1,281 @@ +/* + * fixed.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * Copyright (c) 2009 Nokia Corporation + * Roger Quadros + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fixed_voltage_data { + struct regulator_desc desc; + struct regulator_dev *dev; + int microvolts; +}; + + +/** + * of_get_fixed_voltage_config - extract fixed_voltage_config structure info + * @dev: device requesting for fixed_voltage_config + * + * Populates fixed_voltage_config structure by extracting data from device + * tree node, returns a pointer to the populated structure of NULL if memory + * alloc fails. + */ +static struct fixed_voltage_config * +of_get_fixed_voltage_config(struct device *dev) +{ + struct fixed_voltage_config *config; + struct device_node *np = dev->of_node; + const __be32 *delay; + struct regulator_init_data *init_data; + + config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + config->init_data = of_get_regulator_init_data(dev, dev->of_node); + if (!config->init_data) + return ERR_PTR(-EINVAL); + + init_data = config->init_data; + init_data->constraints.apply_uV = 0; + + config->supply_name = init_data->constraints.name; + if (init_data->constraints.min_uV == init_data->constraints.max_uV) { + config->microvolts = init_data->constraints.min_uV; + } else { + dev_err(dev, + "Fixed regulator specified with variable voltages\n"); + return ERR_PTR(-EINVAL); + } + + if (init_data->constraints.boot_on) + config->enabled_at_boot = true; + + config->gpio = of_get_named_gpio(np, "gpio", 0); + /* + * of_get_named_gpio() currently returns ENODEV rather than + * EPROBE_DEFER. This code attempts to be compatible with both + * for now; the ENODEV check can be removed once the API is fixed. + * of_get_named_gpio() doesn't differentiate between a missing + * property (which would be fine here, since the GPIO is optional) + * and some other error. Patches have been posted for both issues. + * Once they are check in, we should replace this with: + * if (config->gpio < 0 && config->gpio != -ENOENT) + */ + if ((config->gpio == -ENODEV) || (config->gpio == -EPROBE_DEFER)) + return ERR_PTR(-EPROBE_DEFER); + + delay = of_get_property(np, "startup-delay-us", NULL); + if (delay) + config->startup_delay = be32_to_cpu(*delay); + + if (of_find_property(np, "enable-active-high", NULL)) + config->enable_high = true; + + if (of_find_property(np, "gpio-open-drain", NULL)) + config->gpio_is_open_drain = true; + + if (of_find_property(np, "vin-supply", NULL)) + config->input_supply = "vin"; + + return config; +} + +static int fixed_voltage_get_voltage(struct regulator_dev *dev) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + if (data->microvolts) + return data->microvolts; + else + return -EINVAL; +} + +static int fixed_voltage_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + if (selector != 0) + return -EINVAL; + + return data->microvolts; +} + +static struct regulator_ops fixed_voltage_ops = { + .get_voltage = fixed_voltage_get_voltage, + .list_voltage = fixed_voltage_list_voltage, +}; + +static int reg_fixed_voltage_probe(struct platform_device *pdev) +{ + struct fixed_voltage_config *config; + struct fixed_voltage_data *drvdata; + struct regulator_config cfg = { }; + int ret; + + if (pdev->dev.of_node) { + config = of_get_fixed_voltage_config(&pdev->dev); + if (IS_ERR(config)) + return PTR_ERR(config); + } else { + config = pdev->dev.platform_data; + } + + if (!config) + return -ENOMEM; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), + GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&pdev->dev, "Failed to allocate device data\n"); + ret = -ENOMEM; + goto err; + } + + drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + if (drvdata->desc.name == NULL) { + dev_err(&pdev->dev, "Failed to allocate supply name\n"); + ret = -ENOMEM; + goto err; + } + drvdata->desc.type = REGULATOR_VOLTAGE; + drvdata->desc.owner = THIS_MODULE; + drvdata->desc.ops = &fixed_voltage_ops; + + drvdata->desc.enable_time = config->startup_delay; + + if (config->input_supply) { + drvdata->desc.supply_name = kstrdup(config->input_supply, + GFP_KERNEL); + if (!drvdata->desc.supply_name) { + dev_err(&pdev->dev, + "Failed to allocate input supply\n"); + ret = -ENOMEM; + goto err_name; + } + } + + if (config->microvolts) + drvdata->desc.n_voltages = 1; + + drvdata->microvolts = config->microvolts; + + if (config->gpio >= 0) + cfg.ena_gpio = config->gpio; + cfg.ena_gpio_invert = !config->enable_high; + if (config->enabled_at_boot) { + if (config->enable_high) { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + } else { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + } + } else { + if (config->enable_high) { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + } else { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + } + } + if (config->gpio_is_open_drain) + cfg.ena_gpio_flags |= GPIOF_OPEN_DRAIN; + + cfg.dev = &pdev->dev; + cfg.init_data = config->init_data; + cfg.driver_data = drvdata; + cfg.of_node = pdev->dev.of_node; + + drvdata->dev = regulator_register(&drvdata->desc, &cfg); + if (IS_ERR(drvdata->dev)) { + ret = PTR_ERR(drvdata->dev); + dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + goto err_input; + } + + platform_set_drvdata(pdev, drvdata); + + dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, + drvdata->microvolts); + + return 0; + +err_input: + kfree(drvdata->desc.supply_name); +err_name: + kfree(drvdata->desc.name); +err: + return ret; +} + +static int reg_fixed_voltage_remove(struct platform_device *pdev) +{ + struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); + + regulator_unregister(drvdata->dev); + kfree(drvdata->desc.supply_name); + kfree(drvdata->desc.name); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id fixed_of_match[] = { + { .compatible = "regulator-fixed", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fixed_of_match); +#endif + +static struct platform_driver regulator_fixed_voltage_driver = { + .probe = reg_fixed_voltage_probe, + .remove = reg_fixed_voltage_remove, + .driver = { + .name = "reg-fixed-voltage", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fixed_of_match), + }, +}; + +static int __init regulator_fixed_voltage_init(void) +{ + return platform_driver_register(®ulator_fixed_voltage_driver); +} +subsys_initcall(regulator_fixed_voltage_init); + +static void __exit regulator_fixed_voltage_exit(void) +{ + platform_driver_unregister(®ulator_fixed_voltage_driver); +} +module_exit(regulator_fixed_voltage_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Fixed voltage regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:reg-fixed-voltage"); diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c new file mode 100644 index 0000000..9d39eb4 --- /dev/null +++ b/drivers/regulator/gpio-regulator.c @@ -0,0 +1,396 @@ +/* + * gpio-regulator.c + * + * Copyright 2011 Heiko Stuebner + * + * based on fixed.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * Copyright (c) 2009 Nokia Corporation + * Roger Quadros + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_regulator_data { + struct regulator_desc desc; + struct regulator_dev *dev; + + struct gpio *gpios; + int nr_gpios; + + struct gpio_regulator_state *states; + int nr_states; + + int state; +}; + +static int gpio_regulator_get_value(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr; + + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].gpios == data->state) + return data->states[ptr].value; + + return -EINVAL; +} + +static int gpio_regulator_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, + unsigned *selector) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr, target = 0, state, best_val = INT_MAX; + + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].value < best_val && + data->states[ptr].value >= min_uV && + data->states[ptr].value <= max_uV) { + target = data->states[ptr].gpios; + best_val = data->states[ptr].value; + if (selector) + *selector = ptr; + } + + if (best_val == INT_MAX) + return -EINVAL; + + for (ptr = 0; ptr < data->nr_gpios; ptr++) { + state = (target & (1 << ptr)) >> ptr; + gpio_set_value_cansleep(data->gpios[ptr].gpio, state); + } + data->state = target; + + return 0; +} + +static int gpio_regulator_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (selector >= data->nr_states) + return -EINVAL; + + return data->states[selector].value; +} + +static int gpio_regulator_set_current_limit(struct regulator_dev *dev, + int min_uA, int max_uA) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr, target = 0, state, best_val = 0; + + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].value > best_val && + data->states[ptr].value >= min_uA && + data->states[ptr].value <= max_uA) { + target = data->states[ptr].gpios; + best_val = data->states[ptr].value; + } + + if (best_val == 0) + return -EINVAL; + + for (ptr = 0; ptr < data->nr_gpios; ptr++) { + state = (target & (1 << ptr)) >> ptr; + gpio_set_value_cansleep(data->gpios[ptr].gpio, state); + } + data->state = target; + + return 0; +} + +static struct regulator_ops gpio_regulator_voltage_ops = { + .get_voltage = gpio_regulator_get_value, + .set_voltage = gpio_regulator_set_voltage, + .list_voltage = gpio_regulator_list_voltage, +}; + +static struct gpio_regulator_config * +of_get_gpio_regulator_config(struct device *dev, struct device_node *np) +{ + struct gpio_regulator_config *config; + struct property *prop; + const char *regtype; + int proplen, gpio, i; + + config = devm_kzalloc(dev, + sizeof(struct gpio_regulator_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + config->init_data = of_get_regulator_init_data(dev, np); + if (!config->init_data) + return ERR_PTR(-EINVAL); + + config->supply_name = config->init_data->constraints.name; + + if (of_property_read_bool(np, "enable-active-high")) + config->enable_high = true; + + if (of_property_read_bool(np, "enable-at-boot")) + config->enabled_at_boot = true; + + of_property_read_u32(np, "startup-delay-us", &config->startup_delay); + + config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + + /* Fetch GPIOs. */ + config->nr_gpios = of_gpio_count(np); + + config->gpios = devm_kzalloc(dev, + sizeof(struct gpio) * config->nr_gpios, + GFP_KERNEL); + if (!config->gpios) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < config->nr_gpios; i++) { + gpio = of_get_named_gpio(np, "gpios", i); + if (gpio < 0) + break; + config->gpios[i].gpio = gpio; + } + + /* Fetch states. */ + prop = of_find_property(np, "states", NULL); + if (!prop) { + dev_err(dev, "No 'states' property found\n"); + return ERR_PTR(-EINVAL); + } + + proplen = prop->length / sizeof(int); + + config->states = devm_kzalloc(dev, + sizeof(struct gpio_regulator_state) + * (proplen / 2), + GFP_KERNEL); + if (!config->states) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < proplen / 2; i++) { + config->states[i].value = + be32_to_cpup((int *)prop->value + (i * 2)); + config->states[i].gpios = + be32_to_cpup((int *)prop->value + (i * 2 + 1)); + } + config->nr_states = i; + + of_property_read_string(np, "regulator-type", ®type); + + if (!strncmp("voltage", regtype, 7)) + config->type = REGULATOR_VOLTAGE; + else if (!strncmp("current", regtype, 7)) + config->type = REGULATOR_CURRENT; + + return config; +} + +static struct regulator_ops gpio_regulator_current_ops = { + .get_current_limit = gpio_regulator_get_value, + .set_current_limit = gpio_regulator_set_current_limit, +}; + +static int gpio_regulator_probe(struct platform_device *pdev) +{ + struct gpio_regulator_config *config = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + struct gpio_regulator_data *drvdata; + struct regulator_config cfg = { }; + int ptr, ret, state; + + if (np) { + config = of_get_gpio_regulator_config(&pdev->dev, np); + if (IS_ERR(config)) + return PTR_ERR(config); + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), + GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&pdev->dev, "Failed to allocate device data\n"); + return -ENOMEM; + } + + drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + if (drvdata->desc.name == NULL) { + dev_err(&pdev->dev, "Failed to allocate supply name\n"); + ret = -ENOMEM; + goto err; + } + + drvdata->gpios = kmemdup(config->gpios, + config->nr_gpios * sizeof(struct gpio), + GFP_KERNEL); + if (drvdata->gpios == NULL) { + dev_err(&pdev->dev, "Failed to allocate gpio data\n"); + ret = -ENOMEM; + goto err_name; + } + + drvdata->states = kmemdup(config->states, + config->nr_states * + sizeof(struct gpio_regulator_state), + GFP_KERNEL); + if (drvdata->states == NULL) { + dev_err(&pdev->dev, "Failed to allocate state data\n"); + ret = -ENOMEM; + goto err_memgpio; + } + drvdata->nr_states = config->nr_states; + + drvdata->desc.owner = THIS_MODULE; + drvdata->desc.enable_time = config->startup_delay; + + /* handle regulator type*/ + switch (config->type) { + case REGULATOR_VOLTAGE: + drvdata->desc.type = REGULATOR_VOLTAGE; + drvdata->desc.ops = &gpio_regulator_voltage_ops; + drvdata->desc.n_voltages = config->nr_states; + break; + case REGULATOR_CURRENT: + drvdata->desc.type = REGULATOR_CURRENT; + drvdata->desc.ops = &gpio_regulator_current_ops; + break; + default: + dev_err(&pdev->dev, "No regulator type set\n"); + ret = -EINVAL; + goto err_memgpio; + break; + } + + drvdata->nr_gpios = config->nr_gpios; + ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator setting GPIOs: %d\n", ret); + goto err_memstate; + } + + /* build initial state from gpio init data. */ + state = 0; + for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { + if (config->gpios[ptr].flags & GPIOF_OUT_INIT_HIGH) + state |= (1 << ptr); + } + drvdata->state = state; + + cfg.dev = &pdev->dev; + cfg.init_data = config->init_data; + cfg.driver_data = drvdata; + cfg.of_node = np; + + if (config->enable_gpio >= 0) + cfg.ena_gpio = config->enable_gpio; + cfg.ena_gpio_invert = !config->enable_high; + if (config->enabled_at_boot) { + if (config->enable_high) + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + else + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + } else { + if (config->enable_high) + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + else + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + } + + drvdata->dev = regulator_register(&drvdata->desc, &cfg); + if (IS_ERR(drvdata->dev)) { + ret = PTR_ERR(drvdata->dev); + dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + goto err_stategpio; + } + + platform_set_drvdata(pdev, drvdata); + + return 0; + +err_stategpio: + gpio_free_array(drvdata->gpios, drvdata->nr_gpios); +err_memstate: + kfree(drvdata->states); +err_memgpio: + kfree(drvdata->gpios); +err_name: + kfree(drvdata->desc.name); +err: + return ret; +} + +static int gpio_regulator_remove(struct platform_device *pdev) +{ + struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev); + + regulator_unregister(drvdata->dev); + + gpio_free_array(drvdata->gpios, drvdata->nr_gpios); + + kfree(drvdata->states); + kfree(drvdata->gpios); + + kfree(drvdata->desc.name); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id regulator_gpio_of_match[] = { + { .compatible = "regulator-gpio", }, + {}, +}; +#endif + +static struct platform_driver gpio_regulator_driver = { + .probe = gpio_regulator_probe, + .remove = gpio_regulator_remove, + .driver = { + .name = "gpio-regulator", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(regulator_gpio_of_match), + }, +}; + +static int __init gpio_regulator_init(void) +{ + return platform_driver_register(&gpio_regulator_driver); +} +subsys_initcall(gpio_regulator_init); + +static void __exit gpio_regulator_exit(void) +{ + platform_driver_unregister(&gpio_regulator_driver); +} +module_exit(gpio_regulator_exit); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("gpio voltage regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-regulator"); diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c new file mode 100644 index 0000000..d1e5bee --- /dev/null +++ b/drivers/regulator/isl6271a-regulator.c @@ -0,0 +1,196 @@ +/* + * isl6271a-regulator.c + * + * Support for Intersil ISL6271A voltage regulator + * + * Copyright (C) 2010 Marek Vasut + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISL6271A_VOLTAGE_MIN 850000 +#define ISL6271A_VOLTAGE_MAX 1600000 +#define ISL6271A_VOLTAGE_STEP 50000 + +/* PMIC details */ +struct isl_pmic { + struct i2c_client *client; + struct regulator_dev *rdev[3]; + struct mutex mtx; +}; + +static int isl6271a_get_voltage_sel(struct regulator_dev *dev) +{ + struct isl_pmic *pmic = rdev_get_drvdata(dev); + int idx; + + mutex_lock(&pmic->mtx); + + idx = i2c_smbus_read_byte(pmic->client); + if (idx < 0) + dev_err(&pmic->client->dev, "Error getting voltage\n"); + + mutex_unlock(&pmic->mtx); + return idx; +} + +static int isl6271a_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct isl_pmic *pmic = rdev_get_drvdata(dev); + int err; + + mutex_lock(&pmic->mtx); + + err = i2c_smbus_write_byte(pmic->client, selector); + if (err < 0) + dev_err(&pmic->client->dev, "Error setting voltage\n"); + + mutex_unlock(&pmic->mtx); + return err; +} + +static struct regulator_ops isl_core_ops = { + .get_voltage_sel = isl6271a_get_voltage_sel, + .set_voltage_sel = isl6271a_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static struct regulator_ops isl_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static const struct regulator_desc isl_rd[] = { + { + .name = "Core Buck", + .id = 0, + .n_voltages = 16, + .ops = &isl_core_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = ISL6271A_VOLTAGE_MIN, + .uV_step = ISL6271A_VOLTAGE_STEP, + }, { + .name = "LDO1", + .id = 1, + .n_voltages = 1, + .ops = &isl_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = 1100000, + }, { + .name = "LDO2", + .id = 2, + .n_voltages = 1, + .ops = &isl_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .min_uV = 1300000, + }, +}; + +static int isl6271a_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_config config = { }; + struct regulator_init_data *init_data = i2c->dev.platform_data; + struct isl_pmic *pmic; + int err, i; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + pmic = devm_kzalloc(&i2c->dev, sizeof(struct isl_pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + pmic->client = i2c; + + mutex_init(&pmic->mtx); + + for (i = 0; i < 3; i++) { + config.dev = &i2c->dev; + if (i == 0) + config.init_data = init_data; + else + config.init_data = 0; + config.driver_data = pmic; + + pmic->rdev[i] = regulator_register(&isl_rd[i], &config); + if (IS_ERR(pmic->rdev[i])) { + dev_err(&i2c->dev, "failed to register %s\n", id->name); + err = PTR_ERR(pmic->rdev[i]); + goto error; + } + } + + i2c_set_clientdata(i2c, pmic); + + return 0; + +error: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + return err; +} + +static int isl6271a_remove(struct i2c_client *i2c) +{ + struct isl_pmic *pmic = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < 3; i++) + regulator_unregister(pmic->rdev[i]); + return 0; +} + +static const struct i2c_device_id isl6271a_id[] = { + {.name = "isl6271a", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, isl6271a_id); + +static struct i2c_driver isl6271a_i2c_driver = { + .driver = { + .name = "isl6271a", + .owner = THIS_MODULE, + }, + .probe = isl6271a_probe, + .remove = isl6271a_remove, + .id_table = isl6271a_id, +}; + +static int __init isl6271a_init(void) +{ + return i2c_add_driver(&isl6271a_i2c_driver); +} + +static void __exit isl6271a_cleanup(void) +{ + i2c_del_driver(&isl6271a_i2c_driver); +} + +subsys_initcall(isl6271a_init); +module_exit(isl6271a_cleanup); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c new file mode 100644 index 0000000..d8af9e7 --- /dev/null +++ b/drivers/regulator/lp3971.c @@ -0,0 +1,501 @@ +/* + * Regulator driver for National Semiconductors LP3971 PMIC chip + * + * Copyright (C) 2009 Samsung Electronics + * Author: Marek Szyprowski + * + * Based on wm8350.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lp3971 { + struct device *dev; + struct mutex io_lock; + struct i2c_client *i2c; + int num_regulators; + struct regulator_dev **rdev; +}; + +static u8 lp3971_reg_read(struct lp3971 *lp3971, u8 reg); +static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val); + +#define LP3971_SYS_CONTROL1_REG 0x07 + +/* System control register 1 initial value, + bits 4 and 5 are EPROM programmable */ +#define SYS_CONTROL1_INIT_VAL 0x40 +#define SYS_CONTROL1_INIT_MASK 0xCF + +#define LP3971_BUCK_VOL_ENABLE_REG 0x10 +#define LP3971_BUCK_VOL_CHANGE_REG 0x20 + +/* Voltage control registers shift: + LP3971_BUCK1 -> 0 + LP3971_BUCK2 -> 4 + LP3971_BUCK3 -> 6 +*/ +#define BUCK_VOL_CHANGE_SHIFT(x) (((!!x) << 2) | (x & ~0x01)) +#define BUCK_VOL_CHANGE_FLAG_GO 0x01 +#define BUCK_VOL_CHANGE_FLAG_TARGET 0x02 +#define BUCK_VOL_CHANGE_FLAG_MASK 0x03 + +#define LP3971_BUCK1_BASE 0x23 +#define LP3971_BUCK2_BASE 0x29 +#define LP3971_BUCK3_BASE 0x32 + +static const int buck_base_addr[] = { + LP3971_BUCK1_BASE, + LP3971_BUCK2_BASE, + LP3971_BUCK3_BASE, +}; + +#define LP3971_BUCK_TARGET_VOL1_REG(x) (buck_base_addr[x]) +#define LP3971_BUCK_TARGET_VOL2_REG(x) (buck_base_addr[x]+1) + +static const unsigned int buck_voltage_map[] = { + 0, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1800000, 1900000, 2500000, 2800000, + 3000000, 3300000, +}; + +#define BUCK_TARGET_VOL_MASK 0x3f + +#define LP3971_BUCK_RAMP_REG(x) (buck_base_addr[x]+2) + +#define LP3971_LDO_ENABLE_REG 0x12 +#define LP3971_LDO_VOL_CONTR_BASE 0x39 + +/* Voltage control registers: + LP3971_LDO1 -> LP3971_LDO_VOL_CONTR_BASE + 0 + LP3971_LDO2 -> LP3971_LDO_VOL_CONTR_BASE + 0 + LP3971_LDO3 -> LP3971_LDO_VOL_CONTR_BASE + 1 + LP3971_LDO4 -> LP3971_LDO_VOL_CONTR_BASE + 1 + LP3971_LDO5 -> LP3971_LDO_VOL_CONTR_BASE + 2 +*/ +#define LP3971_LDO_VOL_CONTR_REG(x) (LP3971_LDO_VOL_CONTR_BASE + (x >> 1)) + +/* Voltage control registers shift: + LP3971_LDO1 -> 0, LP3971_LDO2 -> 4 + LP3971_LDO3 -> 0, LP3971_LDO4 -> 4 + LP3971_LDO5 -> 0 +*/ +#define LDO_VOL_CONTR_SHIFT(x) ((x & 1) << 2) +#define LDO_VOL_CONTR_MASK 0x0f + +static const unsigned int ldo45_voltage_map[] = { + 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000, 1350000, + 1400000, 1500000, 1800000, 1900000, 2500000, 2800000, 3000000, 3300000, +}; + +static const unsigned int ldo123_voltage_map[] = { + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, +}; + +#define LDO_VOL_MIN_IDX 0x00 +#define LDO_VOL_MAX_IDX 0x0f + +static int lp3971_ldo_is_enabled(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3971_LDO1; + u16 mask = 1 << (1 + ldo); + u16 val; + + val = lp3971_reg_read(lp3971, LP3971_LDO_ENABLE_REG); + return (val & mask) != 0; +} + +static int lp3971_ldo_enable(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3971_LDO1; + u16 mask = 1 << (1 + ldo); + + return lp3971_set_bits(lp3971, LP3971_LDO_ENABLE_REG, mask, mask); +} + +static int lp3971_ldo_disable(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3971_LDO1; + u16 mask = 1 << (1 + ldo); + + return lp3971_set_bits(lp3971, LP3971_LDO_ENABLE_REG, mask, 0); +} + +static int lp3971_ldo_get_voltage_sel(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3971_LDO1; + u16 val, reg; + + reg = lp3971_reg_read(lp3971, LP3971_LDO_VOL_CONTR_REG(ldo)); + val = (reg >> LDO_VOL_CONTR_SHIFT(ldo)) & LDO_VOL_CONTR_MASK; + + return val; +} + +static int lp3971_ldo_set_voltage_sel(struct regulator_dev *dev, + unsigned int selector) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3971_LDO1; + + return lp3971_set_bits(lp3971, LP3971_LDO_VOL_CONTR_REG(ldo), + LDO_VOL_CONTR_MASK << LDO_VOL_CONTR_SHIFT(ldo), + selector << LDO_VOL_CONTR_SHIFT(ldo)); +} + +static struct regulator_ops lp3971_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .is_enabled = lp3971_ldo_is_enabled, + .enable = lp3971_ldo_enable, + .disable = lp3971_ldo_disable, + .get_voltage_sel = lp3971_ldo_get_voltage_sel, + .set_voltage_sel = lp3971_ldo_set_voltage_sel, +}; + +static int lp3971_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3971_DCDC1; + u16 mask = 1 << (buck * 2); + u16 val; + + val = lp3971_reg_read(lp3971, LP3971_BUCK_VOL_ENABLE_REG); + return (val & mask) != 0; +} + +static int lp3971_dcdc_enable(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3971_DCDC1; + u16 mask = 1 << (buck * 2); + + return lp3971_set_bits(lp3971, LP3971_BUCK_VOL_ENABLE_REG, mask, mask); +} + +static int lp3971_dcdc_disable(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3971_DCDC1; + u16 mask = 1 << (buck * 2); + + return lp3971_set_bits(lp3971, LP3971_BUCK_VOL_ENABLE_REG, mask, 0); +} + +static int lp3971_dcdc_get_voltage_sel(struct regulator_dev *dev) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3971_DCDC1; + u16 reg; + + reg = lp3971_reg_read(lp3971, LP3971_BUCK_TARGET_VOL1_REG(buck)); + reg &= BUCK_TARGET_VOL_MASK; + + return reg; +} + +static int lp3971_dcdc_set_voltage_sel(struct regulator_dev *dev, + unsigned int selector) +{ + struct lp3971 *lp3971 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3971_DCDC1; + int ret; + + ret = lp3971_set_bits(lp3971, LP3971_BUCK_TARGET_VOL1_REG(buck), + BUCK_TARGET_VOL_MASK, selector); + if (ret) + return ret; + + ret = lp3971_set_bits(lp3971, LP3971_BUCK_VOL_CHANGE_REG, + BUCK_VOL_CHANGE_FLAG_MASK << BUCK_VOL_CHANGE_SHIFT(buck), + BUCK_VOL_CHANGE_FLAG_GO << BUCK_VOL_CHANGE_SHIFT(buck)); + if (ret) + return ret; + + return lp3971_set_bits(lp3971, LP3971_BUCK_VOL_CHANGE_REG, + BUCK_VOL_CHANGE_FLAG_MASK << BUCK_VOL_CHANGE_SHIFT(buck), + 0 << BUCK_VOL_CHANGE_SHIFT(buck)); +} + +static struct regulator_ops lp3971_dcdc_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .is_enabled = lp3971_dcdc_is_enabled, + .enable = lp3971_dcdc_enable, + .disable = lp3971_dcdc_disable, + .get_voltage_sel = lp3971_dcdc_get_voltage_sel, + .set_voltage_sel = lp3971_dcdc_set_voltage_sel, +}; + +static const struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = LP3971_LDO1, + .ops = &lp3971_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo123_voltage_map), + .volt_table = ldo123_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = LP3971_LDO2, + .ops = &lp3971_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo123_voltage_map), + .volt_table = ldo123_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = LP3971_LDO3, + .ops = &lp3971_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo123_voltage_map), + .volt_table = ldo123_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = LP3971_LDO4, + .ops = &lp3971_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo45_voltage_map), + .volt_table = ldo45_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO5", + .id = LP3971_LDO5, + .ops = &lp3971_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo45_voltage_map), + .volt_table = ldo45_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC1", + .id = LP3971_DCDC1, + .ops = &lp3971_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .volt_table = buck_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = LP3971_DCDC2, + .ops = &lp3971_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .volt_table = buck_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC3", + .id = LP3971_DCDC3, + .ops = &lp3971_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck_voltage_map), + .volt_table = buck_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count, + u16 *dest) +{ + int ret; + + if (count != 1) + return -EIO; + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret < 0) + return -EIO; + + *dest = ret; + return 0; +} + +static int lp3971_i2c_write(struct i2c_client *i2c, char reg, int count, + const u16 *src) +{ + if (count != 1) + return -EIO; + return i2c_smbus_write_byte_data(i2c, reg, *src); +} + +static u8 lp3971_reg_read(struct lp3971 *lp3971, u8 reg) +{ + u16 val = 0; + + mutex_lock(&lp3971->io_lock); + + lp3971_i2c_read(lp3971->i2c, reg, 1, &val); + + dev_dbg(lp3971->dev, "reg read 0x%02x -> 0x%02x\n", (int)reg, + (unsigned)val&0xff); + + mutex_unlock(&lp3971->io_lock); + + return val & 0xff; +} + +static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val) +{ + u16 tmp; + int ret; + + mutex_lock(&lp3971->io_lock); + + ret = lp3971_i2c_read(lp3971->i2c, reg, 1, &tmp); + tmp = (tmp & ~mask) | val; + if (ret == 0) { + ret = lp3971_i2c_write(lp3971->i2c, reg, 1, &tmp); + dev_dbg(lp3971->dev, "reg write 0x%02x -> 0x%02x\n", (int)reg, + (unsigned)val&0xff); + } + mutex_unlock(&lp3971->io_lock); + + return ret; +} + +static int setup_regulators(struct lp3971 *lp3971, + struct lp3971_platform_data *pdata) +{ + int i, err; + + lp3971->num_regulators = pdata->num_regulators; + lp3971->rdev = kcalloc(pdata->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); + if (!lp3971->rdev) { + err = -ENOMEM; + goto err_nomem; + } + + /* Instantiate the regulators */ + for (i = 0; i < pdata->num_regulators; i++) { + struct regulator_config config = { }; + struct lp3971_regulator_subdev *reg = &pdata->regulators[i]; + + config.dev = lp3971->dev; + config.init_data = reg->initdata; + config.driver_data = lp3971; + + lp3971->rdev[i] = regulator_register(®ulators[reg->id], + &config); + if (IS_ERR(lp3971->rdev[i])) { + err = PTR_ERR(lp3971->rdev[i]); + dev_err(lp3971->dev, "regulator init failed: %d\n", + err); + goto error; + } + } + + return 0; + +error: + while (--i >= 0) + regulator_unregister(lp3971->rdev[i]); + kfree(lp3971->rdev); + lp3971->rdev = NULL; +err_nomem: + return err; +} + +static int lp3971_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lp3971 *lp3971; + struct lp3971_platform_data *pdata = i2c->dev.platform_data; + int ret; + u16 val; + + if (!pdata) { + dev_dbg(&i2c->dev, "No platform init data supplied\n"); + return -ENODEV; + } + + lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); + if (lp3971 == NULL) + return -ENOMEM; + + lp3971->i2c = i2c; + lp3971->dev = &i2c->dev; + + mutex_init(&lp3971->io_lock); + + /* Detect LP3971 */ + ret = lp3971_i2c_read(i2c, LP3971_SYS_CONTROL1_REG, 1, &val); + if (ret == 0 && (val & SYS_CONTROL1_INIT_MASK) != SYS_CONTROL1_INIT_VAL) + ret = -ENODEV; + if (ret < 0) { + dev_err(&i2c->dev, "failed to detect device\n"); + goto err_detect; + } + + ret = setup_regulators(lp3971, pdata); + if (ret < 0) + goto err_detect; + + i2c_set_clientdata(i2c, lp3971); + return 0; + +err_detect: + kfree(lp3971); + return ret; +} + +static int lp3971_i2c_remove(struct i2c_client *i2c) +{ + struct lp3971 *lp3971 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < lp3971->num_regulators; i++) + regulator_unregister(lp3971->rdev[i]); + + kfree(lp3971->rdev); + kfree(lp3971); + + return 0; +} + +static const struct i2c_device_id lp3971_i2c_id[] = { + { "lp3971", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp3971_i2c_id); + +static struct i2c_driver lp3971_i2c_driver = { + .driver = { + .name = "LP3971", + .owner = THIS_MODULE, + }, + .probe = lp3971_i2c_probe, + .remove = lp3971_i2c_remove, + .id_table = lp3971_i2c_id, +}; + +module_i2c_driver(lp3971_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Szyprowski "); +MODULE_DESCRIPTION("LP3971 PMIC driver"); diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c new file mode 100644 index 0000000..61e4cf9 --- /dev/null +++ b/drivers/regulator/lp3972.c @@ -0,0 +1,607 @@ +/* + * Regulator driver for National Semiconductors LP3972 PMIC chip + * + * Based on lp3971.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lp3972 { + struct device *dev; + struct mutex io_lock; + struct i2c_client *i2c; + int num_regulators; + struct regulator_dev **rdev; +}; + +/* LP3972 Control Registers */ +#define LP3972_SCR_REG 0x07 +#define LP3972_OVER1_REG 0x10 +#define LP3972_OVSR1_REG 0x11 +#define LP3972_OVER2_REG 0x12 +#define LP3972_OVSR2_REG 0x13 +#define LP3972_VCC1_REG 0x20 +#define LP3972_ADTV1_REG 0x23 +#define LP3972_ADTV2_REG 0x24 +#define LP3972_AVRC_REG 0x25 +#define LP3972_CDTC1_REG 0x26 +#define LP3972_CDTC2_REG 0x27 +#define LP3972_SDTV1_REG 0x29 +#define LP3972_SDTV2_REG 0x2A +#define LP3972_MDTV1_REG 0x32 +#define LP3972_MDTV2_REG 0x33 +#define LP3972_L2VCR_REG 0x39 +#define LP3972_L34VCR_REG 0x3A +#define LP3972_SCR1_REG 0x80 +#define LP3972_SCR2_REG 0x81 +#define LP3972_OEN3_REG 0x82 +#define LP3972_OSR3_REG 0x83 +#define LP3972_LOER4_REG 0x84 +#define LP3972_B2TV_REG 0x85 +#define LP3972_B3TV_REG 0x86 +#define LP3972_B32RC_REG 0x87 +#define LP3972_ISRA_REG 0x88 +#define LP3972_BCCR_REG 0x89 +#define LP3972_II1RR_REG 0x8E +#define LP3972_II2RR_REG 0x8F + +#define LP3972_SYS_CONTROL1_REG LP3972_SCR1_REG +/* System control register 1 initial value, + * bits 5, 6 and 7 are EPROM programmable */ +#define SYS_CONTROL1_INIT_VAL 0x02 +#define SYS_CONTROL1_INIT_MASK 0x1F + +#define LP3972_VOL_CHANGE_REG LP3972_VCC1_REG +#define LP3972_VOL_CHANGE_FLAG_GO 0x01 +#define LP3972_VOL_CHANGE_FLAG_MASK 0x03 + +/* LDO output enable mask */ +#define LP3972_OEN3_L1EN BIT(0) +#define LP3972_OVER2_LDO2_EN BIT(2) +#define LP3972_OVER2_LDO3_EN BIT(3) +#define LP3972_OVER2_LDO4_EN BIT(4) +#define LP3972_OVER1_S_EN BIT(2) + +static const unsigned int ldo1_voltage_map[] = { + 1700000, 1725000, 1750000, 1775000, 1800000, 1825000, 1850000, 1875000, + 1900000, 1925000, 1950000, 1975000, 2000000, +}; + +static const unsigned int ldo23_voltage_map[] = { + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, +}; + +static const unsigned int ldo4_voltage_map[] = { + 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000, 1350000, + 1400000, 1500000, 1800000, 1900000, 2500000, 2800000, 3000000, 3300000, +}; + +static const unsigned int ldo5_voltage_map[] = { + 0, 0, 0, 0, 0, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, +}; + +static const unsigned int buck1_voltage_map[] = { + 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, +}; + +static const unsigned int buck23_voltage_map[] = { + 0, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1800000, 1900000, 2500000, 2800000, + 3000000, 3300000, +}; + +static const int ldo_output_enable_mask[] = { + LP3972_OEN3_L1EN, + LP3972_OVER2_LDO2_EN, + LP3972_OVER2_LDO3_EN, + LP3972_OVER2_LDO4_EN, + LP3972_OVER1_S_EN, +}; + +static const int ldo_output_enable_addr[] = { + LP3972_OEN3_REG, + LP3972_OVER2_REG, + LP3972_OVER2_REG, + LP3972_OVER2_REG, + LP3972_OVER1_REG, +}; + +static const int ldo_vol_ctl_addr[] = { + LP3972_MDTV1_REG, + LP3972_L2VCR_REG, + LP3972_L34VCR_REG, + LP3972_L34VCR_REG, + LP3972_SDTV1_REG, +}; + +static const int buck_vol_enable_addr[] = { + LP3972_OVER1_REG, + LP3972_OEN3_REG, + LP3972_OEN3_REG, +}; + +static const int buck_base_addr[] = { + LP3972_ADTV1_REG, + LP3972_B2TV_REG, + LP3972_B3TV_REG, +}; + +#define LP3972_LDO_OUTPUT_ENABLE_MASK(x) (ldo_output_enable_mask[x]) +#define LP3972_LDO_OUTPUT_ENABLE_REG(x) (ldo_output_enable_addr[x]) + +/* LDO voltage control registers shift: + LP3972_LDO1 -> 0, LP3972_LDO2 -> 4 + LP3972_LDO3 -> 0, LP3972_LDO4 -> 4 + LP3972_LDO5 -> 0 +*/ +#define LP3972_LDO_VOL_CONTR_SHIFT(x) (((x) & 1) << 2) +#define LP3972_LDO_VOL_CONTR_REG(x) (ldo_vol_ctl_addr[x]) +#define LP3972_LDO_VOL_CHANGE_SHIFT(x) ((x) ? 4 : 6) + +#define LP3972_LDO_VOL_MASK(x) (((x) % 4) ? 0x0f : 0x1f) +#define LP3972_LDO_VOL_MIN_IDX(x) (((x) == 4) ? 0x05 : 0x00) +#define LP3972_LDO_VOL_MAX_IDX(x) ((x) ? (((x) == 4) ? 0x1f : 0x0f) : 0x0c) + +#define LP3972_BUCK_VOL_ENABLE_REG(x) (buck_vol_enable_addr[x]) +#define LP3972_BUCK_VOL1_REG(x) (buck_base_addr[x]) +#define LP3972_BUCK_VOL_MASK 0x1f + +static int lp3972_i2c_read(struct i2c_client *i2c, char reg, int count, + u16 *dest) +{ + int ret; + + if (count != 1) + return -EIO; + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret < 0) + return ret; + + *dest = ret; + return 0; +} + +static int lp3972_i2c_write(struct i2c_client *i2c, char reg, int count, + const u16 *src) +{ + if (count != 1) + return -EIO; + return i2c_smbus_write_byte_data(i2c, reg, *src); +} + +static u8 lp3972_reg_read(struct lp3972 *lp3972, u8 reg) +{ + u16 val = 0; + + mutex_lock(&lp3972->io_lock); + + lp3972_i2c_read(lp3972->i2c, reg, 1, &val); + + dev_dbg(lp3972->dev, "reg read 0x%02x -> 0x%02x\n", (int)reg, + (unsigned)val & 0xff); + + mutex_unlock(&lp3972->io_lock); + + return val & 0xff; +} + +static int lp3972_set_bits(struct lp3972 *lp3972, u8 reg, u16 mask, u16 val) +{ + u16 tmp; + int ret; + + mutex_lock(&lp3972->io_lock); + + ret = lp3972_i2c_read(lp3972->i2c, reg, 1, &tmp); + tmp = (tmp & ~mask) | val; + if (ret == 0) { + ret = lp3972_i2c_write(lp3972->i2c, reg, 1, &tmp); + dev_dbg(lp3972->dev, "reg write 0x%02x -> 0x%02x\n", (int)reg, + (unsigned)val & 0xff); + } + mutex_unlock(&lp3972->io_lock); + + return ret; +} + +static int lp3972_ldo_is_enabled(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3972_LDO1; + u16 mask = LP3972_LDO_OUTPUT_ENABLE_MASK(ldo); + u16 val; + + val = lp3972_reg_read(lp3972, LP3972_LDO_OUTPUT_ENABLE_REG(ldo)); + return !!(val & mask); +} + +static int lp3972_ldo_enable(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3972_LDO1; + u16 mask = LP3972_LDO_OUTPUT_ENABLE_MASK(ldo); + + return lp3972_set_bits(lp3972, LP3972_LDO_OUTPUT_ENABLE_REG(ldo), + mask, mask); +} + +static int lp3972_ldo_disable(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3972_LDO1; + u16 mask = LP3972_LDO_OUTPUT_ENABLE_MASK(ldo); + + return lp3972_set_bits(lp3972, LP3972_LDO_OUTPUT_ENABLE_REG(ldo), + mask, 0); +} + +static int lp3972_ldo_get_voltage_sel(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3972_LDO1; + u16 mask = LP3972_LDO_VOL_MASK(ldo); + u16 val, reg; + + reg = lp3972_reg_read(lp3972, LP3972_LDO_VOL_CONTR_REG(ldo)); + val = (reg >> LP3972_LDO_VOL_CONTR_SHIFT(ldo)) & mask; + + return val; +} + +static int lp3972_ldo_set_voltage_sel(struct regulator_dev *dev, + unsigned int selector) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev) - LP3972_LDO1; + int shift, ret; + + shift = LP3972_LDO_VOL_CONTR_SHIFT(ldo); + ret = lp3972_set_bits(lp3972, LP3972_LDO_VOL_CONTR_REG(ldo), + LP3972_LDO_VOL_MASK(ldo) << shift, selector << shift); + + if (ret) + return ret; + + /* + * LDO1 and LDO5 support voltage control by either target voltage1 + * or target voltage2 register. + * We use target voltage1 register for LDO1 and LDO5 in this driver. + * We need to update voltage change control register(0x20) to enable + * LDO1 and LDO5 to change to their programmed target values. + */ + switch (ldo) { + case LP3972_LDO1: + case LP3972_LDO5: + shift = LP3972_LDO_VOL_CHANGE_SHIFT(ldo); + ret = lp3972_set_bits(lp3972, LP3972_VOL_CHANGE_REG, + LP3972_VOL_CHANGE_FLAG_MASK << shift, + LP3972_VOL_CHANGE_FLAG_GO << shift); + if (ret) + return ret; + + ret = lp3972_set_bits(lp3972, LP3972_VOL_CHANGE_REG, + LP3972_VOL_CHANGE_FLAG_MASK << shift, 0); + break; + } + + return ret; +} + +static struct regulator_ops lp3972_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .is_enabled = lp3972_ldo_is_enabled, + .enable = lp3972_ldo_enable, + .disable = lp3972_ldo_disable, + .get_voltage_sel = lp3972_ldo_get_voltage_sel, + .set_voltage_sel = lp3972_ldo_set_voltage_sel, +}; + +static int lp3972_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3972_DCDC1; + u16 mask = 1 << (buck * 2); + u16 val; + + val = lp3972_reg_read(lp3972, LP3972_BUCK_VOL_ENABLE_REG(buck)); + return !!(val & mask); +} + +static int lp3972_dcdc_enable(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3972_DCDC1; + u16 mask = 1 << (buck * 2); + u16 val; + + val = lp3972_set_bits(lp3972, LP3972_BUCK_VOL_ENABLE_REG(buck), + mask, mask); + return val; +} + +static int lp3972_dcdc_disable(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3972_DCDC1; + u16 mask = 1 << (buck * 2); + u16 val; + + val = lp3972_set_bits(lp3972, LP3972_BUCK_VOL_ENABLE_REG(buck), + mask, 0); + return val; +} + +static int lp3972_dcdc_get_voltage_sel(struct regulator_dev *dev) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3972_DCDC1; + u16 reg; + + reg = lp3972_reg_read(lp3972, LP3972_BUCK_VOL1_REG(buck)); + reg &= LP3972_BUCK_VOL_MASK; + + return reg; +} + +static int lp3972_dcdc_set_voltage_sel(struct regulator_dev *dev, + unsigned int selector) +{ + struct lp3972 *lp3972 = rdev_get_drvdata(dev); + int buck = rdev_get_id(dev) - LP3972_DCDC1; + int ret; + + ret = lp3972_set_bits(lp3972, LP3972_BUCK_VOL1_REG(buck), + LP3972_BUCK_VOL_MASK, selector); + if (ret) + return ret; + + if (buck != 0) + return ret; + + ret = lp3972_set_bits(lp3972, LP3972_VOL_CHANGE_REG, + LP3972_VOL_CHANGE_FLAG_MASK, LP3972_VOL_CHANGE_FLAG_GO); + if (ret) + return ret; + + return lp3972_set_bits(lp3972, LP3972_VOL_CHANGE_REG, + LP3972_VOL_CHANGE_FLAG_MASK, 0); +} + +static struct regulator_ops lp3972_dcdc_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .is_enabled = lp3972_dcdc_is_enabled, + .enable = lp3972_dcdc_enable, + .disable = lp3972_dcdc_disable, + .get_voltage_sel = lp3972_dcdc_get_voltage_sel, + .set_voltage_sel = lp3972_dcdc_set_voltage_sel, +}; + +static const struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = LP3972_LDO1, + .ops = &lp3972_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo1_voltage_map), + .volt_table = ldo1_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = LP3972_LDO2, + .ops = &lp3972_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo23_voltage_map), + .volt_table = ldo23_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = LP3972_LDO3, + .ops = &lp3972_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo23_voltage_map), + .volt_table = ldo23_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = LP3972_LDO4, + .ops = &lp3972_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo4_voltage_map), + .volt_table = ldo4_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO5", + .id = LP3972_LDO5, + .ops = &lp3972_ldo_ops, + .n_voltages = ARRAY_SIZE(ldo5_voltage_map), + .volt_table = ldo5_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC1", + .id = LP3972_DCDC1, + .ops = &lp3972_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck1_voltage_map), + .volt_table = buck1_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = LP3972_DCDC2, + .ops = &lp3972_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck23_voltage_map), + .volt_table = buck23_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC3", + .id = LP3972_DCDC3, + .ops = &lp3972_dcdc_ops, + .n_voltages = ARRAY_SIZE(buck23_voltage_map), + .volt_table = buck23_voltage_map, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int setup_regulators(struct lp3972 *lp3972, + struct lp3972_platform_data *pdata) +{ + int i, err; + + lp3972->num_regulators = pdata->num_regulators; + lp3972->rdev = kcalloc(pdata->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); + if (!lp3972->rdev) { + err = -ENOMEM; + goto err_nomem; + } + + /* Instantiate the regulators */ + for (i = 0; i < pdata->num_regulators; i++) { + struct lp3972_regulator_subdev *reg = &pdata->regulators[i]; + struct regulator_config config = { }; + + config.dev = lp3972->dev; + config.init_data = reg->initdata; + config.driver_data = lp3972; + + lp3972->rdev[i] = regulator_register(®ulators[reg->id], + &config); + if (IS_ERR(lp3972->rdev[i])) { + err = PTR_ERR(lp3972->rdev[i]); + dev_err(lp3972->dev, "regulator init failed: %d\n", + err); + goto error; + } + } + + return 0; +error: + while (--i >= 0) + regulator_unregister(lp3972->rdev[i]); + kfree(lp3972->rdev); + lp3972->rdev = NULL; +err_nomem: + return err; +} + +static int lp3972_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lp3972 *lp3972; + struct lp3972_platform_data *pdata = i2c->dev.platform_data; + int ret; + u16 val; + + if (!pdata) { + dev_dbg(&i2c->dev, "No platform init data supplied\n"); + return -ENODEV; + } + + lp3972 = kzalloc(sizeof(struct lp3972), GFP_KERNEL); + if (!lp3972) + return -ENOMEM; + + lp3972->i2c = i2c; + lp3972->dev = &i2c->dev; + + mutex_init(&lp3972->io_lock); + + /* Detect LP3972 */ + ret = lp3972_i2c_read(i2c, LP3972_SYS_CONTROL1_REG, 1, &val); + if (ret == 0 && + (val & SYS_CONTROL1_INIT_MASK) != SYS_CONTROL1_INIT_VAL) { + ret = -ENODEV; + dev_err(&i2c->dev, "chip reported: val = 0x%x\n", val); + } + if (ret < 0) { + dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret); + goto err_detect; + } + + ret = setup_regulators(lp3972, pdata); + if (ret < 0) + goto err_detect; + + i2c_set_clientdata(i2c, lp3972); + return 0; + +err_detect: + kfree(lp3972); + return ret; +} + +static int lp3972_i2c_remove(struct i2c_client *i2c) +{ + struct lp3972 *lp3972 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < lp3972->num_regulators; i++) + regulator_unregister(lp3972->rdev[i]); + kfree(lp3972->rdev); + kfree(lp3972); + + return 0; +} + +static const struct i2c_device_id lp3972_i2c_id[] = { + { "lp3972", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp3972_i2c_id); + +static struct i2c_driver lp3972_i2c_driver = { + .driver = { + .name = "lp3972", + .owner = THIS_MODULE, + }, + .probe = lp3972_i2c_probe, + .remove = lp3972_i2c_remove, + .id_table = lp3972_i2c_id, +}; + +static int __init lp3972_module_init(void) +{ + return i2c_add_driver(&lp3972_i2c_driver); +} +subsys_initcall(lp3972_module_init); + +static void __exit lp3972_module_exit(void) +{ + i2c_del_driver(&lp3972_i2c_driver); +} +module_exit(lp3972_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Axel Lin "); +MODULE_DESCRIPTION("LP3972 PMIC driver"); diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c new file mode 100644 index 0000000..f5fc4a1 --- /dev/null +++ b/drivers/regulator/lp872x.c @@ -0,0 +1,906 @@ +/* + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers : LP8720/8725 shared */ +#define LP872X_GENERAL_CFG 0x00 +#define LP872X_LDO1_VOUT 0x01 +#define LP872X_LDO2_VOUT 0x02 +#define LP872X_LDO3_VOUT 0x03 +#define LP872X_LDO4_VOUT 0x04 +#define LP872X_LDO5_VOUT 0x05 + +/* Registers : LP8720 */ +#define LP8720_BUCK_VOUT1 0x06 +#define LP8720_BUCK_VOUT2 0x07 +#define LP8720_ENABLE 0x08 + +/* Registers : LP8725 */ +#define LP8725_LILO1_VOUT 0x06 +#define LP8725_LILO2_VOUT 0x07 +#define LP8725_BUCK1_VOUT1 0x08 +#define LP8725_BUCK1_VOUT2 0x09 +#define LP8725_BUCK2_VOUT1 0x0A +#define LP8725_BUCK2_VOUT2 0x0B +#define LP8725_BUCK_CTRL 0x0C +#define LP8725_LDO_CTRL 0x0D + +/* Mask/shift : LP8720/LP8725 shared */ +#define LP872X_VOUT_M 0x1F +#define LP872X_START_DELAY_M 0xE0 +#define LP872X_START_DELAY_S 5 +#define LP872X_EN_LDO1_M BIT(0) +#define LP872X_EN_LDO2_M BIT(1) +#define LP872X_EN_LDO3_M BIT(2) +#define LP872X_EN_LDO4_M BIT(3) +#define LP872X_EN_LDO5_M BIT(4) + +/* Mask/shift : LP8720 */ +#define LP8720_TIMESTEP_S 0 /* Addr 00h */ +#define LP8720_TIMESTEP_M BIT(0) +#define LP8720_EXT_DVS_M BIT(2) +#define LP8720_BUCK_FPWM_S 5 /* Addr 07h */ +#define LP8720_BUCK_FPWM_M BIT(5) +#define LP8720_EN_BUCK_M BIT(5) /* Addr 08h */ +#define LP8720_DVS_SEL_M BIT(7) + +/* Mask/shift : LP8725 */ +#define LP8725_TIMESTEP_M 0xC0 /* Addr 00h */ +#define LP8725_TIMESTEP_S 6 +#define LP8725_BUCK1_EN_M BIT(0) +#define LP8725_DVS1_M BIT(2) +#define LP8725_DVS2_M BIT(3) +#define LP8725_BUCK2_EN_M BIT(4) +#define LP8725_BUCK_CL_M 0xC0 /* Addr 09h, 0Bh */ +#define LP8725_BUCK_CL_S 6 +#define LP8725_BUCK1_FPWM_S 1 /* Addr 0Ch */ +#define LP8725_BUCK1_FPWM_M BIT(1) +#define LP8725_BUCK2_FPWM_S 5 +#define LP8725_BUCK2_FPWM_M BIT(5) +#define LP8725_EN_LILO1_M BIT(5) /* Addr 0Dh */ +#define LP8725_EN_LILO2_M BIT(6) + +/* PWM mode */ +#define LP872X_FORCE_PWM 1 +#define LP872X_AUTO_PWM 0 + +#define LP8720_NUM_REGULATORS 6 +#define LP8725_NUM_REGULATORS 9 +#define EXTERN_DVS_USED 0 +#define MAX_DELAY 6 + +/* Default DVS Mode */ +#define LP8720_DEFAULT_DVS 0 +#define LP8725_DEFAULT_DVS BIT(2) + +/* dump registers in regmap-debugfs */ +#define MAX_REGISTERS 0x0F + +enum lp872x_id { + LP8720, + LP8725, +}; + +struct lp872x { + struct regmap *regmap; + struct device *dev; + enum lp872x_id chipid; + struct lp872x_platform_data *pdata; + struct regulator_dev **regulators; + int num_regulators; + enum lp872x_dvs_state dvs_pin; + int dvs_gpio; +}; + +/* LP8720/LP8725 shared voltage table for LDOs */ +static const unsigned int lp872x_ldo_vtbl[] = { + 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 2000000, + 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, 2950000, 3000000, 3100000, 3300000, +}; + +/* LP8720 LDO4 voltage table */ +static const unsigned int lp8720_ldo4_vtbl[] = { + 800000, 850000, 900000, 1000000, 1100000, 1200000, 1250000, 1300000, + 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, +}; + +/* LP8725 LILO(Low Input Low Output) voltage table */ +static const unsigned int lp8725_lilo_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, +}; + +/* LP8720 BUCK voltage table */ +#define EXT_R 0 /* external resistor divider */ +static const unsigned int lp8720_buck_vtbl[] = { + EXT_R, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, +}; + +/* LP8725 BUCK voltage table */ +static const unsigned int lp8725_buck_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, +}; + +/* LP8725 BUCK current limit */ +static const unsigned int lp8725_buck_uA[] = { + 460000, 780000, 1050000, 1370000, +}; + +static int lp872x_read_byte(struct lp872x *lp, u8 addr, u8 *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(lp->regmap, addr, &val); + if (ret < 0) { + dev_err(lp->dev, "failed to read 0x%.2x\n", addr); + return ret; + } + + *data = (u8)val; + return 0; +} + +static inline int lp872x_write_byte(struct lp872x *lp, u8 addr, u8 data) +{ + return regmap_write(lp->regmap, addr, data); +} + +static inline int lp872x_update_bits(struct lp872x *lp, u8 addr, + unsigned int mask, u8 data) +{ + return regmap_update_bits(lp->regmap, addr, mask, data); +} + +static int lp872x_get_timestep_usec(struct lp872x *lp) +{ + enum lp872x_id chip = lp->chipid; + u8 val, mask, shift; + int *time_usec, size, ret; + int lp8720_time_usec[] = { 25, 50 }; + int lp8725_time_usec[] = { 32, 64, 128, 256 }; + + switch (chip) { + case LP8720: + mask = LP8720_TIMESTEP_M; + shift = LP8720_TIMESTEP_S; + time_usec = &lp8720_time_usec[0]; + size = ARRAY_SIZE(lp8720_time_usec); + break; + case LP8725: + mask = LP8725_TIMESTEP_M; + shift = LP8725_TIMESTEP_S; + time_usec = &lp8725_time_usec[0]; + size = ARRAY_SIZE(lp8725_time_usec); + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return -EINVAL; + + val = (val & mask) >> shift; + if (val >= size) + return -EINVAL; + + return *(time_usec + val); +} + +static int lp872x_regulator_enable_time(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id rid = rdev_get_id(rdev); + int time_step_us = lp872x_get_timestep_usec(lp); + int ret; + u8 addr, val; + + if (time_step_us < 0) + return -EINVAL; + + switch (rid) { + case LP8720_ID_LDO1 ... LP8720_ID_BUCK: + addr = LP872X_LDO1_VOUT + rid; + break; + case LP8725_ID_LDO1 ... LP8725_ID_BUCK1: + addr = LP872X_LDO1_VOUT + rid - LP8725_ID_BASE; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT1; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP872X_START_DELAY_M) >> LP872X_START_DELAY_S; + + return val > MAX_DELAY ? 0 : val * time_step_us; +} + +static void lp872x_set_dvs(struct lp872x *lp, enum lp872x_dvs_sel dvs_sel, + int gpio) +{ + enum lp872x_dvs_state state; + + state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW; + gpio_set_value(gpio, state); + lp->dvs_pin = state; +} + +static u8 lp872x_select_buck_vout_addr(struct lp872x *lp, + enum lp872x_regulator_id buck) +{ + u8 val, addr; + + if (lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val)) + return 0; + + switch (buck) { + case LP8720_ID_BUCK: + if (val & LP8720_EXT_DVS_M) { + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } else { + if (lp872x_read_byte(lp, LP8720_ENABLE, &val)) + return 0; + + addr = val & LP8720_DVS_SEL_M ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } + break; + case LP8725_ID_BUCK1: + if (val & LP8725_DVS1_M) + addr = LP8725_BUCK1_VOUT1; + else + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8725_BUCK1_VOUT1 : LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = val & LP8725_DVS2_M ? + LP8725_BUCK2_VOUT1 : LP8725_BUCK2_VOUT2; + break; + default: + return 0; + } + + return addr; +} + +static bool lp872x_is_valid_buck_addr(u8 addr) +{ + switch (addr) { + case LP8720_BUCK_VOUT1: + case LP8720_BUCK_VOUT2: + case LP8725_BUCK1_VOUT1: + case LP8725_BUCK1_VOUT2: + case LP8725_BUCK2_VOUT1: + case LP8725_BUCK2_VOUT2: + return true; + default: + return false; + } +} + +static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask = LP872X_VOUT_M; + struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL; + + if (dvs && gpio_is_valid(dvs->gpio)) + lp872x_set_dvs(lp, dvs->vsel, dvs->gpio); + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, selector); +} + +static int lp872x_buck_get_voltage_sel(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & LP872X_VOUT_M; +} + +static int lp8725_buck_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + int i; + u8 addr; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + for (i = ARRAY_SIZE(lp8725_buck_uA) - 1 ; i >= 0; i--) { + if (lp8725_buck_uA[i] >= min_uA && + lp8725_buck_uA[i] <= max_uA) + return lp872x_update_bits(lp, addr, + LP8725_BUCK_CL_M, + i << LP8725_BUCK_CL_S); + } + + return -EINVAL; +} + +static int lp8725_buck_get_current_limit(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP8725_BUCK_CL_M) >> LP8725_BUCK_CL_S; + + return (val < ARRAY_SIZE(lp8725_buck_uA)) ? + lp8725_buck_uA[val] : -EINVAL; +} + +static int lp872x_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, shift, val; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + shift = LP8720_BUCK_FPWM_S; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + shift = LP8725_BUCK1_FPWM_S; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + shift = LP8725_BUCK2_FPWM_S; + break; + default: + return -EINVAL; + } + + if (mode == REGULATOR_MODE_FAST) + val = LP872X_FORCE_PWM << shift; + else if (mode == REGULATOR_MODE_NORMAL) + val = LP872X_AUTO_PWM << shift; + else + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, val); +} + +static unsigned int lp872x_buck_get_mode(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, val; + int ret; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops lp872x_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, +}; + +static struct regulator_ops lp8720_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, +}; + +static struct regulator_ops lp8725_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, + .set_current_limit = lp8725_buck_set_current_limit, + .get_current_limit = lp8725_buck_get_current_limit, +}; + +static struct regulator_desc lp8720_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8720_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8720_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8720_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8720_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl), + .volt_table = lp8720_ldo4_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8720_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "buck", + .id = LP8720_ID_BUCK, + .ops = &lp8720_buck_ops, + .n_voltages = ARRAY_SIZE(lp8720_buck_vtbl), + .volt_table = lp8720_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP8720_EN_BUCK_M, + }, +}; + +static struct regulator_desc lp8725_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8725_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8725_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8725_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8725_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8725_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "lilo1", + .id = LP8725_ID_LILO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO1_M, + }, + { + .name = "lilo2", + .id = LP8725_ID_LILO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO2_M, + }, + { + .name = "buck1", + .id = LP8725_ID_BUCK1, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK1_EN_M, + }, + { + .name = "buck2", + .id = LP8725_ID_BUCK2, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK2_EN_M, + }, +}; + +static int lp872x_init_dvs(struct lp872x *lp) +{ + int ret, gpio; + struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL; + enum lp872x_dvs_state pinstate; + u8 mask[] = { LP8720_EXT_DVS_M, LP8725_DVS1_M | LP8725_DVS2_M }; + u8 default_dvs_mode[] = { LP8720_DEFAULT_DVS, LP8725_DEFAULT_DVS }; + + if (!dvs) + goto set_default_dvs_mode; + + gpio = dvs->gpio; + if (!gpio_is_valid(gpio)) { + dev_err(lp->dev, "invalid gpio: %d\n", gpio); + return -EINVAL; + } + + pinstate = dvs->init_state; + ret = devm_gpio_request_one(lp->dev, gpio, pinstate, "LP872X DVS"); + if (ret) { + dev_err(lp->dev, "gpio request err: %d\n", ret); + return ret; + } + + lp->dvs_pin = pinstate; + lp->dvs_gpio = gpio; + + return 0; + +set_default_dvs_mode: + return lp872x_update_bits(lp, LP872X_GENERAL_CFG, mask[lp->chipid], + default_dvs_mode[lp->chipid]); +} + +static int lp872x_config(struct lp872x *lp) +{ + struct lp872x_platform_data *pdata = lp->pdata; + int ret; + + if (!pdata || !pdata->update_config) + goto init_dvs; + + ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config); + if (ret) + return ret; + +init_dvs: + return lp872x_init_dvs(lp); +} + +static struct regulator_init_data +*lp872x_find_regulator_init_data(int id, struct lp872x *lp) +{ + struct lp872x_platform_data *pdata = lp->pdata; + int i; + + if (!pdata) + return NULL; + + for (i = 0; i < lp->num_regulators; i++) { + if (pdata->regulator_data[i].id == id) + return pdata->regulator_data[i].init_data; + } + + return NULL; +} + +static int lp872x_regulator_register(struct lp872x *lp) +{ + struct regulator_desc *desc; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int i, ret; + + for (i = 0 ; i < lp->num_regulators ; i++) { + desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : + &lp8725_regulator_desc[i]; + + cfg.dev = lp->dev; + cfg.init_data = lp872x_find_regulator_init_data(desc->id, lp); + cfg.driver_data = lp; + cfg.regmap = lp->regmap; + + rdev = regulator_register(desc, &cfg); + if (IS_ERR(rdev)) { + dev_err(lp->dev, "regulator register err"); + ret = PTR_ERR(rdev); + goto err; + } + + *(lp->regulators + i) = rdev; + } + + return 0; +err: + while (--i >= 0) { + rdev = *(lp->regulators + i); + regulator_unregister(rdev); + } + return ret; +} + +static void lp872x_regulator_unregister(struct lp872x *lp) +{ + struct regulator_dev *rdev; + int i; + + for (i = 0 ; i < lp->num_regulators ; i++) { + rdev = *(lp->regulators + i); + regulator_unregister(rdev); + } +} + +static const struct regmap_config lp872x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX_REGISTERS, +}; + +static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp872x *lp; + int ret, size, num_regulators; + const int lp872x_num_regulators[] = { + [LP8720] = LP8720_NUM_REGULATORS, + [LP8725] = LP8725_NUM_REGULATORS, + }; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); + if (!lp) + goto err_mem; + + num_regulators = lp872x_num_regulators[id->driver_data]; + size = sizeof(struct regulator_dev *) * num_regulators; + + lp->regulators = devm_kzalloc(&cl->dev, size, GFP_KERNEL); + if (!lp->regulators) + goto err_mem; + + lp->regmap = devm_regmap_init_i2c(cl, &lp872x_regmap_config); + if (IS_ERR(lp->regmap)) { + ret = PTR_ERR(lp->regmap); + dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); + goto err_dev; + } + + lp->dev = &cl->dev; + lp->pdata = cl->dev.platform_data; + lp->chipid = id->driver_data; + lp->num_regulators = num_regulators; + i2c_set_clientdata(cl, lp); + + ret = lp872x_config(lp); + if (ret) + goto err_dev; + + return lp872x_regulator_register(lp); + +err_mem: + return -ENOMEM; +err_dev: + return ret; +} + +static int lp872x_remove(struct i2c_client *cl) +{ + struct lp872x *lp = i2c_get_clientdata(cl); + + lp872x_regulator_unregister(lp); + return 0; +} + +static const struct i2c_device_id lp872x_ids[] = { + {"lp8720", LP8720}, + {"lp8725", LP8725}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp872x_ids); + +static struct i2c_driver lp872x_driver = { + .driver = { + .name = "lp872x", + .owner = THIS_MODULE, + }, + .probe = lp872x_probe, + .remove = lp872x_remove, + .id_table = lp872x_ids, +}; + +module_i2c_driver(lp872x_driver); + +MODULE_DESCRIPTION("TI/National Semiconductor LP872x PMU Regulator Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c new file mode 100644 index 0000000..f0f6ea0 --- /dev/null +++ b/drivers/regulator/lp8755.c @@ -0,0 +1,566 @@ +/* + * LP8755 High Performance Power Management Unit : System Interface Driver + * (based on rev. 0.26) + * Copyright 2012 Texas Instruments + * + * Author: Daniel(Geon Si) Jeong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LP8755_REG_BUCK0 0x00 +#define LP8755_REG_BUCK1 0x03 +#define LP8755_REG_BUCK2 0x04 +#define LP8755_REG_BUCK3 0x01 +#define LP8755_REG_BUCK4 0x05 +#define LP8755_REG_BUCK5 0x02 +#define LP8755_REG_MAX 0xFF + +#define LP8755_BUCK_EN_M BIT(7) +#define LP8755_BUCK_LINEAR_OUT_MAX 0x76 +#define LP8755_BUCK_VOUT_M 0x7F + +struct lp8755_mphase { + int nreg; + int buck_num[LP8755_BUCK_MAX]; +}; + +struct lp8755_chip { + struct device *dev; + struct regmap *regmap; + struct lp8755_platform_data *pdata; + + int irq; + unsigned int irqmask; + + int mphase; + struct regulator_dev *rdev[LP8755_BUCK_MAX]; +}; + +/** + *lp8755_read : read a single register value from lp8755. + *@pchip : device to read from + *@reg : register to read from + *@val : pointer to store read value + */ +static int lp8755_read(struct lp8755_chip *pchip, unsigned int reg, + unsigned int *val) +{ + return regmap_read(pchip->regmap, reg, val); +} + +/** + *lp8755_write : write a single register value to lp8755. + *@pchip : device to write to + *@reg : register to write to + *@val : value to be written + */ +static int lp8755_write(struct lp8755_chip *pchip, unsigned int reg, + unsigned int val) +{ + return regmap_write(pchip->regmap, reg, val); +} + +/** + *lp8755_update_bits : set the values of bit fields in lp8755 register. + *@pchip : device to read from + *@reg : register to update + *@mask : bitmask to be changed + *@val : value for bitmask + */ +static int lp8755_update_bits(struct lp8755_chip *pchip, unsigned int reg, + unsigned int mask, unsigned int val) +{ + return regmap_update_bits(pchip->regmap, reg, mask, val); +} + +static int lp8755_buck_enable_time(struct regulator_dev *rdev) +{ + int ret; + unsigned int regval; + enum lp8755_bucks id = rdev_get_id(rdev); + struct lp8755_chip *pchip = rdev_get_drvdata(rdev); + + ret = lp8755_read(pchip, 0x12 + id, ®val); + if (ret < 0) { + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return ret; + } + return (regval & 0xff) * 100; +} + +static int lp8755_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + int ret; + unsigned int regbval = 0x0; + enum lp8755_bucks id = rdev_get_id(rdev); + struct lp8755_chip *pchip = rdev_get_drvdata(rdev); + + switch (mode) { + case REGULATOR_MODE_FAST: + /* forced pwm mode */ + regbval = (0x01 << id); + break; + case REGULATOR_MODE_NORMAL: + /* enable automatic pwm/pfm mode */ + ret = lp8755_update_bits(pchip, 0x08 + id, 0x20, 0x00); + if (ret < 0) + goto err_i2c; + break; + case REGULATOR_MODE_IDLE: + /* enable automatic pwm/pfm/lppfm mode */ + ret = lp8755_update_bits(pchip, 0x08 + id, 0x20, 0x20); + if (ret < 0) + goto err_i2c; + + ret = lp8755_update_bits(pchip, 0x10, 0x01, 0x01); + if (ret < 0) + goto err_i2c; + break; + default: + dev_err(pchip->dev, "Not supported buck mode %s\n", __func__); + /* forced pwm mode */ + regbval = (0x01 << id); + } + + ret = lp8755_update_bits(pchip, 0x06, 0x01 << id, regbval); + if (ret < 0) + goto err_i2c; + return ret; +err_i2c: + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return ret; +} + +static unsigned int lp8755_buck_get_mode(struct regulator_dev *rdev) +{ + int ret; + unsigned int regval; + enum lp8755_bucks id = rdev_get_id(rdev); + struct lp8755_chip *pchip = rdev_get_drvdata(rdev); + + ret = lp8755_read(pchip, 0x06, ®val); + if (ret < 0) + goto err_i2c; + + /* mode fast means forced pwm mode */ + if (regval & (0x01 << id)) + return REGULATOR_MODE_FAST; + + ret = lp8755_read(pchip, 0x08 + id, ®val); + if (ret < 0) + goto err_i2c; + + /* mode idle means automatic pwm/pfm/lppfm mode */ + if (regval & 0x20) + return REGULATOR_MODE_IDLE; + + /* mode normal means automatic pwm/pfm mode */ + return REGULATOR_MODE_NORMAL; + +err_i2c: + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return 0; +} + +static int lp8755_buck_set_ramp(struct regulator_dev *rdev, int ramp) +{ + int ret; + unsigned int regval = 0x00; + enum lp8755_bucks id = rdev_get_id(rdev); + struct lp8755_chip *pchip = rdev_get_drvdata(rdev); + + /* uV/us */ + switch (ramp) { + case 0 ... 230: + regval = 0x07; + break; + case 231 ... 470: + regval = 0x06; + break; + case 471 ... 940: + regval = 0x05; + break; + case 941 ... 1900: + regval = 0x04; + break; + case 1901 ... 3800: + regval = 0x03; + break; + case 3801 ... 7500: + regval = 0x02; + break; + case 7501 ... 15000: + regval = 0x01; + break; + case 15001 ... 30000: + regval = 0x00; + break; + default: + dev_err(pchip->dev, + "Not supported ramp value %d %s\n", ramp, __func__); + return -EINVAL; + } + + ret = lp8755_update_bits(pchip, 0x07 + id, 0x07, regval); + if (ret < 0) + goto err_i2c; + return ret; +err_i2c: + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return ret; +} + +static struct regulator_ops lp8755_buck_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp8755_buck_enable_time, + .set_mode = lp8755_buck_set_mode, + .get_mode = lp8755_buck_get_mode, + .set_ramp_delay = lp8755_buck_set_ramp, +}; + +#define lp8755_rail(_id) "lp8755_buck"#_id +#define lp8755_buck_init(_id)\ +{\ + .constraints = {\ + .name = lp8755_rail(_id),\ + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,\ + .min_uV = 500000,\ + .max_uV = 1675000,\ + },\ +} + +static struct regulator_init_data lp8755_reg_default[LP8755_BUCK_MAX] = { + [LP8755_BUCK0] = lp8755_buck_init(0), + [LP8755_BUCK1] = lp8755_buck_init(1), + [LP8755_BUCK2] = lp8755_buck_init(2), + [LP8755_BUCK3] = lp8755_buck_init(3), + [LP8755_BUCK4] = lp8755_buck_init(4), + [LP8755_BUCK5] = lp8755_buck_init(5), +}; + +static const struct lp8755_mphase mphase_buck[MPHASE_CONF_MAX] = { + { 3, { LP8755_BUCK0, LP8755_BUCK3, LP8755_BUCK5 } }, + { 6, { LP8755_BUCK0, LP8755_BUCK1, LP8755_BUCK2, LP8755_BUCK3, + LP8755_BUCK4, LP8755_BUCK5 } }, + { 5, { LP8755_BUCK0, LP8755_BUCK2, LP8755_BUCK3, LP8755_BUCK4, + LP8755_BUCK5} }, + { 4, { LP8755_BUCK0, LP8755_BUCK3, LP8755_BUCK4, LP8755_BUCK5} }, + { 3, { LP8755_BUCK0, LP8755_BUCK4, LP8755_BUCK5} }, + { 2, { LP8755_BUCK0, LP8755_BUCK5} }, + { 1, { LP8755_BUCK0} }, + { 2, { LP8755_BUCK0, LP8755_BUCK3} }, + { 4, { LP8755_BUCK0, LP8755_BUCK2, LP8755_BUCK3, LP8755_BUCK5} }, +}; + +static int lp8755_init_data(struct lp8755_chip *pchip) +{ + unsigned int regval; + int ret, icnt, buck_num; + struct lp8755_platform_data *pdata = pchip->pdata; + + /* read back muti-phase configuration */ + ret = lp8755_read(pchip, 0x3D, ®val); + if (ret < 0) + goto out_i2c_error; + pchip->mphase = regval & 0x0F; + + /* set default data based on multi-phase config */ + for (icnt = 0; icnt < mphase_buck[pchip->mphase].nreg; icnt++) { + buck_num = mphase_buck[pchip->mphase].buck_num[icnt]; + pdata->buck_data[buck_num] = &lp8755_reg_default[buck_num]; + } + return ret; + +out_i2c_error: + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return ret; +} + +#define lp8755_buck_desc(_id)\ +{\ + .name = lp8755_rail(_id),\ + .id = LP8755_BUCK##_id,\ + .ops = &lp8755_buck_ops,\ + .n_voltages = LP8755_BUCK_LINEAR_OUT_MAX+1,\ + .uV_step = 10000,\ + .min_uV = 500000,\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .enable_reg = LP8755_REG_BUCK##_id,\ + .enable_mask = LP8755_BUCK_EN_M,\ + .vsel_reg = LP8755_REG_BUCK##_id,\ + .vsel_mask = LP8755_BUCK_VOUT_M,\ +} + +static struct regulator_desc lp8755_regulators[] = { + lp8755_buck_desc(0), + lp8755_buck_desc(1), + lp8755_buck_desc(2), + lp8755_buck_desc(3), + lp8755_buck_desc(4), + lp8755_buck_desc(5), +}; + +static int lp8755_regulator_init(struct lp8755_chip *pchip) +{ + int ret, icnt, buck_num; + struct lp8755_platform_data *pdata = pchip->pdata; + struct regulator_config rconfig = { }; + + rconfig.regmap = pchip->regmap; + rconfig.dev = pchip->dev; + rconfig.driver_data = pchip; + + for (icnt = 0; icnt < mphase_buck[pchip->mphase].nreg; icnt++) { + buck_num = mphase_buck[pchip->mphase].buck_num[icnt]; + rconfig.init_data = pdata->buck_data[buck_num]; + rconfig.of_node = pchip->dev->of_node; + pchip->rdev[buck_num] = + regulator_register(&lp8755_regulators[buck_num], &rconfig); + if (IS_ERR(pchip->rdev[buck_num])) { + ret = PTR_ERR(pchip->rdev[buck_num]); + pchip->rdev[buck_num] = NULL; + dev_err(pchip->dev, "regulator init failed: buck %d\n", + buck_num); + goto err_buck; + } + } + + return 0; + +err_buck: + for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) + regulator_unregister(pchip->rdev[icnt]); + return ret; +} + +static irqreturn_t lp8755_irq_handler(int irq, void *data) +{ + int ret, icnt; + unsigned int flag0, flag1; + struct lp8755_chip *pchip = data; + + /* read flag0 register */ + ret = lp8755_read(pchip, 0x0D, &flag0); + if (ret < 0) + goto err_i2c; + /* clear flag register to pull up int. pin */ + ret = lp8755_write(pchip, 0x0D, 0x00); + if (ret < 0) + goto err_i2c; + + /* sent power fault detection event to specific regulator */ + for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) + if ((flag0 & (0x4 << icnt)) + && (pchip->irqmask & (0x04 << icnt)) + && (pchip->rdev[icnt] != NULL)) + regulator_notifier_call_chain(pchip->rdev[icnt], + LP8755_EVENT_PWR_FAULT, + NULL); + + /* read flag1 register */ + ret = lp8755_read(pchip, 0x0E, &flag1); + if (ret < 0) + goto err_i2c; + /* clear flag register to pull up int. pin */ + ret = lp8755_write(pchip, 0x0E, 0x00); + if (ret < 0) + goto err_i2c; + + /* send OCP event to all regualtor devices */ + if ((flag1 & 0x01) && (pchip->irqmask & 0x01)) + for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) + if (pchip->rdev[icnt] != NULL) + regulator_notifier_call_chain(pchip->rdev[icnt], + LP8755_EVENT_OCP, + NULL); + + /* send OVP event to all regualtor devices */ + if ((flag1 & 0x02) && (pchip->irqmask & 0x02)) + for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) + if (pchip->rdev[icnt] != NULL) + regulator_notifier_call_chain(pchip->rdev[icnt], + LP8755_EVENT_OVP, + NULL); + return IRQ_HANDLED; + +err_i2c: + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return IRQ_NONE; +} + +static int lp8755_int_config(struct lp8755_chip *pchip) +{ + int ret; + unsigned int regval; + + if (pchip->irq == 0) { + dev_warn(pchip->dev, "not use interrupt : %s\n", __func__); + return 0; + } + + ret = lp8755_read(pchip, 0x0F, ®val); + if (ret < 0) + goto err_i2c; + pchip->irqmask = regval; + ret = request_threaded_irq(pchip->irq, NULL, lp8755_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "lp8755-irq", pchip); + if (ret) + return ret; + + return ret; + +err_i2c: + dev_err(pchip->dev, "i2c acceess error %s\n", __func__); + return ret; +} + +static const struct regmap_config lp8755_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LP8755_REG_MAX, +}; + +static int lp8755_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret, icnt; + struct lp8755_chip *pchip; + struct lp8755_platform_data *pdata = client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c functionality check fail.\n"); + return -EOPNOTSUPP; + } + + pchip = devm_kzalloc(&client->dev, + sizeof(struct lp8755_chip), GFP_KERNEL); + if (!pchip) + return -ENOMEM; + + pchip->dev = &client->dev; + pchip->regmap = devm_regmap_init_i2c(client, &lp8755_regmap); + if (IS_ERR(pchip->regmap)) { + ret = PTR_ERR(pchip->regmap); + dev_err(&client->dev, "fail to allocate regmap %d\n", ret); + return ret; + } + i2c_set_clientdata(client, pchip); + + if (pdata != NULL) { + pchip->pdata = pdata; + pchip->mphase = pdata->mphase; + } else { + pchip->pdata = devm_kzalloc(pchip->dev, + sizeof(struct lp8755_platform_data), + GFP_KERNEL); + if (!pchip->pdata) + return -ENOMEM; + ret = lp8755_init_data(pchip); + if (ret < 0) { + dev_err(&client->dev, "fail to initialize chip\n"); + return ret; + } + } + + ret = lp8755_regulator_init(pchip); + if (ret < 0) { + dev_err(&client->dev, "fail to initialize regulators\n"); + goto err_regulator; + } + + pchip->irq = client->irq; + ret = lp8755_int_config(pchip); + if (ret < 0) { + dev_err(&client->dev, "fail to irq config\n"); + goto err_irq; + } + + return ret; + +err_irq: + for (icnt = 0; icnt < mphase_buck[pchip->mphase].nreg; icnt++) + regulator_unregister(pchip->rdev[icnt]); + +err_regulator: + /* output disable */ + for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) + lp8755_write(pchip, icnt, 0x00); + + return ret; +} + +static int lp8755_remove(struct i2c_client *client) +{ + int icnt; + struct lp8755_chip *pchip = i2c_get_clientdata(client); + + for (icnt = 0; icnt < mphase_buck[pchip->mphase].nreg; icnt++) + regulator_unregister(pchip->rdev[icnt]); + + for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++) + lp8755_write(pchip, icnt, 0x00); + + if (pchip->irq != 0) + free_irq(pchip->irq, pchip); + + return 0; +} + +static const struct i2c_device_id lp8755_id[] = { + {LP8755_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lp8755_id); + +static struct i2c_driver lp8755_i2c_driver = { + .driver = { + .name = LP8755_NAME, + }, + .probe = lp8755_probe, + .remove = lp8755_remove, + .id_table = lp8755_id, +}; + +static int __init lp8755_init(void) +{ + return i2c_add_driver(&lp8755_i2c_driver); +} + +subsys_initcall(lp8755_init); + +static void __exit lp8755_exit(void) +{ + i2c_del_driver(&lp8755_i2c_driver); +} + +module_exit(lp8755_exit); + +MODULE_DESCRIPTION("Texas Instruments lp8755 driver"); +MODULE_AUTHOR("Daniel Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c new file mode 100644 index 0000000..eb1e1e8 --- /dev/null +++ b/drivers/regulator/lp8788-buck.c @@ -0,0 +1,566 @@ +/* + * TI LP8788 MFD - buck regulator driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* register address */ +#define LP8788_EN_BUCK 0x0C +#define LP8788_BUCK_DVS_SEL 0x1D +#define LP8788_BUCK1_VOUT0 0x1E +#define LP8788_BUCK1_VOUT1 0x1F +#define LP8788_BUCK1_VOUT2 0x20 +#define LP8788_BUCK1_VOUT3 0x21 +#define LP8788_BUCK2_VOUT0 0x22 +#define LP8788_BUCK2_VOUT1 0x23 +#define LP8788_BUCK2_VOUT2 0x24 +#define LP8788_BUCK2_VOUT3 0x25 +#define LP8788_BUCK3_VOUT 0x26 +#define LP8788_BUCK4_VOUT 0x27 +#define LP8788_BUCK1_TIMESTEP 0x28 +#define LP8788_BUCK_PWM 0x2D + +/* mask/shift bits */ +#define LP8788_EN_BUCK1_M BIT(0) /* Addr 0Ch */ +#define LP8788_EN_BUCK2_M BIT(1) +#define LP8788_EN_BUCK3_M BIT(2) +#define LP8788_EN_BUCK4_M BIT(3) +#define LP8788_BUCK1_DVS_SEL_M 0x04 /* Addr 1Dh */ +#define LP8788_BUCK1_DVS_M 0x03 +#define LP8788_BUCK1_DVS_S 0 +#define LP8788_BUCK2_DVS_SEL_M 0x40 +#define LP8788_BUCK2_DVS_M 0x30 +#define LP8788_BUCK2_DVS_S 4 +#define LP8788_BUCK1_DVS_I2C BIT(2) +#define LP8788_BUCK2_DVS_I2C BIT(6) +#define LP8788_BUCK1_DVS_PIN (0 << 2) +#define LP8788_BUCK2_DVS_PIN (0 << 6) +#define LP8788_VOUT_M 0x1F /* Addr 1Eh ~ 27h */ +#define LP8788_STARTUP_TIME_M 0xF8 /* Addr 28h ~ 2Bh */ +#define LP8788_STARTUP_TIME_S 3 +#define LP8788_FPWM_BUCK1_M BIT(0) /* Addr 2Dh */ +#define LP8788_FPWM_BUCK1_S 0 +#define LP8788_FPWM_BUCK2_M BIT(1) +#define LP8788_FPWM_BUCK2_S 1 +#define LP8788_FPWM_BUCK3_M BIT(2) +#define LP8788_FPWM_BUCK3_S 2 +#define LP8788_FPWM_BUCK4_M BIT(3) +#define LP8788_FPWM_BUCK4_S 3 + +#define INVALID_ADDR 0xFF +#define LP8788_FORCE_PWM 1 +#define LP8788_AUTO_PWM 0 +#define PIN_LOW 0 +#define PIN_HIGH 1 +#define ENABLE_TIME_USEC 32 + +#define BUCK_FPWM_MASK(x) (1 << (x)) +#define BUCK_FPWM_SHIFT(x) (x) + +enum lp8788_dvs_state { + DVS_LOW = GPIOF_OUT_INIT_LOW, + DVS_HIGH = GPIOF_OUT_INIT_HIGH, +}; + +enum lp8788_dvs_mode { + REGISTER, + EXTPIN, +}; + +enum lp8788_buck_id { + BUCK1, + BUCK2, + BUCK3, + BUCK4, +}; + +struct lp8788_buck { + struct lp8788 *lp; + struct regulator_dev *regulator; + void *dvs; +}; + +/* BUCK 1 ~ 4 voltage table */ +static const int lp8788_buck_vtbl[] = { + 500000, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, +}; + +static void lp8788_buck1_set_dvs(struct lp8788_buck *buck) +{ + struct lp8788_buck1_dvs *dvs = (struct lp8788_buck1_dvs *)buck->dvs; + enum lp8788_dvs_state pinstate; + + if (!dvs) + return; + + pinstate = dvs->vsel == DVS_SEL_V0 ? DVS_LOW : DVS_HIGH; + if (gpio_is_valid(dvs->gpio)) + gpio_set_value(dvs->gpio, pinstate); +} + +static void lp8788_buck2_set_dvs(struct lp8788_buck *buck) +{ + struct lp8788_buck2_dvs *dvs = (struct lp8788_buck2_dvs *)buck->dvs; + enum lp8788_dvs_state pin1, pin2; + + if (!dvs) + return; + + switch (dvs->vsel) { + case DVS_SEL_V0: + pin1 = DVS_LOW; + pin2 = DVS_LOW; + break; + case DVS_SEL_V1: + pin1 = DVS_HIGH; + pin2 = DVS_LOW; + break; + case DVS_SEL_V2: + pin1 = DVS_LOW; + pin2 = DVS_HIGH; + break; + case DVS_SEL_V3: + pin1 = DVS_HIGH; + pin2 = DVS_HIGH; + break; + default: + return; + } + + if (gpio_is_valid(dvs->gpio[0])) + gpio_set_value(dvs->gpio[0], pin1); + + if (gpio_is_valid(dvs->gpio[1])) + gpio_set_value(dvs->gpio[1], pin2); +} + +static void lp8788_set_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id) +{ + switch (id) { + case BUCK1: + lp8788_buck1_set_dvs(buck); + break; + case BUCK2: + lp8788_buck2_set_dvs(buck); + break; + default: + break; + } +} + +static enum lp8788_dvs_mode +lp8788_get_buck_dvs_ctrl_mode(struct lp8788_buck *buck, enum lp8788_buck_id id) +{ + u8 val, mask; + + switch (id) { + case BUCK1: + mask = LP8788_BUCK1_DVS_SEL_M; + break; + case BUCK2: + mask = LP8788_BUCK2_DVS_SEL_M; + break; + default: + return REGISTER; + } + + lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); + + return val & mask ? REGISTER : EXTPIN; +} + +static bool lp8788_is_valid_buck_addr(u8 addr) +{ + switch (addr) { + case LP8788_BUCK1_VOUT0: + case LP8788_BUCK1_VOUT1: + case LP8788_BUCK1_VOUT2: + case LP8788_BUCK1_VOUT3: + case LP8788_BUCK2_VOUT0: + case LP8788_BUCK2_VOUT1: + case LP8788_BUCK2_VOUT2: + case LP8788_BUCK2_VOUT3: + return true; + default: + return false; + } +} + +static u8 lp8788_select_buck_vout_addr(struct lp8788_buck *buck, + enum lp8788_buck_id id) +{ + enum lp8788_dvs_mode mode = lp8788_get_buck_dvs_ctrl_mode(buck, id); + struct lp8788_buck1_dvs *b1_dvs; + struct lp8788_buck2_dvs *b2_dvs; + u8 val, idx, addr; + int pin1, pin2; + + switch (id) { + case BUCK1: + if (mode == EXTPIN) { + b1_dvs = (struct lp8788_buck1_dvs *)buck->dvs; + if (!b1_dvs) + goto err; + + idx = gpio_get_value(b1_dvs->gpio) ? 1 : 0; + } else { + lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); + idx = (val & LP8788_BUCK1_DVS_M) >> LP8788_BUCK1_DVS_S; + } + addr = LP8788_BUCK1_VOUT0 + idx; + break; + case BUCK2: + if (mode == EXTPIN) { + b2_dvs = (struct lp8788_buck2_dvs *)buck->dvs; + if (!b2_dvs) + goto err; + + pin1 = gpio_get_value(b2_dvs->gpio[0]); + pin2 = gpio_get_value(b2_dvs->gpio[1]); + + if (pin1 == PIN_LOW && pin2 == PIN_LOW) + idx = 0; + else if (pin1 == PIN_LOW && pin2 == PIN_HIGH) + idx = 2; + else if (pin1 == PIN_HIGH && pin2 == PIN_LOW) + idx = 1; + else + idx = 3; + } else { + lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); + idx = (val & LP8788_BUCK2_DVS_M) >> LP8788_BUCK2_DVS_S; + } + addr = LP8788_BUCK2_VOUT0 + idx; + break; + default: + goto err; + } + + return addr; +err: + return INVALID_ADDR; +} + +static int lp8788_buck12_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct lp8788_buck *buck = rdev_get_drvdata(rdev); + enum lp8788_buck_id id = rdev_get_id(rdev); + u8 addr; + + if (buck->dvs) + lp8788_set_dvs(buck, id); + + addr = lp8788_select_buck_vout_addr(buck, id); + if (!lp8788_is_valid_buck_addr(addr)) + return -EINVAL; + + return lp8788_update_bits(buck->lp, addr, LP8788_VOUT_M, selector); +} + +static int lp8788_buck12_get_voltage_sel(struct regulator_dev *rdev) +{ + struct lp8788_buck *buck = rdev_get_drvdata(rdev); + enum lp8788_buck_id id = rdev_get_id(rdev); + int ret; + u8 val, addr; + + addr = lp8788_select_buck_vout_addr(buck, id); + if (!lp8788_is_valid_buck_addr(addr)) + return -EINVAL; + + ret = lp8788_read_byte(buck->lp, addr, &val); + if (ret) + return ret; + + return val & LP8788_VOUT_M; +} + +static int lp8788_buck_enable_time(struct regulator_dev *rdev) +{ + struct lp8788_buck *buck = rdev_get_drvdata(rdev); + enum lp8788_buck_id id = rdev_get_id(rdev); + u8 val, addr = LP8788_BUCK1_TIMESTEP + id; + + if (lp8788_read_byte(buck->lp, addr, &val)) + return -EINVAL; + + val = (val & LP8788_STARTUP_TIME_M) >> LP8788_STARTUP_TIME_S; + + return ENABLE_TIME_USEC * val; +} + +static int lp8788_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct lp8788_buck *buck = rdev_get_drvdata(rdev); + enum lp8788_buck_id id = rdev_get_id(rdev); + u8 mask, val; + + mask = BUCK_FPWM_MASK(id); + switch (mode) { + case REGULATOR_MODE_FAST: + val = LP8788_FORCE_PWM << BUCK_FPWM_SHIFT(id); + break; + case REGULATOR_MODE_NORMAL: + val = LP8788_AUTO_PWM << BUCK_FPWM_SHIFT(id); + break; + default: + return -EINVAL; + } + + return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, mask, val); +} + +static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev) +{ + struct lp8788_buck *buck = rdev_get_drvdata(rdev); + enum lp8788_buck_id id = rdev_get_id(rdev); + u8 val; + int ret; + + ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val); + if (ret) + return ret; + + return val & BUCK_FPWM_MASK(id) ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops lp8788_buck12_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = lp8788_buck12_set_voltage_sel, + .get_voltage_sel = lp8788_buck12_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp8788_buck_enable_time, + .set_mode = lp8788_buck_set_mode, + .get_mode = lp8788_buck_get_mode, +}; + +static struct regulator_ops lp8788_buck34_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp8788_buck_enable_time, + .set_mode = lp8788_buck_set_mode, + .get_mode = lp8788_buck_get_mode, +}; + +static struct regulator_desc lp8788_buck_desc[] = { + { + .name = "buck1", + .id = BUCK1, + .ops = &lp8788_buck12_ops, + .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), + .volt_table = lp8788_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_BUCK, + .enable_mask = LP8788_EN_BUCK1_M, + }, + { + .name = "buck2", + .id = BUCK2, + .ops = &lp8788_buck12_ops, + .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), + .volt_table = lp8788_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_BUCK, + .enable_mask = LP8788_EN_BUCK2_M, + }, + { + .name = "buck3", + .id = BUCK3, + .ops = &lp8788_buck34_ops, + .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), + .volt_table = lp8788_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_BUCK3_VOUT, + .vsel_mask = LP8788_VOUT_M, + .enable_reg = LP8788_EN_BUCK, + .enable_mask = LP8788_EN_BUCK3_M, + }, + { + .name = "buck4", + .id = BUCK4, + .ops = &lp8788_buck34_ops, + .n_voltages = ARRAY_SIZE(lp8788_buck_vtbl), + .volt_table = lp8788_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_BUCK4_VOUT, + .vsel_mask = LP8788_VOUT_M, + .enable_reg = LP8788_EN_BUCK, + .enable_mask = LP8788_EN_BUCK4_M, + }, +}; + +static int lp8788_dvs_gpio_request(struct platform_device *pdev, + struct lp8788_buck *buck, + enum lp8788_buck_id id) +{ + struct lp8788_platform_data *pdata = buck->lp->pdata; + char *b1_name = "LP8788_B1_DVS"; + char *b2_name[] = { "LP8788_B2_DVS1", "LP8788_B2_DVS2" }; + int i, gpio, ret; + + switch (id) { + case BUCK1: + gpio = pdata->buck1_dvs->gpio; + ret = devm_gpio_request_one(&pdev->dev, gpio, DVS_LOW, + b1_name); + if (ret) + return ret; + + buck->dvs = pdata->buck1_dvs; + break; + case BUCK2: + for (i = 0; i < LP8788_NUM_BUCK2_DVS; i++) { + gpio = pdata->buck2_dvs->gpio[i]; + ret = devm_gpio_request_one(&pdev->dev, gpio, + DVS_LOW, b2_name[i]); + if (ret) + return ret; + } + buck->dvs = pdata->buck2_dvs; + break; + default: + break; + } + + return 0; +} + +static int lp8788_init_dvs(struct platform_device *pdev, + struct lp8788_buck *buck, enum lp8788_buck_id id) +{ + struct lp8788_platform_data *pdata = buck->lp->pdata; + u8 mask[] = { LP8788_BUCK1_DVS_SEL_M, LP8788_BUCK2_DVS_SEL_M }; + u8 val[] = { LP8788_BUCK1_DVS_PIN, LP8788_BUCK2_DVS_PIN }; + u8 default_dvs_mode[] = { LP8788_BUCK1_DVS_I2C, LP8788_BUCK2_DVS_I2C }; + + /* no dvs for buck3, 4 */ + if (id > BUCK2) + return 0; + + /* no dvs platform data, then dvs will be selected by I2C registers */ + if (!pdata) + goto set_default_dvs_mode; + + if ((id == BUCK1 && !pdata->buck1_dvs) || + (id == BUCK2 && !pdata->buck2_dvs)) + goto set_default_dvs_mode; + + if (lp8788_dvs_gpio_request(pdev, buck, id)) + goto set_default_dvs_mode; + + return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id], + val[id]); + +set_default_dvs_mode: + return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id], + default_dvs_mode[id]); +} + +static int lp8788_buck_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + int id = pdev->id; + struct lp8788_buck *buck; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int ret; + + if (id >= LP8788_NUM_BUCKS) + return -EINVAL; + + buck = devm_kzalloc(&pdev->dev, sizeof(struct lp8788_buck), GFP_KERNEL); + if (!buck) + return -ENOMEM; + + buck->lp = lp; + + ret = lp8788_init_dvs(pdev, buck, id); + if (ret) + return ret; + + cfg.dev = pdev->dev.parent; + cfg.init_data = lp->pdata ? lp->pdata->buck_data[id] : NULL; + cfg.driver_data = buck; + cfg.regmap = lp->regmap; + + rdev = regulator_register(&lp8788_buck_desc[id], &cfg); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&pdev->dev, "BUCK%d regulator register err = %d\n", + id + 1, ret); + return ret; + } + + buck->regulator = rdev; + platform_set_drvdata(pdev, buck); + + return 0; +} + +static int lp8788_buck_remove(struct platform_device *pdev) +{ + struct lp8788_buck *buck = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(buck->regulator); + + return 0; +} + +static struct platform_driver lp8788_buck_driver = { + .probe = lp8788_buck_probe, + .remove = lp8788_buck_remove, + .driver = { + .name = LP8788_DEV_BUCK, + .owner = THIS_MODULE, + }, +}; + +static int __init lp8788_buck_init(void) +{ + return platform_driver_register(&lp8788_buck_driver); +} +subsys_initcall(lp8788_buck_init); + +static void __exit lp8788_buck_exit(void) +{ + platform_driver_unregister(&lp8788_buck_driver); +} +module_exit(lp8788_buck_exit); + +MODULE_DESCRIPTION("TI LP8788 BUCK Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-buck"); diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c new file mode 100644 index 0000000..0ce2c4c --- /dev/null +++ b/drivers/regulator/lp8788-ldo.c @@ -0,0 +1,663 @@ +/* + * TI LP8788 MFD - ldo regulator driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* register address */ +#define LP8788_EN_LDO_A 0x0D /* DLDO 1 ~ 8 */ +#define LP8788_EN_LDO_B 0x0E /* DLDO 9 ~ 12, ALDO 1 ~ 4 */ +#define LP8788_EN_LDO_C 0x0F /* ALDO 5 ~ 10 */ +#define LP8788_EN_SEL 0x10 +#define LP8788_DLDO1_VOUT 0x2E +#define LP8788_DLDO2_VOUT 0x2F +#define LP8788_DLDO3_VOUT 0x30 +#define LP8788_DLDO4_VOUT 0x31 +#define LP8788_DLDO5_VOUT 0x32 +#define LP8788_DLDO6_VOUT 0x33 +#define LP8788_DLDO7_VOUT 0x34 +#define LP8788_DLDO8_VOUT 0x35 +#define LP8788_DLDO9_VOUT 0x36 +#define LP8788_DLDO10_VOUT 0x37 +#define LP8788_DLDO11_VOUT 0x38 +#define LP8788_DLDO12_VOUT 0x39 +#define LP8788_ALDO1_VOUT 0x3A +#define LP8788_ALDO2_VOUT 0x3B +#define LP8788_ALDO3_VOUT 0x3C +#define LP8788_ALDO4_VOUT 0x3D +#define LP8788_ALDO5_VOUT 0x3E +#define LP8788_ALDO6_VOUT 0x3F +#define LP8788_ALDO7_VOUT 0x40 +#define LP8788_ALDO8_VOUT 0x41 +#define LP8788_ALDO9_VOUT 0x42 +#define LP8788_ALDO10_VOUT 0x43 +#define LP8788_DLDO1_TIMESTEP 0x44 + +/* mask/shift bits */ +#define LP8788_EN_DLDO1_M BIT(0) /* Addr 0Dh ~ 0Fh */ +#define LP8788_EN_DLDO2_M BIT(1) +#define LP8788_EN_DLDO3_M BIT(2) +#define LP8788_EN_DLDO4_M BIT(3) +#define LP8788_EN_DLDO5_M BIT(4) +#define LP8788_EN_DLDO6_M BIT(5) +#define LP8788_EN_DLDO7_M BIT(6) +#define LP8788_EN_DLDO8_M BIT(7) +#define LP8788_EN_DLDO9_M BIT(0) +#define LP8788_EN_DLDO10_M BIT(1) +#define LP8788_EN_DLDO11_M BIT(2) +#define LP8788_EN_DLDO12_M BIT(3) +#define LP8788_EN_ALDO1_M BIT(4) +#define LP8788_EN_ALDO2_M BIT(5) +#define LP8788_EN_ALDO3_M BIT(6) +#define LP8788_EN_ALDO4_M BIT(7) +#define LP8788_EN_ALDO5_M BIT(0) +#define LP8788_EN_ALDO6_M BIT(1) +#define LP8788_EN_ALDO7_M BIT(2) +#define LP8788_EN_ALDO8_M BIT(3) +#define LP8788_EN_ALDO9_M BIT(4) +#define LP8788_EN_ALDO10_M BIT(5) +#define LP8788_EN_SEL_DLDO911_M BIT(0) /* Addr 10h */ +#define LP8788_EN_SEL_DLDO7_M BIT(1) +#define LP8788_EN_SEL_ALDO7_M BIT(2) +#define LP8788_EN_SEL_ALDO5_M BIT(3) +#define LP8788_EN_SEL_ALDO234_M BIT(4) +#define LP8788_EN_SEL_ALDO1_M BIT(5) +#define LP8788_VOUT_5BIT_M 0x1F /* Addr 2Eh ~ 43h */ +#define LP8788_VOUT_4BIT_M 0x0F +#define LP8788_VOUT_3BIT_M 0x07 +#define LP8788_VOUT_1BIT_M 0x01 +#define LP8788_STARTUP_TIME_M 0xF8 /* Addr 44h ~ 59h */ +#define LP8788_STARTUP_TIME_S 3 + +#define ENABLE_TIME_USEC 32 +#define ENABLE GPIOF_OUT_INIT_HIGH +#define DISABLE GPIOF_OUT_INIT_LOW + +enum lp8788_ldo_id { + DLDO1, + DLDO2, + DLDO3, + DLDO4, + DLDO5, + DLDO6, + DLDO7, + DLDO8, + DLDO9, + DLDO10, + DLDO11, + DLDO12, + ALDO1, + ALDO2, + ALDO3, + ALDO4, + ALDO5, + ALDO6, + ALDO7, + ALDO8, + ALDO9, + ALDO10, +}; + +struct lp8788_ldo { + struct lp8788 *lp; + struct regulator_desc *desc; + struct regulator_dev *regulator; + struct lp8788_ldo_enable_pin *en_pin; +}; + +/* DLDO 1, 2, 3, 9 voltage table */ +static const int lp8788_dldo1239_vtbl[] = { + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 2850000, 2850000, 2850000, + 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, + 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, +}; + +/* DLDO 4 voltage table */ +static const int lp8788_dldo4_vtbl[] = { 1800000, 3000000 }; + +/* DLDO 5, 7, 8 and ALDO 6 voltage table */ +static const int lp8788_dldo578_aldo6_vtbl[] = { + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 3000000, 3000000, 3000000, +}; + +/* DLDO 6 voltage table */ +static const int lp8788_dldo6_vtbl[] = { + 3000000, 3100000, 3200000, 3300000, 3400000, 3500000, 3600000, 3600000, +}; + +/* DLDO 10, 11 voltage table */ +static const int lp8788_dldo1011_vtbl[] = { + 1100000, 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, + 1500000, 1500000, 1500000, 1500000, 1500000, 1500000, 1500000, 1500000, +}; + +/* ALDO 1 voltage table */ +static const int lp8788_aldo1_vtbl[] = { 1800000, 2850000 }; + +/* ALDO 7 voltage table */ +static const int lp8788_aldo7_vtbl[] = { + 1200000, 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1800000, +}; + +static int lp8788_ldo_enable_time(struct regulator_dev *rdev) +{ + struct lp8788_ldo *ldo = rdev_get_drvdata(rdev); + enum lp8788_ldo_id id = rdev_get_id(rdev); + u8 val, addr = LP8788_DLDO1_TIMESTEP + id; + + if (lp8788_read_byte(ldo->lp, addr, &val)) + return -EINVAL; + + val = (val & LP8788_STARTUP_TIME_M) >> LP8788_STARTUP_TIME_S; + + return ENABLE_TIME_USEC * val; +} + +static struct regulator_ops lp8788_ldo_voltage_table_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp8788_ldo_enable_time, +}; + +static struct regulator_ops lp8788_ldo_voltage_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp8788_ldo_enable_time, +}; + +static struct regulator_desc lp8788_dldo_desc[] = { + { + .name = "dldo1", + .id = DLDO1, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo1239_vtbl), + .volt_table = lp8788_dldo1239_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO1_VOUT, + .vsel_mask = LP8788_VOUT_5BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO1_M, + }, + { + .name = "dldo2", + .id = DLDO2, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo1239_vtbl), + .volt_table = lp8788_dldo1239_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO2_VOUT, + .vsel_mask = LP8788_VOUT_5BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO2_M, + }, + { + .name = "dldo3", + .id = DLDO3, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo1239_vtbl), + .volt_table = lp8788_dldo1239_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO3_VOUT, + .vsel_mask = LP8788_VOUT_5BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO3_M, + }, + { + .name = "dldo4", + .id = DLDO4, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo4_vtbl), + .volt_table = lp8788_dldo4_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO4_VOUT, + .vsel_mask = LP8788_VOUT_1BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO4_M, + }, + { + .name = "dldo5", + .id = DLDO5, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo578_aldo6_vtbl), + .volt_table = lp8788_dldo578_aldo6_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO5_VOUT, + .vsel_mask = LP8788_VOUT_4BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO5_M, + }, + { + .name = "dldo6", + .id = DLDO6, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo6_vtbl), + .volt_table = lp8788_dldo6_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO6_VOUT, + .vsel_mask = LP8788_VOUT_3BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO6_M, + }, + { + .name = "dldo7", + .id = DLDO7, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo578_aldo6_vtbl), + .volt_table = lp8788_dldo578_aldo6_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO7_VOUT, + .vsel_mask = LP8788_VOUT_4BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO7_M, + }, + { + .name = "dldo8", + .id = DLDO8, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo578_aldo6_vtbl), + .volt_table = lp8788_dldo578_aldo6_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO8_VOUT, + .vsel_mask = LP8788_VOUT_4BIT_M, + .enable_reg = LP8788_EN_LDO_A, + .enable_mask = LP8788_EN_DLDO8_M, + }, + { + .name = "dldo9", + .id = DLDO9, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo1239_vtbl), + .volt_table = lp8788_dldo1239_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO9_VOUT, + .vsel_mask = LP8788_VOUT_5BIT_M, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_DLDO9_M, + }, + { + .name = "dldo10", + .id = DLDO10, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo1011_vtbl), + .volt_table = lp8788_dldo1011_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO10_VOUT, + .vsel_mask = LP8788_VOUT_4BIT_M, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_DLDO10_M, + }, + { + .name = "dldo11", + .id = DLDO11, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo1011_vtbl), + .volt_table = lp8788_dldo1011_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_DLDO11_VOUT, + .vsel_mask = LP8788_VOUT_4BIT_M, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_DLDO11_M, + }, + { + .name = "dldo12", + .id = DLDO12, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_DLDO12_M, + .min_uV = 2500000, + }, +}; + +static struct regulator_desc lp8788_aldo_desc[] = { + { + .name = "aldo1", + .id = ALDO1, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_aldo1_vtbl), + .volt_table = lp8788_aldo1_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_ALDO1_VOUT, + .vsel_mask = LP8788_VOUT_1BIT_M, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_ALDO1_M, + }, + { + .name = "aldo2", + .id = ALDO2, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_ALDO2_M, + .min_uV = 2850000, + }, + { + .name = "aldo3", + .id = ALDO3, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_ALDO3_M, + .min_uV = 2850000, + }, + { + .name = "aldo4", + .id = ALDO4, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_B, + .enable_mask = LP8788_EN_ALDO4_M, + .min_uV = 2850000, + }, + { + .name = "aldo5", + .id = ALDO5, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_C, + .enable_mask = LP8788_EN_ALDO5_M, + .min_uV = 2850000, + }, + { + .name = "aldo6", + .id = ALDO6, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_dldo578_aldo6_vtbl), + .volt_table = lp8788_dldo578_aldo6_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_ALDO6_VOUT, + .vsel_mask = LP8788_VOUT_4BIT_M, + .enable_reg = LP8788_EN_LDO_C, + .enable_mask = LP8788_EN_ALDO6_M, + }, + { + .name = "aldo7", + .id = ALDO7, + .ops = &lp8788_ldo_voltage_table_ops, + .n_voltages = ARRAY_SIZE(lp8788_aldo7_vtbl), + .volt_table = lp8788_aldo7_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8788_ALDO7_VOUT, + .vsel_mask = LP8788_VOUT_3BIT_M, + .enable_reg = LP8788_EN_LDO_C, + .enable_mask = LP8788_EN_ALDO7_M, + }, + { + .name = "aldo8", + .id = ALDO8, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_C, + .enable_mask = LP8788_EN_ALDO8_M, + .min_uV = 2500000, + }, + { + .name = "aldo9", + .id = ALDO9, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_C, + .enable_mask = LP8788_EN_ALDO9_M, + .min_uV = 2500000, + }, + { + .name = "aldo10", + .id = ALDO10, + .ops = &lp8788_ldo_voltage_fixed_ops, + .n_voltages = 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8788_EN_LDO_C, + .enable_mask = LP8788_EN_ALDO10_M, + .min_uV = 1100000, + }, +}; + +static int lp8788_config_ldo_enable_mode(struct platform_device *pdev, + struct lp8788_ldo *ldo, + enum lp8788_ldo_id id) +{ + struct lp8788 *lp = ldo->lp; + struct lp8788_platform_data *pdata = lp->pdata; + enum lp8788_ext_ldo_en_id enable_id; + u8 en_mask[] = { + [EN_ALDO1] = LP8788_EN_SEL_ALDO1_M, + [EN_ALDO234] = LP8788_EN_SEL_ALDO234_M, + [EN_ALDO5] = LP8788_EN_SEL_ALDO5_M, + [EN_ALDO7] = LP8788_EN_SEL_ALDO7_M, + [EN_DLDO7] = LP8788_EN_SEL_DLDO7_M, + [EN_DLDO911] = LP8788_EN_SEL_DLDO911_M, + }; + + switch (id) { + case DLDO7: + enable_id = EN_DLDO7; + break; + case DLDO9: + case DLDO11: + enable_id = EN_DLDO911; + break; + case ALDO1: + enable_id = EN_ALDO1; + break; + case ALDO2 ... ALDO4: + enable_id = EN_ALDO234; + break; + case ALDO5: + enable_id = EN_ALDO5; + break; + case ALDO7: + enable_id = EN_ALDO7; + break; + default: + return 0; + } + + /* if no platform data for ldo pin, then set default enable mode */ + if (!pdata || !pdata->ldo_pin || !pdata->ldo_pin[enable_id]) + goto set_default_ldo_enable_mode; + + ldo->en_pin = pdata->ldo_pin[enable_id]; + return 0; + +set_default_ldo_enable_mode: + return lp8788_update_bits(lp, LP8788_EN_SEL, en_mask[enable_id], 0); +} + +static int lp8788_dldo_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + int id = pdev->id; + struct lp8788_ldo *ldo; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int ret; + + ldo = devm_kzalloc(&pdev->dev, sizeof(struct lp8788_ldo), GFP_KERNEL); + if (!ldo) + return -ENOMEM; + + ldo->lp = lp; + ret = lp8788_config_ldo_enable_mode(pdev, ldo, id); + if (ret) + return ret; + + if (ldo->en_pin) { + cfg.ena_gpio = ldo->en_pin->gpio; + cfg.ena_gpio_flags = ldo->en_pin->init_state; + } + + cfg.dev = pdev->dev.parent; + cfg.init_data = lp->pdata ? lp->pdata->dldo_data[id] : NULL; + cfg.driver_data = ldo; + cfg.regmap = lp->regmap; + + rdev = regulator_register(&lp8788_dldo_desc[id], &cfg); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&pdev->dev, "DLDO%d regulator register err = %d\n", + id + 1, ret); + return ret; + } + + ldo->regulator = rdev; + platform_set_drvdata(pdev, ldo); + + return 0; +} + +static int lp8788_dldo_remove(struct platform_device *pdev) +{ + struct lp8788_ldo *ldo = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(ldo->regulator); + + return 0; +} + +static struct platform_driver lp8788_dldo_driver = { + .probe = lp8788_dldo_probe, + .remove = lp8788_dldo_remove, + .driver = { + .name = LP8788_DEV_DLDO, + .owner = THIS_MODULE, + }, +}; + +static int lp8788_aldo_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + int id = pdev->id; + struct lp8788_ldo *ldo; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int ret; + + ldo = devm_kzalloc(&pdev->dev, sizeof(struct lp8788_ldo), GFP_KERNEL); + if (!ldo) + return -ENOMEM; + + ldo->lp = lp; + ret = lp8788_config_ldo_enable_mode(pdev, ldo, id + ALDO1); + if (ret) + return ret; + + if (ldo->en_pin) { + cfg.ena_gpio = ldo->en_pin->gpio; + cfg.ena_gpio_flags = ldo->en_pin->init_state; + } + + cfg.dev = pdev->dev.parent; + cfg.init_data = lp->pdata ? lp->pdata->aldo_data[id] : NULL; + cfg.driver_data = ldo; + cfg.regmap = lp->regmap; + + rdev = regulator_register(&lp8788_aldo_desc[id], &cfg); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&pdev->dev, "ALDO%d regulator register err = %d\n", + id + 1, ret); + return ret; + } + + ldo->regulator = rdev; + platform_set_drvdata(pdev, ldo); + + return 0; +} + +static int lp8788_aldo_remove(struct platform_device *pdev) +{ + struct lp8788_ldo *ldo = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(ldo->regulator); + + return 0; +} + +static struct platform_driver lp8788_aldo_driver = { + .probe = lp8788_aldo_probe, + .remove = lp8788_aldo_remove, + .driver = { + .name = LP8788_DEV_ALDO, + .owner = THIS_MODULE, + }, +}; + +static int __init lp8788_ldo_init(void) +{ + int ret; + + ret = platform_driver_register(&lp8788_dldo_driver); + if (ret) + return ret; + + return platform_driver_register(&lp8788_aldo_driver); +} +subsys_initcall(lp8788_ldo_init); + +static void __exit lp8788_ldo_exit(void) +{ + platform_driver_unregister(&lp8788_aldo_driver); + platform_driver_unregister(&lp8788_dldo_driver); +} +module_exit(lp8788_ldo_exit); + +MODULE_DESCRIPTION("TI LP8788 LDO Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-dldo"); +MODULE_ALIAS("platform:lp8788-aldo"); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c new file mode 100644 index 0000000..54af610 --- /dev/null +++ b/drivers/regulator/max1586.c @@ -0,0 +1,270 @@ +/* + * max1586.c -- Voltage and current regulation for the Maxim 1586 + * + * Copyright (C) 2008 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +#define MAX1586_V3_MAX_VSEL 31 +#define MAX1586_V6_MAX_VSEL 3 + +#define MAX1586_V3_MIN_UV 700000 +#define MAX1586_V3_MAX_UV 1475000 + +#define MAX1586_V6_MIN_UV 0 +#define MAX1586_V6_MAX_UV 3000000 + +#define I2C_V3_SELECT (0 << 5) +#define I2C_V6_SELECT (1 << 5) + +struct max1586_data { + struct i2c_client *client; + + /* min/max V3 voltage */ + unsigned int min_uV; + unsigned int max_uV; + + unsigned int v3_curr_sel; + unsigned int v6_curr_sel; + + struct regulator_dev *rdev[0]; +}; + +/* + * V6 voltage + * On I2C bus, sending a "x" byte to the max1586 means : + * set V6 to either 0V, 1.8V, 2.5V, 3V depending on (x & 0x3) + * As regulator framework doesn't accept voltages to be 0V, we use 1uV. + */ +static const unsigned int v6_voltages_uv[] = { 1, 1800000, 2500000, 3000000 }; + +/* + * V3 voltage + * On I2C bus, sending a "x" byte to the max1586 means : + * set V3 to 0.700V + (x & 0x1f) * 0.025V + * This voltage can be increased by external resistors + * R24 and R25=100kOhm as described in the data sheet. + * The gain is approximately: 1 + R24/R25 + R24/185.5kOhm + */ +static int max1586_v3_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max1586_data *max1586 = rdev_get_drvdata(rdev); + + return max1586->v3_curr_sel; +} + +static int max1586_v3_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct max1586_data *max1586 = rdev_get_drvdata(rdev); + struct i2c_client *client = max1586->client; + int ret; + u8 v3_prog; + + dev_dbg(&client->dev, "changing voltage v3 to %dmv\n", + regulator_list_voltage_linear(rdev, selector) / 1000); + + v3_prog = I2C_V3_SELECT | (u8) selector; + ret = i2c_smbus_write_byte(client, v3_prog); + if (ret) + return ret; + + max1586->v3_curr_sel = selector; + + return 0; +} + +static int max1586_v6_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max1586_data *max1586 = rdev_get_drvdata(rdev); + + return max1586->v6_curr_sel; +} + +static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max1586_data *max1586 = rdev_get_drvdata(rdev); + struct i2c_client *client = max1586->client; + u8 v6_prog; + int ret; + + dev_dbg(&client->dev, "changing voltage v6 to %dmv\n", + rdev->desc->volt_table[selector] / 1000); + + v6_prog = I2C_V6_SELECT | (u8) selector; + ret = i2c_smbus_write_byte(client, v6_prog); + if (ret) + return ret; + + max1586->v6_curr_sel = selector; + + return 0; +} + +/* + * The Maxim 1586 controls V3 and V6 voltages, but offers no way of reading back + * the set up value. + */ +static struct regulator_ops max1586_v3_ops = { + .get_voltage_sel = max1586_v3_get_voltage_sel, + .set_voltage_sel = max1586_v3_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static struct regulator_ops max1586_v6_ops = { + .get_voltage_sel = max1586_v6_get_voltage_sel, + .set_voltage_sel = max1586_v6_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static struct regulator_desc max1586_reg[] = { + { + .name = "Output_V3", + .id = MAX1586_V3, + .ops = &max1586_v3_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX1586_V3_MAX_VSEL + 1, + .owner = THIS_MODULE, + }, + { + .name = "Output_V6", + .id = MAX1586_V6, + .ops = &max1586_v6_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX1586_V6_MAX_VSEL + 1, + .volt_table = v6_voltages_uv, + .owner = THIS_MODULE, + }, +}; + +static int max1586_pmic_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct regulator_dev **rdev; + struct max1586_platform_data *pdata = client->dev.platform_data; + struct regulator_config config = { }; + struct max1586_data *max1586; + int i, id, ret = -ENOMEM; + + max1586 = devm_kzalloc(&client->dev, sizeof(struct max1586_data) + + sizeof(struct regulator_dev *) * (MAX1586_V6 + 1), + GFP_KERNEL); + if (!max1586) + return -ENOMEM; + + max1586->client = client; + + if (!pdata->v3_gain) + return -EINVAL; + + max1586->min_uV = MAX1586_V3_MIN_UV / 1000 * pdata->v3_gain / 1000; + max1586->max_uV = MAX1586_V3_MAX_UV / 1000 * pdata->v3_gain / 1000; + + /* Set curr_sel to default voltage on power-up */ + max1586->v3_curr_sel = 24; /* 1.3V */ + max1586->v6_curr_sel = 0; + + rdev = max1586->rdev; + for (i = 0; i < pdata->num_subdevs && i <= MAX1586_V6; i++) { + id = pdata->subdevs[i].id; + if (!pdata->subdevs[i].platform_data) + continue; + if (id < MAX1586_V3 || id > MAX1586_V6) { + dev_err(&client->dev, "invalid regulator id %d\n", id); + goto err; + } + + if (id == MAX1586_V3) { + max1586_reg[id].min_uV = max1586->min_uV; + max1586_reg[id].uV_step = + (max1586->max_uV - max1586->min_uV) / + MAX1586_V3_MAX_VSEL; + } + + config.dev = &client->dev; + config.init_data = pdata->subdevs[i].platform_data; + config.driver_data = max1586; + + rdev[i] = regulator_register(&max1586_reg[id], &config); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(&client->dev, "failed to register %s\n", + max1586_reg[id].name); + goto err; + } + } + + i2c_set_clientdata(client, max1586); + dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n"); + return 0; + +err: + while (--i >= 0) + regulator_unregister(rdev[i]); + return ret; +} + +static int max1586_pmic_remove(struct i2c_client *client) +{ + struct max1586_data *max1586 = i2c_get_clientdata(client); + int i; + + for (i = 0; i <= MAX1586_V6; i++) + regulator_unregister(max1586->rdev[i]); + return 0; +} + +static const struct i2c_device_id max1586_id[] = { + { "max1586", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max1586_id); + +static struct i2c_driver max1586_pmic_driver = { + .probe = max1586_pmic_probe, + .remove = max1586_pmic_remove, + .driver = { + .name = "max1586", + .owner = THIS_MODULE, + }, + .id_table = max1586_id, +}; + +static int __init max1586_pmic_init(void) +{ + return i2c_add_driver(&max1586_pmic_driver); +} +subsys_initcall(max1586_pmic_init); + +static void __exit max1586_pmic_exit(void) +{ + i2c_del_driver(&max1586_pmic_driver); +} +module_exit(max1586_pmic_exit); + +/* Module information */ +MODULE_DESCRIPTION("MAXIM 1586 voltage regulator driver"); +MODULE_AUTHOR("Robert Jarzmik"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c new file mode 100644 index 0000000..20935b1 --- /dev/null +++ b/drivers/regulator/max77686.c @@ -0,0 +1,540 @@ +/* + * max77686.c - Regulator driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electronics + * Chiwoong Byun + * Jonghwa Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77686_LDO_MINUV 800000 +#define MAX77686_LDO_UVSTEP 50000 +#define MAX77686_LDO_LOW_MINUV 800000 +#define MAX77686_LDO_LOW_UVSTEP 25000 +#define MAX77686_BUCK_MINUV 750000 +#define MAX77686_BUCK_UVSTEP 50000 +#define MAX77686_RAMP_DELAY 100000 /* uV/us */ +#define MAX77686_DVS_RAMP_DELAY 27500 /* uV/us */ +#define MAX77686_DVS_MINUV 600000 +#define MAX77686_DVS_UVSTEP 12500 + +#define MAX77686_OPMODE_SHIFT 6 +#define MAX77686_OPMODE_BUCK234_SHIFT 4 +#define MAX77686_OPMODE_MASK 0x3 + +#define MAX77686_VSEL_MASK 0x3F +#define MAX77686_DVS_VSEL_MASK 0xFF + +#define MAX77686_RAMP_RATE_MASK 0xC0 + +#define MAX77686_REGULATORS MAX77686_REG_MAX +#define MAX77686_LDOS 26 + +enum max77686_ramp_rate { + RAMP_RATE_13P75MV, + RAMP_RATE_27P5MV, + RAMP_RATE_55MV, + RAMP_RATE_NO_CTRL, /* 100mV/us */ +}; + +struct max77686_data { + struct regulator_dev *rdev[MAX77686_REGULATORS]; + unsigned int opmode[MAX77686_REGULATORS]; +}; + +/* Some BUCKS supports Normal[ON/OFF] mode during suspend */ +static int max77686_buck_set_suspend_disable(struct regulator_dev *rdev) +{ + unsigned int val; + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + + if (id == MAX77686_BUCK1) + val = 0x1; + else + val = 0x1 << MAX77686_OPMODE_BUCK234_SHIFT; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); + if (ret) + return ret; + + max77686->opmode[id] = val; + return 0; +} + +/* Some LDOs supports [LPM/Normal]ON mode during suspend state */ +static int max77686_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + unsigned int val; + int ret, id = rdev_get_id(rdev); + + /* BUCK[5-9] doesn't support this feature */ + if (id >= MAX77686_BUCK5) + return 0; + + switch (mode) { + case REGULATOR_MODE_IDLE: /* ON in LP Mode */ + val = 0x2 << MAX77686_OPMODE_SHIFT; + break; + case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ + val = 0x3 << MAX77686_OPMODE_SHIFT; + break; + default: + pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", + rdev->desc->name, mode); + return -EINVAL; + } + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); + if (ret) + return ret; + + max77686->opmode[id] = val; + return 0; +} + +/* Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state */ +static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + unsigned int val; + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + int ret; + + switch (mode) { + case REGULATOR_MODE_STANDBY: /* switch off */ + val = 0x1 << MAX77686_OPMODE_SHIFT; + break; + case REGULATOR_MODE_IDLE: /* ON in LP Mode */ + val = 0x2 << MAX77686_OPMODE_SHIFT; + break; + case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ + val = 0x3 << MAX77686_OPMODE_SHIFT; + break; + default: + pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", + rdev->desc->name, mode); + return -EINVAL; + } + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); + if (ret) + return ret; + + max77686->opmode[rdev_get_id(rdev)] = val; + return 0; +} + +static int max77686_enable(struct regulator_dev *rdev) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, + max77686->opmode[rdev_get_id(rdev)]); +} + +static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + unsigned int ramp_value = RAMP_RATE_NO_CTRL; + + switch (ramp_delay) { + case 1 ... 13750: + ramp_value = RAMP_RATE_13P75MV; + break; + case 13751 ... 27500: + ramp_value = RAMP_RATE_27P5MV; + break; + case 27501 ... 55000: + ramp_value = RAMP_RATE_55MV; + break; + case 55001 ... 100000: + break; + default: + pr_warn("%s: ramp_delay: %d not supported, setting 100000\n", + rdev->desc->name, ramp_delay); + } + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX77686_RAMP_RATE_MASK, ramp_value << 6); +} + +static struct regulator_ops max77686_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = max77686_enable, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_suspend_mode = max77686_set_suspend_mode, +}; + +static struct regulator_ops max77686_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = max77686_enable, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_suspend_mode = max77686_ldo_set_suspend_mode, +}; + +static struct regulator_ops max77686_buck1_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = max77686_enable, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_suspend_disable = max77686_buck_set_suspend_disable, +}; + +static struct regulator_ops max77686_buck_dvs_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = max77686_enable, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = max77686_set_ramp_delay, + .set_suspend_disable = max77686_buck_set_suspend_disable, +}; + +#define regulator_desc_ldo(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_LDO_MINUV, \ + .uV_step = MAX77686_LDO_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_SHIFT, \ +} +#define regulator_desc_lpm_ldo(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_LDO_MINUV, \ + .uV_step = MAX77686_LDO_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_SHIFT, \ +} +#define regulator_desc_ldo_low(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_LDO_LOW_MINUV, \ + .uV_step = MAX77686_LDO_LOW_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_SHIFT, \ +} +#define regulator_desc_ldo1_low(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_LDO_LOW_MINUV, \ + .uV_step = MAX77686_LDO_LOW_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_SHIFT, \ +} +#define regulator_desc_buck(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_BUCK_MINUV, \ + .uV_step = MAX77686_BUCK_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_BUCK5OUT + (num - 5) * 2, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_BUCK5CTRL + (num - 5) * 2, \ + .enable_mask = MAX77686_OPMODE_MASK, \ +} +#define regulator_desc_buck1(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_buck1_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_BUCK_MINUV, \ + .uV_step = MAX77686_BUCK_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_BUCK1OUT, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_BUCK1CTRL, \ + .enable_mask = MAX77686_OPMODE_MASK, \ +} +#define regulator_desc_buck_dvs(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_buck_dvs_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_DVS_MINUV, \ + .uV_step = MAX77686_DVS_UVSTEP, \ + .ramp_delay = MAX77686_DVS_RAMP_DELAY, \ + .n_voltages = MAX77686_DVS_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10, \ + .vsel_mask = MAX77686_DVS_VSEL_MASK, \ + .enable_reg = MAX77686_REG_BUCK2CTRL1 + (num - 2) * 10, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_BUCK234_SHIFT, \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_ldo1_low(1), + regulator_desc_ldo_low(2), + regulator_desc_ldo(3), + regulator_desc_ldo(4), + regulator_desc_ldo(5), + regulator_desc_ldo_low(6), + regulator_desc_ldo_low(7), + regulator_desc_ldo_low(8), + regulator_desc_ldo(9), + regulator_desc_lpm_ldo(10), + regulator_desc_lpm_ldo(11), + regulator_desc_lpm_ldo(12), + regulator_desc_ldo(13), + regulator_desc_lpm_ldo(14), + regulator_desc_ldo_low(15), + regulator_desc_lpm_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_buck1(1), + regulator_desc_buck_dvs(2), + regulator_desc_buck_dvs(3), + regulator_desc_buck_dvs(4), + regulator_desc_buck(5), + regulator_desc_buck(6), + regulator_desc_buck(7), + regulator_desc_buck(8), + regulator_desc_buck(9), +}; + +#ifdef CONFIG_OF +static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, + struct max77686_platform_data *pdata) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct device_node *pmic_np, *regulators_np; + struct max77686_regulator_data *rdata; + struct of_regulator_match rmatch; + unsigned int i; + + pmic_np = iodev->dev->of_node; + regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators"); + if (!regulators_np) { + dev_err(&pdev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + pdata->num_regulators = ARRAY_SIZE(regulators); + rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(&pdev->dev, + "could not allocate memory for regulator data\n"); + return -ENOMEM; + } + + for (i = 0; i < pdata->num_regulators; i++) { + rmatch.name = regulators[i].name; + rmatch.init_data = NULL; + rmatch.of_node = NULL; + of_regulator_match(&pdev->dev, regulators_np, &rmatch, 1); + rdata[i].initdata = rmatch.init_data; + rdata[i].of_node = rmatch.of_node; + } + + pdata->regulators = rdata; + + return 0; +} +#else +static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, + struct max77686_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int max77686_pmic_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max77686_data *max77686; + int i, ret = 0; + struct regulator_config config = { }; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + if (!pdata) { + dev_err(&pdev->dev, "no platform data found for regulator\n"); + return -ENODEV; + } + + if (iodev->dev->of_node) { + ret = max77686_pmic_dt_parse_pdata(pdev, pdata); + if (ret) + return ret; + } + + if (pdata->num_regulators != MAX77686_REGULATORS) { + dev_err(&pdev->dev, + "Invalid initial data for regulator's initialiation\n"); + return -EINVAL; + } + + max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data), + GFP_KERNEL); + if (!max77686) + return -ENOMEM; + + config.dev = &pdev->dev; + config.regmap = iodev->regmap; + config.driver_data = max77686; + platform_set_drvdata(pdev, max77686); + + for (i = 0; i < MAX77686_REGULATORS; i++) { + config.init_data = pdata->regulators[i].initdata; + config.of_node = pdata->regulators[i].of_node; + + max77686->opmode[i] = regulators[i].enable_mask; + max77686->rdev[i] = regulator_register(®ulators[i], &config); + if (IS_ERR(max77686->rdev[i])) { + ret = PTR_ERR(max77686->rdev[i]); + dev_err(&pdev->dev, + "regulator init failed for %d\n", i); + max77686->rdev[i] = NULL; + goto err; + } + } + + return 0; +err: + while (--i >= 0) + regulator_unregister(max77686->rdev[i]); + return ret; +} + +static int max77686_pmic_remove(struct platform_device *pdev) +{ + struct max77686_data *max77686 = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < MAX77686_REGULATORS; i++) + regulator_unregister(max77686->rdev[i]); + + return 0; +} + +static const struct platform_device_id max77686_pmic_id[] = { + {"max77686-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77686_pmic_id); + +static struct platform_driver max77686_pmic_driver = { + .driver = { + .name = "max77686-pmic", + .owner = THIS_MODULE, + }, + .probe = max77686_pmic_probe, + .remove = max77686_pmic_remove, + .id_table = max77686_pmic_id, +}; + +static int __init max77686_pmic_init(void) +{ + return platform_driver_register(&max77686_pmic_driver); +} +subsys_initcall(max77686_pmic_init); + +static void __exit max77686_pmic_cleanup(void) +{ + platform_driver_unregister(&max77686_pmic_driver); +} +module_exit(max77686_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver"); +MODULE_AUTHOR("Chiwoong Byun "); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c new file mode 100644 index 0000000..db6c9be --- /dev/null +++ b/drivers/regulator/max8649.c @@ -0,0 +1,287 @@ +/* + * Regulators driver for Maxim max8649 + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX8649_DCDC_VMIN 750000 /* uV */ +#define MAX8649_DCDC_VMAX 1380000 /* uV */ +#define MAX8649_DCDC_STEP 10000 /* uV */ +#define MAX8649_VOL_MASK 0x3f + +/* Registers */ +#define MAX8649_MODE0 0x00 +#define MAX8649_MODE1 0x01 +#define MAX8649_MODE2 0x02 +#define MAX8649_MODE3 0x03 +#define MAX8649_CONTROL 0x04 +#define MAX8649_SYNC 0x05 +#define MAX8649_RAMP 0x06 +#define MAX8649_CHIP_ID1 0x08 +#define MAX8649_CHIP_ID2 0x09 + +/* Bits */ +#define MAX8649_EN_PD (1 << 7) +#define MAX8649_VID0_PD (1 << 6) +#define MAX8649_VID1_PD (1 << 5) +#define MAX8649_VID_MASK (3 << 5) + +#define MAX8649_FORCE_PWM (1 << 7) +#define MAX8649_SYNC_EXTCLK (1 << 6) + +#define MAX8649_EXT_MASK (3 << 6) + +#define MAX8649_RAMP_MASK (7 << 5) +#define MAX8649_RAMP_DOWN (1 << 1) + +struct max8649_regulator_info { + struct regulator_dev *regulator; + struct device *dev; + struct regmap *regmap; + + unsigned mode:2; /* bit[1:0] = VID1, VID0 */ + unsigned extclk_freq:2; + unsigned extclk:1; + unsigned ramp_timing:3; + unsigned ramp_down:1; +}; + +static int max8649_enable_time(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int voltage, rate, ret; + unsigned int val; + + /* get voltage */ + ret = regmap_read(info->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + val &= MAX8649_VOL_MASK; + voltage = regulator_list_voltage_linear(rdev, (unsigned char)val); + + /* get rate */ + ret = regmap_read(info->regmap, MAX8649_RAMP, &val); + if (ret != 0) + return ret; + ret = (val & MAX8649_RAMP_MASK) >> 5; + rate = (32 * 1000) >> ret; /* uV/uS */ + + return DIV_ROUND_UP(voltage, rate); +} + +static int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + + switch (mode) { + case REGULATOR_MODE_FAST: + regmap_update_bits(info->regmap, rdev->desc->vsel_reg, + MAX8649_FORCE_PWM, MAX8649_FORCE_PWM); + break; + case REGULATOR_MODE_NORMAL: + regmap_update_bits(info->regmap, rdev->desc->vsel_reg, + MAX8649_FORCE_PWM, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned int max8649_get_mode(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + unsigned int val; + int ret; + + ret = regmap_read(info->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + if (val & MAX8649_FORCE_PWM) + return REGULATOR_MODE_FAST; + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops max8649_dcdc_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = max8649_enable_time, + .set_mode = max8649_set_mode, + .get_mode = max8649_get_mode, + +}; + +static struct regulator_desc dcdc_desc = { + .name = "max8649", + .ops = &max8649_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1 << 6, + .owner = THIS_MODULE, + .vsel_mask = MAX8649_VOL_MASK, + .min_uV = MAX8649_DCDC_VMIN, + .uV_step = MAX8649_DCDC_STEP, + .enable_reg = MAX8649_CONTROL, + .enable_mask = MAX8649_EN_PD, + .enable_is_inverted = true, +}; + +static struct regmap_config max8649_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int max8649_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max8649_platform_data *pdata = client->dev.platform_data; + struct max8649_regulator_info *info = NULL; + struct regulator_config config = { }; + unsigned int val; + unsigned char data; + int ret; + + info = devm_kzalloc(&client->dev, sizeof(struct max8649_regulator_info), + GFP_KERNEL); + if (!info) { + dev_err(&client->dev, "No enough memory\n"); + return -ENOMEM; + } + + info->regmap = devm_regmap_init_i2c(client, &max8649_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + info->dev = &client->dev; + i2c_set_clientdata(client, info); + + info->mode = pdata->mode; + switch (info->mode) { + case 0: + dcdc_desc.vsel_reg = MAX8649_MODE0; + break; + case 1: + dcdc_desc.vsel_reg = MAX8649_MODE1; + break; + case 2: + dcdc_desc.vsel_reg = MAX8649_MODE2; + break; + case 3: + dcdc_desc.vsel_reg = MAX8649_MODE3; + break; + default: + break; + } + + ret = regmap_read(info->regmap, MAX8649_CHIP_ID1, &val); + if (ret != 0) { + dev_err(info->dev, "Failed to detect ID of MAX8649:%d\n", + ret); + return ret; + } + dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", val); + + /* enable VID0 & VID1 */ + regmap_update_bits(info->regmap, MAX8649_CONTROL, MAX8649_VID_MASK, 0); + + /* enable/disable external clock synchronization */ + info->extclk = pdata->extclk; + data = (info->extclk) ? MAX8649_SYNC_EXTCLK : 0; + regmap_update_bits(info->regmap, dcdc_desc.vsel_reg, + MAX8649_SYNC_EXTCLK, data); + if (info->extclk) { + /* set external clock frequency */ + info->extclk_freq = pdata->extclk_freq; + regmap_update_bits(info->regmap, MAX8649_SYNC, MAX8649_EXT_MASK, + info->extclk_freq << 6); + } + + if (pdata->ramp_timing) { + info->ramp_timing = pdata->ramp_timing; + regmap_update_bits(info->regmap, MAX8649_RAMP, MAX8649_RAMP_MASK, + info->ramp_timing << 5); + } + + info->ramp_down = pdata->ramp_down; + if (info->ramp_down) { + regmap_update_bits(info->regmap, MAX8649_RAMP, MAX8649_RAMP_DOWN, + MAX8649_RAMP_DOWN); + } + + config.dev = &client->dev; + config.init_data = pdata->regulator; + config.driver_data = info; + config.regmap = info->regmap; + + info->regulator = regulator_register(&dcdc_desc, &config); + if (IS_ERR(info->regulator)) { + dev_err(info->dev, "failed to register regulator %s\n", + dcdc_desc.name); + return PTR_ERR(info->regulator); + } + + return 0; +} + +static int max8649_regulator_remove(struct i2c_client *client) +{ + struct max8649_regulator_info *info = i2c_get_clientdata(client); + + if (info) + regulator_unregister(info->regulator); + + return 0; +} + +static const struct i2c_device_id max8649_id[] = { + { "max8649", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8649_id); + +static struct i2c_driver max8649_driver = { + .probe = max8649_regulator_probe, + .remove = max8649_regulator_remove, + .driver = { + .name = "max8649", + }, + .id_table = max8649_id, +}; + +static int __init max8649_init(void) +{ + return i2c_add_driver(&max8649_driver); +} +subsys_initcall(max8649_init); + +static void __exit max8649_exit(void) +{ + i2c_del_driver(&max8649_driver); +} +module_exit(max8649_exit); + +/* Module information */ +MODULE_DESCRIPTION("MAXIM 8649 voltage regulator driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c new file mode 100644 index 0000000..d428ef9 --- /dev/null +++ b/drivers/regulator/max8660.c @@ -0,0 +1,465 @@ +/* + * max8660.c -- Voltage regulation for the Maxim 8660/8661 + * + * based on max1586.c and wm8400-regulator.c + * + * Copyright (C) 2009 Wolfram Sang, Pengutronix e.K. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Some info: + * + * Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8660-MAX8661.pdf + * + * This chip is a bit nasty because it is a write-only device. Thus, the driver + * uses shadow registers to keep track of its values. The main problem appears + * to be the initialization: When Linux boots up, we cannot know if the chip is + * in the default state or not, so we would have to pass such information in + * platform_data. As this adds a bit of complexity to the driver, this is left + * out for now until it is really needed. + * + * [A|S|M]DTV1 registers are currently not used, but [A|S|M]DTV2. + * + * If the driver is feature complete, it might be worth to check if one set of + * functions for V3-V7 is sufficient. For maximum flexibility during + * development, they are separated for now. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX8660_DCDC_MIN_UV 725000 +#define MAX8660_DCDC_MAX_UV 1800000 +#define MAX8660_DCDC_STEP 25000 +#define MAX8660_DCDC_MAX_SEL 0x2b + +#define MAX8660_LDO5_MIN_UV 1700000 +#define MAX8660_LDO5_MAX_UV 2000000 +#define MAX8660_LDO5_STEP 25000 +#define MAX8660_LDO5_MAX_SEL 0x0c + +#define MAX8660_LDO67_MIN_UV 1800000 +#define MAX8660_LDO67_MAX_UV 3300000 +#define MAX8660_LDO67_STEP 100000 +#define MAX8660_LDO67_MAX_SEL 0x0f + +enum { + MAX8660_OVER1, + MAX8660_OVER2, + MAX8660_VCC1, + MAX8660_ADTV1, + MAX8660_ADTV2, + MAX8660_SDTV1, + MAX8660_SDTV2, + MAX8660_MDTV1, + MAX8660_MDTV2, + MAX8660_L12VCR, + MAX8660_FPWM, + MAX8660_N_REGS, /* not a real register */ +}; + +struct max8660 { + struct i2c_client *client; + u8 shadow_regs[MAX8660_N_REGS]; /* as chip is write only */ + struct regulator_dev *rdev[]; +}; + +static int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val) +{ + static const u8 max8660_addresses[MAX8660_N_REGS] = + { 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 }; + + int ret; + u8 reg_val = (max8660->shadow_regs[reg] & mask) | val; + dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n", + max8660_addresses[reg], reg_val); + + ret = i2c_smbus_write_byte_data(max8660->client, + max8660_addresses[reg], reg_val); + if (ret == 0) + max8660->shadow_regs[reg] = reg_val; + + return ret; +} + + +/* + * DCDC functions + */ + +static int max8660_dcdc_is_enabled(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 val = max8660->shadow_regs[MAX8660_OVER1]; + u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; + return !!(val & mask); +} + +static int max8660_dcdc_enable(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; + return max8660_write(max8660, MAX8660_OVER1, 0xff, bit); +} + +static int max8660_dcdc_disable(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4; + return max8660_write(max8660, MAX8660_OVER1, mask, 0); +} + +static int max8660_dcdc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + + u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; + u8 selector = max8660->shadow_regs[reg]; + return selector; +} + +static int max8660_dcdc_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 reg, bits; + int ret; + + reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; + ret = max8660_write(max8660, reg, 0, selector); + if (ret) + return ret; + + /* Select target voltage register and activate regulation */ + bits = (rdev_get_id(rdev) == MAX8660_V3) ? 0x03 : 0x30; + return max8660_write(max8660, MAX8660_VCC1, 0xff, bits); +} + +static struct regulator_ops max8660_dcdc_ops = { + .is_enabled = max8660_dcdc_is_enabled, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = max8660_dcdc_set_voltage_sel, + .get_voltage_sel = max8660_dcdc_get_voltage_sel, +}; + + +/* + * LDO5 functions + */ + +static int max8660_ldo5_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + + u8 selector = max8660->shadow_regs[MAX8660_MDTV2]; + return selector; +} + +static int max8660_ldo5_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + int ret; + + ret = max8660_write(max8660, MAX8660_MDTV2, 0, selector); + if (ret) + return ret; + + /* Select target voltage register and activate regulation */ + return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0); +} + +static struct regulator_ops max8660_ldo5_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = max8660_ldo5_set_voltage_sel, + .get_voltage_sel = max8660_ldo5_get_voltage_sel, +}; + + +/* + * LDO67 functions + */ + +static int max8660_ldo67_is_enabled(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 val = max8660->shadow_regs[MAX8660_OVER2]; + u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4; + return !!(val & mask); +} + +static int max8660_ldo67_enable(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 bit = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4; + return max8660_write(max8660, MAX8660_OVER2, 0xff, bit); +} + +static int max8660_ldo67_disable(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? ~2 : ~4; + return max8660_write(max8660, MAX8660_OVER2, mask, 0); +} + +static int max8660_ldo67_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + + u8 shift = (rdev_get_id(rdev) == MAX8660_V6) ? 0 : 4; + u8 selector = (max8660->shadow_regs[MAX8660_L12VCR] >> shift) & 0xf; + return selector; +} + +static int max8660_ldo67_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max8660 *max8660 = rdev_get_drvdata(rdev); + + if (rdev_get_id(rdev) == MAX8660_V6) + return max8660_write(max8660, MAX8660_L12VCR, 0xf0, selector); + else + return max8660_write(max8660, MAX8660_L12VCR, 0x0f, + selector << 4); +} + +static struct regulator_ops max8660_ldo67_ops = { + .is_enabled = max8660_ldo67_is_enabled, + .enable = max8660_ldo67_enable, + .disable = max8660_ldo67_disable, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = max8660_ldo67_get_voltage_sel, + .set_voltage_sel = max8660_ldo67_set_voltage_sel, +}; + +static const struct regulator_desc max8660_reg[] = { + { + .name = "V3(DCDC)", + .id = MAX8660_V3, + .ops = &max8660_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX8660_DCDC_MAX_SEL + 1, + .owner = THIS_MODULE, + .min_uV = MAX8660_DCDC_MIN_UV, + .uV_step = MAX8660_DCDC_STEP, + }, + { + .name = "V4(DCDC)", + .id = MAX8660_V4, + .ops = &max8660_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX8660_DCDC_MAX_SEL + 1, + .owner = THIS_MODULE, + .min_uV = MAX8660_DCDC_MIN_UV, + .uV_step = MAX8660_DCDC_STEP, + }, + { + .name = "V5(LDO)", + .id = MAX8660_V5, + .ops = &max8660_ldo5_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX8660_LDO5_MAX_SEL + 1, + .owner = THIS_MODULE, + .min_uV = MAX8660_LDO5_MIN_UV, + .uV_step = MAX8660_LDO5_STEP, + }, + { + .name = "V6(LDO)", + .id = MAX8660_V6, + .ops = &max8660_ldo67_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX8660_LDO67_MAX_SEL + 1, + .owner = THIS_MODULE, + .min_uV = MAX8660_LDO67_MIN_UV, + .uV_step = MAX8660_LDO67_STEP, + }, + { + .name = "V7(LDO)", + .id = MAX8660_V7, + .ops = &max8660_ldo67_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX8660_LDO67_MAX_SEL + 1, + .owner = THIS_MODULE, + .min_uV = MAX8660_LDO67_MIN_UV, + .uV_step = MAX8660_LDO67_STEP, + }, +}; + +static int max8660_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct regulator_dev **rdev; + struct max8660_platform_data *pdata = client->dev.platform_data; + struct regulator_config config = { }; + struct max8660 *max8660; + int boot_on, i, id, ret = -EINVAL; + + if (pdata->num_subdevs > MAX8660_V_END) { + dev_err(&client->dev, "Too many regulators found!\n"); + return -EINVAL; + } + + max8660 = devm_kzalloc(&client->dev, sizeof(struct max8660) + + sizeof(struct regulator_dev *) * MAX8660_V_END, + GFP_KERNEL); + if (!max8660) + return -ENOMEM; + + max8660->client = client; + rdev = max8660->rdev; + + if (pdata->en34_is_high) { + /* Simulate always on */ + max8660->shadow_regs[MAX8660_OVER1] = 5; + } else { + /* Otherwise devices can be toggled via software */ + max8660_dcdc_ops.enable = max8660_dcdc_enable; + max8660_dcdc_ops.disable = max8660_dcdc_disable; + } + + /* + * First, set up shadow registers to prevent glitches. As some + * registers are shared between regulators, everything must be properly + * set up for all regulators in advance. + */ + max8660->shadow_regs[MAX8660_ADTV1] = + max8660->shadow_regs[MAX8660_ADTV2] = + max8660->shadow_regs[MAX8660_SDTV1] = + max8660->shadow_regs[MAX8660_SDTV2] = 0x1b; + max8660->shadow_regs[MAX8660_MDTV1] = + max8660->shadow_regs[MAX8660_MDTV2] = 0x04; + + for (i = 0; i < pdata->num_subdevs; i++) { + + if (!pdata->subdevs[i].platform_data) + goto err_out; + + boot_on = pdata->subdevs[i].platform_data->constraints.boot_on; + + switch (pdata->subdevs[i].id) { + case MAX8660_V3: + if (boot_on) + max8660->shadow_regs[MAX8660_OVER1] |= 1; + break; + + case MAX8660_V4: + if (boot_on) + max8660->shadow_regs[MAX8660_OVER1] |= 4; + break; + + case MAX8660_V5: + break; + + case MAX8660_V6: + if (boot_on) + max8660->shadow_regs[MAX8660_OVER2] |= 2; + break; + + case MAX8660_V7: + if (!strcmp(i2c_id->name, "max8661")) { + dev_err(&client->dev, "Regulator not on this chip!\n"); + goto err_out; + } + + if (boot_on) + max8660->shadow_regs[MAX8660_OVER2] |= 4; + break; + + default: + dev_err(&client->dev, "invalid regulator %s\n", + pdata->subdevs[i].name); + goto err_out; + } + } + + /* Finally register devices */ + for (i = 0; i < pdata->num_subdevs; i++) { + + id = pdata->subdevs[i].id; + + config.dev = &client->dev; + config.init_data = pdata->subdevs[i].platform_data; + config.driver_data = max8660; + + rdev[i] = regulator_register(&max8660_reg[id], &config); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(&client->dev, "failed to register %s\n", + max8660_reg[id].name); + goto err_unregister; + } + } + + i2c_set_clientdata(client, max8660); + return 0; + +err_unregister: + while (--i >= 0) + regulator_unregister(rdev[i]); +err_out: + return ret; +} + +static int max8660_remove(struct i2c_client *client) +{ + struct max8660 *max8660 = i2c_get_clientdata(client); + int i; + + for (i = 0; i < MAX8660_V_END; i++) + regulator_unregister(max8660->rdev[i]); + return 0; +} + +static const struct i2c_device_id max8660_id[] = { + { "max8660", 0 }, + { "max8661", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8660_id); + +static struct i2c_driver max8660_driver = { + .probe = max8660_probe, + .remove = max8660_remove, + .driver = { + .name = "max8660", + .owner = THIS_MODULE, + }, + .id_table = max8660_id, +}; + +static int __init max8660_init(void) +{ + return i2c_add_driver(&max8660_driver); +} +subsys_initcall(max8660_init); + +static void __exit max8660_exit(void) +{ + i2c_del_driver(&max8660_driver); +} +module_exit(max8660_exit); + +/* Module information */ +MODULE_DESCRIPTION("MAXIM 8660/8661 voltage regulator driver"); +MODULE_AUTHOR("Wolfram Sang"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c new file mode 100644 index 0000000..4568c15 --- /dev/null +++ b/drivers/regulator/max8907-regulator.c @@ -0,0 +1,408 @@ +/* + * max8907-regulator.c -- support regulators in max8907 + * + * Copyright (C) 2010 Gyungoh Yoo + * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * Portions based on drivers/regulator/tps65910-regulator.c, + * Copyright 2010 Texas Instruments Inc. + * Author: Graeme Gregory + * Author: Jorge Eduardo Candelaria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX8907_II2RR_VERSION_MASK 0xF0 +#define MAX8907_II2RR_VERSION_REV_A 0x00 +#define MAX8907_II2RR_VERSION_REV_B 0x10 +#define MAX8907_II2RR_VERSION_REV_C 0x30 + +struct max8907_regulator { + struct regulator_desc desc[MAX8907_NUM_REGULATORS]; + struct regulator_dev *rdev[MAX8907_NUM_REGULATORS]; +}; + +#define REG_MBATT() \ + [MAX8907_MBATT] = { \ + .name = "MBATT", \ + .supply_name = "mbatt", \ + .id = MAX8907_MBATT, \ + .ops = &max8907_mbatt_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +#define REG_LDO(ids, supply, base, min, max, step) \ + [MAX8907_##ids] = { \ + .name = #ids, \ + .supply_name = supply, \ + .id = MAX8907_##ids, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &max8907_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + MAX8907_VOUT, \ + .vsel_mask = 0x3f, \ + .enable_reg = (base) + MAX8907_CTL, \ + .enable_mask = MAX8907_MASK_LDO_EN, \ + } + +#define REG_FIXED(ids, supply, voltage) \ + [MAX8907_##ids] = { \ + .name = #ids, \ + .supply_name = supply, \ + .id = MAX8907_##ids, \ + .n_voltages = 1, \ + .ops = &max8907_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + } + +#define REG_OUT5V(ids, supply, base, voltage) \ + [MAX8907_##ids] = { \ + .name = #ids, \ + .supply_name = supply, \ + .id = MAX8907_##ids, \ + .n_voltages = 1, \ + .ops = &max8907_out5v_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + .enable_reg = (base), \ + .enable_mask = MAX8907_MASK_OUT5V_EN, \ + } + +#define REG_BBAT(ids, supply, base, min, max, step) \ + [MAX8907_##ids] = { \ + .name = #ids, \ + .supply_name = supply, \ + .id = MAX8907_##ids, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &max8907_bbat_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = MAX8907_MASK_VBBATTCV, \ + } + +#define LDO_750_50(id, supply, base) REG_LDO(id, supply, (base), \ + 750000, 3900000, 50000) +#define LDO_650_25(id, supply, base) REG_LDO(id, supply, (base), \ + 650000, 2225000, 25000) + +static struct regulator_ops max8907_mbatt_ops = { +}; + +static struct regulator_ops max8907_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops max8907_ldo_hwctl_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_ops max8907_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops max8907_out5v_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops max8907_out5v_hwctl_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops max8907_bbat_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_desc max8907_regulators[] = { + REG_MBATT(), + REG_LDO(SD1, "in-v1", MAX8907_REG_SDCTL1, 650000, 2225000, 25000), + REG_LDO(SD2, "in-v2", MAX8907_REG_SDCTL2, 637500, 1425000, 12500), + REG_LDO(SD3, "in-v3", MAX8907_REG_SDCTL3, 750000, 3900000, 50000), + LDO_750_50(LDO1, "in1", MAX8907_REG_LDOCTL1), + LDO_650_25(LDO2, "in2", MAX8907_REG_LDOCTL2), + LDO_650_25(LDO3, "in3", MAX8907_REG_LDOCTL3), + LDO_750_50(LDO4, "in4", MAX8907_REG_LDOCTL4), + LDO_750_50(LDO5, "in5", MAX8907_REG_LDOCTL5), + LDO_750_50(LDO6, "in6", MAX8907_REG_LDOCTL6), + LDO_750_50(LDO7, "in7", MAX8907_REG_LDOCTL7), + LDO_750_50(LDO8, "in8", MAX8907_REG_LDOCTL8), + LDO_750_50(LDO9, "in9", MAX8907_REG_LDOCTL9), + LDO_750_50(LDO10, "in10", MAX8907_REG_LDOCTL10), + LDO_750_50(LDO11, "in11", MAX8907_REG_LDOCTL11), + LDO_750_50(LDO12, "in12", MAX8907_REG_LDOCTL12), + LDO_750_50(LDO13, "in13", MAX8907_REG_LDOCTL13), + LDO_750_50(LDO14, "in14", MAX8907_REG_LDOCTL14), + LDO_750_50(LDO15, "in15", MAX8907_REG_LDOCTL15), + LDO_750_50(LDO16, "in16", MAX8907_REG_LDOCTL16), + LDO_650_25(LDO17, "in17", MAX8907_REG_LDOCTL17), + LDO_650_25(LDO18, "in18", MAX8907_REG_LDOCTL18), + LDO_750_50(LDO19, "in19", MAX8907_REG_LDOCTL19), + LDO_750_50(LDO20, "in20", MAX8907_REG_LDOCTL20), + REG_OUT5V(OUT5V, "mbatt", MAX8907_REG_OUT5VEN, 5000000), + REG_OUT5V(OUT33V, "mbatt", MAX8907_REG_OUT33VEN, 3300000), + REG_BBAT(BBAT, "MBATT", MAX8907_REG_BBAT_CNFG, + 2400000, 3000000, 200000), + REG_FIXED(SDBY, "MBATT", 1200000), + REG_FIXED(VRTC, "MBATT", 3300000), +}; + +#ifdef CONFIG_OF + +#define MATCH(_name, _id) \ + [MAX8907_##_id] = { \ + .name = #_name, \ + .driver_data = (void *)&max8907_regulators[MAX8907_##_id], \ + } + +static struct of_regulator_match max8907_matches[] = { + MATCH(mbatt, MBATT), + MATCH(sd1, SD1), + MATCH(sd2, SD2), + MATCH(sd3, SD3), + MATCH(ldo1, LDO1), + MATCH(ldo2, LDO2), + MATCH(ldo3, LDO3), + MATCH(ldo4, LDO4), + MATCH(ldo5, LDO5), + MATCH(ldo6, LDO6), + MATCH(ldo7, LDO7), + MATCH(ldo8, LDO8), + MATCH(ldo9, LDO9), + MATCH(ldo10, LDO10), + MATCH(ldo11, LDO11), + MATCH(ldo12, LDO12), + MATCH(ldo13, LDO13), + MATCH(ldo14, LDO14), + MATCH(ldo15, LDO15), + MATCH(ldo16, LDO16), + MATCH(ldo17, LDO17), + MATCH(ldo18, LDO18), + MATCH(ldo19, LDO19), + MATCH(ldo20, LDO20), + MATCH(out5v, OUT5V), + MATCH(out33v, OUT33V), + MATCH(bbat, BBAT), + MATCH(sdby, SDBY), + MATCH(vrtc, VRTC), +}; + +static int max8907_regulator_parse_dt(struct platform_device *pdev) +{ + struct device_node *np, *regulators; + int ret; + + np = of_node_get(pdev->dev.parent->of_node); + if (!np) + return 0; + + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_err(&pdev->dev, "regulators node not found\n"); + return -EINVAL; + } + + ret = of_regulator_match(&pdev->dev, regulators, max8907_matches, + ARRAY_SIZE(max8907_matches)); + of_node_put(regulators); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", + ret); + return ret; + } + + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return max8907_matches[index].init_data; +} + +static inline struct device_node *match_of_node(int index) +{ + return max8907_matches[index].of_node; +} +#else +static int max8907_regulator_parse_dt(struct platform_device *pdev) +{ + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return NULL; +} + +static inline struct device_node *match_of_node(int index) +{ + return NULL; +} +#endif + +static int max8907_regulator_probe(struct platform_device *pdev) +{ + struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent); + struct max8907_platform_data *pdata = dev_get_platdata(max8907->dev); + int ret; + struct max8907_regulator *pmic; + unsigned int val; + int i; + struct regulator_config config = {}; + struct regulator_init_data *idata; + const char *mbatt_rail_name = NULL; + + ret = max8907_regulator_parse_dt(pdev); + if (ret) + return ret; + + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "Failed to alloc pmic\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, pmic); + + memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc)); + + /* Backwards compatibility with MAX8907B; SD1 uses different voltages */ + regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val); + if ((val & MAX8907_II2RR_VERSION_MASK) == + MAX8907_II2RR_VERSION_REV_B) { + pmic->desc[MAX8907_SD1].min_uV = 637500; + pmic->desc[MAX8907_SD1].uV_step = 12500; + pmic->desc[MAX8907_SD1].n_voltages = + (1425000 - 637500) / 12500 + 1; + } + + for (i = 0; i < MAX8907_NUM_REGULATORS; i++) { + config.dev = pdev->dev.parent; + if (pdata) + idata = pdata->init_data[i]; + else + idata = match_init_data(i); + config.init_data = idata; + config.driver_data = pmic; + config.regmap = max8907->regmap_gen; + config.of_node = match_of_node(i); + + switch (pmic->desc[i].id) { + case MAX8907_MBATT: + if (idata && idata->constraints.name) + mbatt_rail_name = idata->constraints.name; + else + mbatt_rail_name = pmic->desc[i].name; + break; + case MAX8907_BBAT: + case MAX8907_SDBY: + case MAX8907_VRTC: + idata->supply_regulator = mbatt_rail_name; + break; + } + + if (pmic->desc[i].ops == &max8907_ldo_ops) { + regmap_read(config.regmap, pmic->desc[i].enable_reg, + &val); + if ((val & MAX8907_MASK_LDO_SEQ) != + MAX8907_MASK_LDO_SEQ) + pmic->desc[i].ops = &max8907_ldo_hwctl_ops; + } else if (pmic->desc[i].ops == &max8907_out5v_ops) { + regmap_read(config.regmap, pmic->desc[i].enable_reg, + &val); + if ((val & (MAX8907_MASK_OUT5V_VINEN | + MAX8907_MASK_OUT5V_ENSRC)) != + MAX8907_MASK_OUT5V_ENSRC) + pmic->desc[i].ops = &max8907_out5v_hwctl_ops; + } + + pmic->rdev[i] = regulator_register(&pmic->desc[i], &config); + if (IS_ERR(pmic->rdev[i])) { + dev_err(&pdev->dev, + "failed to register %s regulator\n", + pmic->desc[i].name); + ret = PTR_ERR(pmic->rdev[i]); + goto err_unregister_regulator; + } + } + + return 0; + +err_unregister_regulator: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + return ret; +} + +static int max8907_regulator_remove(struct platform_device *pdev) +{ + struct max8907_regulator *pmic = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < MAX8907_NUM_REGULATORS; i++) + regulator_unregister(pmic->rdev[i]); + + return 0; +} + +static struct platform_driver max8907_regulator_driver = { + .driver = { + .name = "max8907-regulator", + .owner = THIS_MODULE, + }, + .probe = max8907_regulator_probe, + .remove = max8907_regulator_remove, +}; + +static int __init max8907_regulator_init(void) +{ + return platform_driver_register(&max8907_regulator_driver); +} + +subsys_initcall(max8907_regulator_init); + +static void __exit max8907_reg_exit(void) +{ + platform_driver_unregister(&max8907_regulator_driver); +} + +module_exit(max8907_reg_exit); + +MODULE_DESCRIPTION("MAX8907 regulator driver"); +MODULE_AUTHOR("Gyungoh Yoo "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:max8907-regulator"); diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c new file mode 100644 index 0000000..3597da8 --- /dev/null +++ b/drivers/regulator/max8925-regulator.c @@ -0,0 +1,360 @@ +/* + * Regulators driver for Maxim max8925 + * + * Copyright (C) 2009 Marvell International Ltd. + * Haojian Zhuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SD1_DVM_VMIN 850000 +#define SD1_DVM_VMAX 1000000 +#define SD1_DVM_STEP 50000 +#define SD1_DVM_SHIFT 5 /* SDCTL1 bit5 */ +#define SD1_DVM_EN 6 /* SDV1 bit 6 */ + +/* bit definitions in LDO control registers */ +#define LDO_SEQ_I2C 0x7 /* Power U/D by i2c */ +#define LDO_SEQ_MASK 0x7 /* Power U/D sequence mask */ +#define LDO_SEQ_SHIFT 2 /* Power U/D sequence offset */ +#define LDO_I2C_EN 0x1 /* Enable by i2c */ +#define LDO_I2C_EN_MASK 0x1 /* Enable mask by i2c */ +#define LDO_I2C_EN_SHIFT 0 /* Enable offset by i2c */ + +struct max8925_regulator_info { + struct regulator_desc desc; + struct regulator_dev *regulator; + struct i2c_client *i2c; + struct max8925_chip *chip; + + int vol_reg; + int enable_reg; +}; + +static int max8925_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char mask = rdev->desc->n_voltages - 1; + + return max8925_set_bits(info->i2c, info->vol_reg, mask, selector); +} + +static int max8925_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data, mask; + int ret; + + ret = max8925_reg_read(info->i2c, info->vol_reg); + if (ret < 0) + return ret; + mask = rdev->desc->n_voltages - 1; + data = ret & mask; + + return data; +} + +static int max8925_enable(struct regulator_dev *rdev) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + + return max8925_set_bits(info->i2c, info->enable_reg, + LDO_SEQ_MASK << LDO_SEQ_SHIFT | + LDO_I2C_EN_MASK << LDO_I2C_EN_SHIFT, + LDO_SEQ_I2C << LDO_SEQ_SHIFT | + LDO_I2C_EN << LDO_I2C_EN_SHIFT); +} + +static int max8925_disable(struct regulator_dev *rdev) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + + return max8925_set_bits(info->i2c, info->enable_reg, + LDO_SEQ_MASK << LDO_SEQ_SHIFT | + LDO_I2C_EN_MASK << LDO_I2C_EN_SHIFT, + LDO_SEQ_I2C << LDO_SEQ_SHIFT); +} + +static int max8925_is_enabled(struct regulator_dev *rdev) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + int ldo_seq, ret; + + ret = max8925_reg_read(info->i2c, info->enable_reg); + if (ret < 0) + return ret; + ldo_seq = (ret >> LDO_SEQ_SHIFT) & LDO_SEQ_MASK; + if (ldo_seq != LDO_SEQ_I2C) + return 1; + else + return ret & (LDO_I2C_EN_MASK << LDO_I2C_EN_SHIFT); +} + +static int max8925_set_dvm_voltage(struct regulator_dev *rdev, int uV) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data, mask; + + if (uV < SD1_DVM_VMIN || uV > SD1_DVM_VMAX) + return -EINVAL; + + data = DIV_ROUND_UP(uV - SD1_DVM_VMIN, SD1_DVM_STEP); + data <<= SD1_DVM_SHIFT; + mask = 3 << SD1_DVM_SHIFT; + + return max8925_set_bits(info->i2c, info->enable_reg, mask, data); +} + +static int max8925_set_dvm_enable(struct regulator_dev *rdev) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + + return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN, + 1 << SD1_DVM_EN); +} + +static int max8925_set_dvm_disable(struct regulator_dev *rdev) +{ + struct max8925_regulator_info *info = rdev_get_drvdata(rdev); + + return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN, 0); +} + +static struct regulator_ops max8925_regulator_sdv_ops = { + .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = max8925_set_voltage_sel, + .get_voltage_sel = max8925_get_voltage_sel, + .enable = max8925_enable, + .disable = max8925_disable, + .is_enabled = max8925_is_enabled, + .set_suspend_voltage = max8925_set_dvm_voltage, + .set_suspend_enable = max8925_set_dvm_enable, + .set_suspend_disable = max8925_set_dvm_disable, +}; + +static struct regulator_ops max8925_regulator_ldo_ops = { + .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = max8925_set_voltage_sel, + .get_voltage_sel = max8925_get_voltage_sel, + .enable = max8925_enable, + .disable = max8925_disable, + .is_enabled = max8925_is_enabled, +}; + +#define MAX8925_SDV(_id, min, max, step) \ +{ \ + .desc = { \ + .name = "SDV" #_id, \ + .ops = &max8925_regulator_sdv_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MAX8925_ID_SD##_id, \ + .owner = THIS_MODULE, \ + .n_voltages = 64, \ + .min_uV = min * 1000, \ + .uV_step = step * 1000, \ + }, \ + .vol_reg = MAX8925_SDV##_id, \ + .enable_reg = MAX8925_SDCTL##_id, \ +} + +#define MAX8925_LDO(_id, min, max, step) \ +{ \ + .desc = { \ + .name = "LDO" #_id, \ + .ops = &max8925_regulator_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MAX8925_ID_LDO##_id, \ + .owner = THIS_MODULE, \ + .n_voltages = 64, \ + .min_uV = min * 1000, \ + .uV_step = step * 1000, \ + }, \ + .vol_reg = MAX8925_LDOVOUT##_id, \ + .enable_reg = MAX8925_LDOCTL##_id, \ +} + +#ifdef CONFIG_OF +static struct of_regulator_match max8925_regulator_matches[] = { + { .name = "SDV1",}, + { .name = "SDV2",}, + { .name = "SDV3",}, + { .name = "LDO1",}, + { .name = "LDO2",}, + { .name = "LDO3",}, + { .name = "LDO4",}, + { .name = "LDO5",}, + { .name = "LDO6",}, + { .name = "LDO7",}, + { .name = "LDO8",}, + { .name = "LDO9",}, + { .name = "LDO10",}, + { .name = "LDO11",}, + { .name = "LDO12",}, + { .name = "LDO13",}, + { .name = "LDO14",}, + { .name = "LDO15",}, + { .name = "LDO16",}, + { .name = "LDO17",}, + { .name = "LDO18",}, + { .name = "LDO19",}, + { .name = "LDO20",}, +}; +#endif + +static struct max8925_regulator_info max8925_regulator_info[] = { + MAX8925_SDV(1, 637.5, 1425, 12.5), + MAX8925_SDV(2, 650, 2225, 25), + MAX8925_SDV(3, 750, 3900, 50), + + MAX8925_LDO(1, 750, 3900, 50), + MAX8925_LDO(2, 650, 2250, 25), + MAX8925_LDO(3, 650, 2250, 25), + MAX8925_LDO(4, 750, 3900, 50), + MAX8925_LDO(5, 750, 3900, 50), + MAX8925_LDO(6, 750, 3900, 50), + MAX8925_LDO(7, 750, 3900, 50), + MAX8925_LDO(8, 750, 3900, 50), + MAX8925_LDO(9, 750, 3900, 50), + MAX8925_LDO(10, 750, 3900, 50), + MAX8925_LDO(11, 750, 3900, 50), + MAX8925_LDO(12, 750, 3900, 50), + MAX8925_LDO(13, 750, 3900, 50), + MAX8925_LDO(14, 750, 3900, 50), + MAX8925_LDO(15, 750, 3900, 50), + MAX8925_LDO(16, 750, 3900, 50), + MAX8925_LDO(17, 650, 2250, 25), + MAX8925_LDO(18, 650, 2250, 25), + MAX8925_LDO(19, 750, 3900, 50), + MAX8925_LDO(20, 750, 3900, 50), +}; + +#ifdef CONFIG_OF +static int max8925_regulator_dt_init(struct platform_device *pdev, + struct regulator_config *config, + int ridx) +{ + struct device_node *nproot, *np; + int rcount; + nproot = of_node_get(pdev->dev.parent->of_node); + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "regulators"); + if (!np) { + dev_err(&pdev->dev, "failed to find regulators node\n"); + return -ENODEV; + } + + rcount = of_regulator_match(&pdev->dev, np, + &max8925_regulator_matches[ridx], 1); + of_node_put(np); + if (rcount < 0) + return -ENODEV; + config->init_data = max8925_regulator_matches[ridx].init_data; + config->of_node = max8925_regulator_matches[ridx].of_node; + + return 0; +} +#else +#define max8925_regulator_dt_init(x, y, z) (-1) +#endif + +static int max8925_regulator_probe(struct platform_device *pdev) +{ + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct regulator_init_data *pdata = pdev->dev.platform_data; + struct regulator_config config = { }; + struct max8925_regulator_info *ri; + struct resource *res; + struct regulator_dev *rdev; + int i, regulator_idx; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (!res) { + dev_err(&pdev->dev, "No REG resource!\n"); + return -EINVAL; + } + for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) { + ri = &max8925_regulator_info[i]; + if (ri->vol_reg == res->start) { + regulator_idx = i; + break; + } + } + + if (i == ARRAY_SIZE(max8925_regulator_info)) { + dev_err(&pdev->dev, "Failed to find regulator %llu\n", + (unsigned long long)res->start); + return -EINVAL; + } + ri->i2c = chip->i2c; + ri->chip = chip; + + config.dev = &pdev->dev; + config.driver_data = ri; + + if (max8925_regulator_dt_init(pdev, &config, regulator_idx)) + if (pdata) + config.init_data = pdata; + + rdev = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + return 0; +} + +static int max8925_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver max8925_regulator_driver = { + .driver = { + .name = "max8925-regulator", + .owner = THIS_MODULE, + }, + .probe = max8925_regulator_probe, + .remove = max8925_regulator_remove, +}; + +static int __init max8925_regulator_init(void) +{ + return platform_driver_register(&max8925_regulator_driver); +} +subsys_initcall(max8925_regulator_init); + +static void __exit max8925_regulator_exit(void) +{ + platform_driver_unregister(&max8925_regulator_driver); +} +module_exit(max8925_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_DESCRIPTION("Regulator Driver for Maxim 8925 PMIC"); +MODULE_ALIAS("platform:max8925-regulator"); diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c new file mode 100644 index 0000000..5259c2f --- /dev/null +++ b/drivers/regulator/max8952.c @@ -0,0 +1,363 @@ +/* + * max8952.c - Voltage and current regulation for the Maxim 8952 + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +enum { + MAX8952_REG_MODE0, + MAX8952_REG_MODE1, + MAX8952_REG_MODE2, + MAX8952_REG_MODE3, + MAX8952_REG_CONTROL, + MAX8952_REG_SYNC, + MAX8952_REG_RAMP, + MAX8952_REG_CHIP_ID1, + MAX8952_REG_CHIP_ID2, +}; + +struct max8952_data { + struct i2c_client *client; + struct device *dev; + struct max8952_platform_data *pdata; + struct regulator_dev *rdev; + + bool vid0; + bool vid1; +}; + +static int max8952_read_reg(struct max8952_data *max8952, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(max8952->client, reg); + if (ret > 0) + ret &= 0xff; + + return ret; +} + +static int max8952_write_reg(struct max8952_data *max8952, + u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(max8952->client, reg, value); +} + +static int max8952_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + + if (rdev_get_id(rdev) != 0) + return -EINVAL; + + return (max8952->pdata->dvs_mode[selector] * 10 + 770) * 1000; +} + +static int max8952_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + u8 vid = 0; + + if (max8952->vid0) + vid += 1; + if (max8952->vid1) + vid += 2; + + return vid; +} + +static int max8952_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct max8952_data *max8952 = rdev_get_drvdata(rdev); + + if (!gpio_is_valid(max8952->pdata->gpio_vid0) || + !gpio_is_valid(max8952->pdata->gpio_vid1)) { + /* DVS not supported */ + return -EPERM; + } + + max8952->vid0 = selector & 0x1; + max8952->vid1 = (selector >> 1) & 0x1; + gpio_set_value(max8952->pdata->gpio_vid0, max8952->vid0); + gpio_set_value(max8952->pdata->gpio_vid1, max8952->vid1); + + return 0; +} + +static struct regulator_ops max8952_ops = { + .list_voltage = max8952_list_voltage, + .get_voltage_sel = max8952_get_voltage_sel, + .set_voltage_sel = max8952_set_voltage_sel, +}; + +static const struct regulator_desc regulator = { + .name = "MAX8952_VOUT", + .id = 0, + .n_voltages = MAX8952_NUM_DVS_MODE, + .ops = &max8952_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +#ifdef CONFIG_OF +static struct of_device_id max8952_dt_match[] = { + { .compatible = "maxim,max8952" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max8952_dt_match); + +static struct max8952_platform_data *max8952_parse_dt(struct device *dev) +{ + struct max8952_platform_data *pd; + struct device_node *np = dev->of_node; + int ret; + int i; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "Failed to allocate platform data\n"); + return NULL; + } + + pd->gpio_vid0 = of_get_named_gpio(np, "max8952,vid-gpios", 0); + pd->gpio_vid1 = of_get_named_gpio(np, "max8952,vid-gpios", 1); + pd->gpio_en = of_get_named_gpio(np, "max8952,en-gpio", 0); + + if (of_property_read_u32(np, "max8952,default-mode", &pd->default_mode)) + dev_warn(dev, "Default mode not specified, assuming 0\n"); + + ret = of_property_read_u32_array(np, "max8952,dvs-mode-microvolt", + pd->dvs_mode, ARRAY_SIZE(pd->dvs_mode)); + if (ret) { + dev_err(dev, "max8952,dvs-mode-microvolt property not specified"); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(pd->dvs_mode); ++i) { + if (pd->dvs_mode[i] < 770000 || pd->dvs_mode[i] > 1400000) { + dev_err(dev, "DVS voltage %d out of range\n", i); + return NULL; + } + pd->dvs_mode[i] = (pd->dvs_mode[i] - 770000) / 10000; + } + + if (of_property_read_u32(np, "max8952,sync-freq", &pd->sync_freq)) + dev_warn(dev, "max8952,sync-freq property not specified, defaulting to 26MHz\n"); + + if (of_property_read_u32(np, "max8952,ramp-speed", &pd->ramp_speed)) + dev_warn(dev, "max8952,ramp-speed property not specified, defaulting to 32mV/us\n"); + + pd->reg_data = of_get_regulator_init_data(dev, np); + if (!pd->reg_data) { + dev_err(dev, "Failed to parse regulator init data\n"); + return NULL; + } + + return pd; +} +#else +static struct max8952_platform_data *max8952_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + +static int max8952_pmic_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct max8952_platform_data *pdata = client->dev.platform_data; + struct regulator_config config = { }; + struct max8952_data *max8952; + + int ret = 0, err = 0; + + if (client->dev.of_node) + pdata = max8952_parse_dt(&client->dev); + + if (!pdata) { + dev_err(&client->dev, "Require the platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + max8952 = devm_kzalloc(&client->dev, sizeof(struct max8952_data), + GFP_KERNEL); + if (!max8952) + return -ENOMEM; + + max8952->client = client; + max8952->dev = &client->dev; + max8952->pdata = pdata; + + config.dev = max8952->dev; + config.init_data = pdata->reg_data; + config.driver_data = max8952; + config.of_node = client->dev.of_node; + + config.ena_gpio = pdata->gpio_en; + if (pdata->reg_data->constraints.boot_on) + config.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + + max8952->rdev = regulator_register(®ulator, &config); + + if (IS_ERR(max8952->rdev)) { + ret = PTR_ERR(max8952->rdev); + dev_err(max8952->dev, "regulator init failed (%d)\n", ret); + return ret; + } + + max8952->vid0 = pdata->default_mode & 0x1; + max8952->vid1 = (pdata->default_mode >> 1) & 0x1; + + if (gpio_is_valid(pdata->gpio_vid0) && + gpio_is_valid(pdata->gpio_vid1)) { + if (!gpio_request(pdata->gpio_vid0, "MAX8952 VID0")) + gpio_direction_output(pdata->gpio_vid0, + (pdata->default_mode) & 0x1); + else + err = 1; + + if (!gpio_request(pdata->gpio_vid1, "MAX8952 VID1")) + gpio_direction_output(pdata->gpio_vid1, + (pdata->default_mode >> 1) & 0x1); + else { + if (!err) + gpio_free(pdata->gpio_vid0); + err = 2; + } + + } else + err = 3; + + if (err) { + dev_warn(max8952->dev, "VID0/1 gpio invalid: " + "DVS not available.\n"); + max8952->vid0 = 0; + max8952->vid1 = 0; + /* Mark invalid */ + pdata->gpio_vid0 = -1; + pdata->gpio_vid1 = -1; + + /* Disable Pulldown of EN only */ + max8952_write_reg(max8952, MAX8952_REG_CONTROL, 0x60); + + dev_err(max8952->dev, "DVS modes disabled because VID0 and VID1" + " do not have proper controls.\n"); + } else { + /* + * Disable Pulldown on EN, VID0, VID1 to reduce + * leakage current of MAX8952 assuming that MAX8952 + * is turned on (EN==1). Note that without having VID0/1 + * properly connected, turning pulldown off can be + * problematic. Thus, turn this off only when they are + * controllable by GPIO. + */ + max8952_write_reg(max8952, MAX8952_REG_CONTROL, 0x0); + } + + max8952_write_reg(max8952, MAX8952_REG_MODE0, + (max8952_read_reg(max8952, + MAX8952_REG_MODE0) & 0xC0) | + (pdata->dvs_mode[0] & 0x3F)); + max8952_write_reg(max8952, MAX8952_REG_MODE1, + (max8952_read_reg(max8952, + MAX8952_REG_MODE1) & 0xC0) | + (pdata->dvs_mode[1] & 0x3F)); + max8952_write_reg(max8952, MAX8952_REG_MODE2, + (max8952_read_reg(max8952, + MAX8952_REG_MODE2) & 0xC0) | + (pdata->dvs_mode[2] & 0x3F)); + max8952_write_reg(max8952, MAX8952_REG_MODE3, + (max8952_read_reg(max8952, + MAX8952_REG_MODE3) & 0xC0) | + (pdata->dvs_mode[3] & 0x3F)); + + max8952_write_reg(max8952, MAX8952_REG_SYNC, + (max8952_read_reg(max8952, MAX8952_REG_SYNC) & 0x3F) | + ((pdata->sync_freq & 0x3) << 6)); + max8952_write_reg(max8952, MAX8952_REG_RAMP, + (max8952_read_reg(max8952, MAX8952_REG_RAMP) & 0x1F) | + ((pdata->ramp_speed & 0x7) << 5)); + + i2c_set_clientdata(client, max8952); + + return 0; +} + +static int max8952_pmic_remove(struct i2c_client *client) +{ + struct max8952_data *max8952 = i2c_get_clientdata(client); + struct max8952_platform_data *pdata = max8952->pdata; + struct regulator_dev *rdev = max8952->rdev; + + regulator_unregister(rdev); + + gpio_free(pdata->gpio_vid0); + gpio_free(pdata->gpio_vid1); + return 0; +} + +static const struct i2c_device_id max8952_ids[] = { + { "max8952", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8952_ids); + +static struct i2c_driver max8952_pmic_driver = { + .probe = max8952_pmic_probe, + .remove = max8952_pmic_remove, + .driver = { + .name = "max8952", + .of_match_table = of_match_ptr(max8952_dt_match), + }, + .id_table = max8952_ids, +}; + +static int __init max8952_pmic_init(void) +{ + return i2c_add_driver(&max8952_pmic_driver); +} +subsys_initcall(max8952_pmic_init); + +static void __exit max8952_pmic_exit(void) +{ + i2c_del_driver(&max8952_pmic_driver); +} +module_exit(max8952_pmic_exit); + +MODULE_DESCRIPTION("MAXIM 8952 voltage regulator driver"); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c new file mode 100644 index 0000000..adb1414 --- /dev/null +++ b/drivers/regulator/max8973-regulator.c @@ -0,0 +1,505 @@ +/* + * max8973-regulator.c -- Maxim max8973 + * + * Regulator driver for MAXIM 8973 DC-DC step-down switching regulator. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define MAX8973_VOUT 0x0 +#define MAX8973_VOUT_DVS 0x1 +#define MAX8973_CONTROL1 0x2 +#define MAX8973_CONTROL2 0x3 +#define MAX8973_CHIPID1 0x4 +#define MAX8973_CHIPID2 0x5 + +#define MAX8973_MAX_VOUT_REG 2 + +/* MAX8973_VOUT */ +#define MAX8973_VOUT_ENABLE BIT(7) +#define MAX8973_VOUT_MASK 0x7F + +/* MAX8973_VOUT_DVS */ +#define MAX8973_DVS_VOUT_MASK 0x7F + +/* MAX8973_CONTROL1 */ +#define MAX8973_SNS_ENABLE BIT(7) +#define MAX8973_FPWM_EN_M BIT(6) +#define MAX8973_NFSR_ENABLE BIT(5) +#define MAX8973_AD_ENABLE BIT(4) +#define MAX8973_BIAS_ENABLE BIT(3) +#define MAX8973_FREQSHIFT_9PER BIT(2) + +#define MAX8973_RAMP_12mV_PER_US 0x0 +#define MAX8973_RAMP_25mV_PER_US 0x1 +#define MAX8973_RAMP_50mV_PER_US 0x2 +#define MAX8973_RAMP_200mV_PER_US 0x3 + +/* MAX8973_CONTROL2 */ +#define MAX8973_WDTMR_ENABLE BIT(6) +#define MAX8973_DISCH_ENBABLE BIT(5) +#define MAX8973_FT_ENABLE BIT(4) + +#define MAX8973_CKKADV_TRIP_DISABLE 0xC +#define MAX8973_CKKADV_TRIP_75mV_PER_US 0x0 +#define MAX8973_CKKADV_TRIP_150mV_PER_US 0x4 +#define MAX8973_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8 +#define MAX8973_CONTROL_CLKADV_TRIP_MASK 0x00030000 + +#define MAX8973_INDUCTOR_MIN_30_PER 0x0 +#define MAX8973_INDUCTOR_NOMINAL 0x1 +#define MAX8973_INDUCTOR_PLUS_30_PER 0x2 +#define MAX8973_INDUCTOR_PLUS_60_PER 0x3 +#define MAX8973_CONTROL_INDUCTOR_VALUE_MASK 0x00300000 + +#define MAX8973_MIN_VOLATGE 606250 +#define MAX8973_MAX_VOLATGE 1400000 +#define MAX8973_VOLATGE_STEP 6250 +#define MAX8973_BUCK_N_VOLTAGE 0x80 + +/* Maxim 8973 chip information */ +struct max8973_chip { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regmap *regmap; + bool enable_external_control; + int dvs_gpio; + int lru_index[MAX8973_MAX_VOUT_REG]; + int curr_vout_val[MAX8973_MAX_VOUT_REG]; + int curr_vout_reg; + int curr_gpio_val; + bool valid_dvs_gpio; +}; + +/* + * find_voltage_set_register: Find new voltage configuration register (VOUT). + * The finding of the new VOUT register will be based on the LRU mechanism. + * Each VOUT register will have different voltage configured . This + * Function will look if any of the VOUT register have requested voltage set + * or not. + * - If it is already there then it will make that register as most + * recently used and return as found so that caller need not to set + * the VOUT register but need to set the proper gpios to select this + * VOUT register. + * - If requested voltage is not found then it will use the least + * recently mechanism to get new VOUT register for new configuration + * and will return not_found so that caller need to set new VOUT + * register and then gpios (both). + */ +static bool find_voltage_set_register(struct max8973_chip *tps, + int req_vsel, int *vout_reg, int *gpio_val) +{ + int i; + bool found = false; + int new_vout_reg = tps->lru_index[MAX8973_MAX_VOUT_REG - 1]; + int found_index = MAX8973_MAX_VOUT_REG - 1; + + for (i = 0; i < MAX8973_MAX_VOUT_REG; ++i) { + if (tps->curr_vout_val[tps->lru_index[i]] == req_vsel) { + new_vout_reg = tps->lru_index[i]; + found_index = i; + found = true; + goto update_lru_index; + } + } + +update_lru_index: + for (i = found_index; i > 0; i--) + tps->lru_index[i] = tps->lru_index[i - 1]; + + tps->lru_index[0] = new_vout_reg; + *gpio_val = new_vout_reg; + *vout_reg = MAX8973_VOUT + new_vout_reg; + return found; +} + +static int max8973_dcdc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(max->regmap, max->curr_vout_reg, &data); + if (ret < 0) { + dev_err(max->dev, "register %d read failed, err = %d\n", + max->curr_vout_reg, ret); + return ret; + } + return data & MAX8973_VOUT_MASK; +} + +static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev, + unsigned vsel) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int ret; + bool found = false; + int vout_reg = max->curr_vout_reg; + int gpio_val = max->curr_gpio_val; + + /* + * If gpios are available to select the VOUT register then least + * recently used register for new configuration. + */ + if (max->valid_dvs_gpio) + found = find_voltage_set_register(max, vsel, + &vout_reg, &gpio_val); + + if (!found) { + ret = regmap_update_bits(max->regmap, vout_reg, + MAX8973_VOUT_MASK, vsel); + if (ret < 0) { + dev_err(max->dev, "register %d update failed, err %d\n", + vout_reg, ret); + return ret; + } + max->curr_vout_reg = vout_reg; + max->curr_vout_val[gpio_val] = vsel; + } + + /* Select proper VOUT register vio gpios */ + if (max->valid_dvs_gpio) { + gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1); + max->curr_gpio_val = gpio_val; + } + return 0; +} + +static int max8973_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + int ret; + int pwm; + + /* Enable force PWM mode in FAST mode only. */ + switch (mode) { + case REGULATOR_MODE_FAST: + pwm = MAX8973_FPWM_EN_M; + break; + + case REGULATOR_MODE_NORMAL: + pwm = 0; + break; + + default: + return -EINVAL; + } + + ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1, + MAX8973_FPWM_EN_M, pwm); + if (ret < 0) + dev_err(max->dev, "register %d update failed, err %d\n", + MAX8973_CONTROL1, ret); + return ret; +} + +static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct max8973_chip *max = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data); + if (ret < 0) { + dev_err(max->dev, "register %d read failed, err %d\n", + MAX8973_CONTROL1, ret); + return ret; + } + return (data & MAX8973_FPWM_EN_M) ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops max8973_dcdc_ops = { + .get_voltage_sel = max8973_dcdc_get_voltage_sel, + .set_voltage_sel = max8973_dcdc_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .set_mode = max8973_dcdc_set_mode, + .get_mode = max8973_dcdc_get_mode, +}; + +static int max8973_init_dcdc(struct max8973_chip *max, + struct max8973_regulator_platform_data *pdata) +{ + int ret; + uint8_t control1 = 0; + uint8_t control2 = 0; + + if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE) + control1 |= MAX8973_SNS_ENABLE; + + if (!(pdata->control_flags & MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE)) + control1 |= MAX8973_NFSR_ENABLE; + + if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE) + control1 |= MAX8973_AD_ENABLE; + + if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE) + control1 |= MAX8973_BIAS_ENABLE; + + if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE) + control1 |= MAX8973_FREQSHIFT_9PER; + + /* Set ramp delay */ + if (pdata->reg_init_data && + pdata->reg_init_data->constraints.ramp_delay) { + if (pdata->reg_init_data->constraints.ramp_delay < 25000) + control1 |= MAX8973_RAMP_12mV_PER_US; + else if (pdata->reg_init_data->constraints.ramp_delay < 50000) + control1 |= MAX8973_RAMP_25mV_PER_US; + else if (pdata->reg_init_data->constraints.ramp_delay < 200000) + control1 |= MAX8973_RAMP_50mV_PER_US; + else + control1 |= MAX8973_RAMP_200mV_PER_US; + } else { + control1 |= MAX8973_RAMP_12mV_PER_US; + max->desc.ramp_delay = 12500; + } + + if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE)) + control2 |= MAX8973_DISCH_ENBABLE; + + /* Clock advance trip configuration */ + switch (pdata->control_flags & MAX8973_CONTROL_CLKADV_TRIP_MASK) { + case MAX8973_CONTROL_CLKADV_TRIP_DISABLED: + control2 |= MAX8973_CKKADV_TRIP_DISABLE; + break; + + case MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US: + control2 |= MAX8973_CKKADV_TRIP_75mV_PER_US; + break; + + case MAX8973_CONTROL_CLKADV_TRIP_150mV_PER_US: + control2 |= MAX8973_CKKADV_TRIP_150mV_PER_US; + break; + + case MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US_HIST_DIS: + control2 |= MAX8973_CKKADV_TRIP_75mV_PER_US_HIST_DIS; + break; + } + + /* Configure inductor value */ + switch (pdata->control_flags & MAX8973_CONTROL_INDUCTOR_VALUE_MASK) { + case MAX8973_CONTROL_INDUCTOR_VALUE_NOMINAL: + control2 |= MAX8973_INDUCTOR_NOMINAL; + break; + + case MAX8973_CONTROL_INDUCTOR_VALUE_MINUS_30_PER: + control2 |= MAX8973_INDUCTOR_MIN_30_PER; + break; + + case MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_30_PER: + control2 |= MAX8973_INDUCTOR_PLUS_30_PER; + break; + + case MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_60_PER: + control2 |= MAX8973_INDUCTOR_PLUS_60_PER; + break; + } + + ret = regmap_write(max->regmap, MAX8973_CONTROL1, control1); + if (ret < 0) { + dev_err(max->dev, "register %d write failed, err = %d", + MAX8973_CONTROL1, ret); + return ret; + } + + ret = regmap_write(max->regmap, MAX8973_CONTROL2, control2); + if (ret < 0) { + dev_err(max->dev, "register %d write failed, err = %d", + MAX8973_CONTROL2, ret); + return ret; + } + + /* If external control is enabled then disable EN bit */ + if (max->enable_external_control) { + ret = regmap_update_bits(max->regmap, MAX8973_VOUT, + MAX8973_VOUT_ENABLE, 0); + if (ret < 0) + dev_err(max->dev, "register %d update failed, err = %d", + MAX8973_VOUT, ret); + } + return ret; +} + +static const struct regmap_config max8973_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX8973_CHIPID2, + .cache_type = REGCACHE_RBTREE, +}; + +static int max8973_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max8973_regulator_platform_data *pdata; + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct max8973_chip *max; + int ret; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "No Platform data"); + return -EIO; + } + + max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL); + if (!max) { + dev_err(&client->dev, "Memory allocation for max failed\n"); + return -ENOMEM; + } + + max->regmap = devm_regmap_init_i2c(client, &max8973_regmap_config); + if (IS_ERR(max->regmap)) { + ret = PTR_ERR(max->regmap); + dev_err(&client->dev, "regmap init failed, err %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, max); + max->dev = &client->dev; + max->desc.name = id->name; + max->desc.id = 0; + max->desc.ops = &max8973_dcdc_ops; + max->desc.type = REGULATOR_VOLTAGE; + max->desc.owner = THIS_MODULE; + max->desc.min_uV = MAX8973_MIN_VOLATGE; + max->desc.uV_step = MAX8973_VOLATGE_STEP; + max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE; + + if (!pdata->enable_ext_control) { + max->desc.enable_reg = MAX8973_VOUT; + max->desc.enable_mask = MAX8973_VOUT_ENABLE; + max8973_dcdc_ops.enable = regulator_enable_regmap; + max8973_dcdc_ops.disable = regulator_disable_regmap; + max8973_dcdc_ops.is_enabled = regulator_is_enabled_regmap; + } + + max->enable_external_control = pdata->enable_ext_control; + max->dvs_gpio = pdata->dvs_gpio; + max->curr_gpio_val = pdata->dvs_def_state; + max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state; + max->lru_index[0] = max->curr_vout_reg; + max->valid_dvs_gpio = false; + + if (gpio_is_valid(max->dvs_gpio)) { + int gpio_flags; + int i; + + gpio_flags = (pdata->dvs_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = devm_gpio_request_one(&client->dev, max->dvs_gpio, + gpio_flags, "max8973-dvs"); + if (ret) { + dev_err(&client->dev, + "gpio_request for gpio %d failed, err = %d\n", + max->dvs_gpio, ret); + return ret; + } + max->valid_dvs_gpio = true; + + /* + * Initialize the lru index with vout_reg id + * The index 0 will be most recently used and + * set with the max->curr_vout_reg */ + for (i = 0; i < MAX8973_MAX_VOUT_REG; ++i) + max->lru_index[i] = i; + max->lru_index[0] = max->curr_vout_reg; + max->lru_index[max->curr_vout_reg] = 0; + } + + ret = max8973_init_dcdc(max, pdata); + if (ret < 0) { + dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret); + return ret; + } + + config.dev = &client->dev; + config.init_data = pdata->reg_init_data; + config.driver_data = max; + config.of_node = client->dev.of_node; + config.regmap = max->regmap; + + /* Register the regulators */ + rdev = regulator_register(&max->desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(max->dev, "regulator register failed, err %d\n", ret); + return ret; + } + + max->rdev = rdev; + return 0; +} + +static int max8973_remove(struct i2c_client *client) +{ + struct max8973_chip *max = i2c_get_clientdata(client); + + regulator_unregister(max->rdev); + return 0; +} + +static const struct i2c_device_id max8973_id[] = { + {.name = "max8973",}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, max8973_id); + +static struct i2c_driver max8973_i2c_driver = { + .driver = { + .name = "max8973", + .owner = THIS_MODULE, + }, + .probe = max8973_probe, + .remove = max8973_remove, + .id_table = max8973_id, +}; + +static int __init max8973_init(void) +{ + return i2c_add_driver(&max8973_i2c_driver); +} +subsys_initcall(max8973_init); + +static void __exit max8973_cleanup(void) +{ + i2c_del_driver(&max8973_i2c_driver); +} +module_exit(max8973_cleanup); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("MAX8973 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c new file mode 100644 index 0000000..df20069 --- /dev/null +++ b/drivers/regulator/max8997.c @@ -0,0 +1,1267 @@ +/* + * max8997.c - Regulator driver for the Maxim 8997/8966 + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8998.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct max8997_data { + struct device *dev; + struct max8997_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + int ramp_delay; /* in mV/us */ + + bool buck1_gpiodvs; + bool buck2_gpiodvs; + bool buck5_gpiodvs; + u8 buck1_vol[8]; + u8 buck2_vol[8]; + u8 buck5_vol[8]; + int buck125_gpios[3]; + int buck125_gpioindex; + bool ignore_gpiodvs_side_effect; + + u8 saved_states[MAX8997_REG_MAX]; +}; + +static const unsigned int safeoutvolt[] = { + 4850000, + 4900000, + 4950000, + 3300000, +}; + +static inline void max8997_set_gpio(struct max8997_data *max8997) +{ + int set3 = (max8997->buck125_gpioindex) & 0x1; + int set2 = ((max8997->buck125_gpioindex) >> 1) & 0x1; + int set1 = ((max8997->buck125_gpioindex) >> 2) & 0x1; + + gpio_set_value(max8997->buck125_gpios[0], set1); + gpio_set_value(max8997->buck125_gpios[1], set2); + gpio_set_value(max8997->buck125_gpios[2], set3); +} + +struct voltage_map_desc { + int min; + int max; + int step; +}; + +/* Voltage maps in uV */ +static const struct voltage_map_desc ldo_voltage_map_desc = { + .min = 800000, .max = 3950000, .step = 50000, +}; /* LDO1 ~ 18, 21 all */ + +static const struct voltage_map_desc buck1245_voltage_map_desc = { + .min = 650000, .max = 2225000, .step = 25000, +}; /* Buck1, 2, 4, 5 */ + +static const struct voltage_map_desc buck37_voltage_map_desc = { + .min = 750000, .max = 3900000, .step = 50000, +}; /* Buck3, 7 */ + +/* current map in uA */ +static const struct voltage_map_desc charger_current_map_desc = { + .min = 200000, .max = 950000, .step = 50000, +}; + +static const struct voltage_map_desc topoff_current_map_desc = { + .min = 50000, .max = 200000, .step = 10000, +}; + +static const struct voltage_map_desc *reg_voltage_map[] = { + [MAX8997_LDO1] = &ldo_voltage_map_desc, + [MAX8997_LDO2] = &ldo_voltage_map_desc, + [MAX8997_LDO3] = &ldo_voltage_map_desc, + [MAX8997_LDO4] = &ldo_voltage_map_desc, + [MAX8997_LDO5] = &ldo_voltage_map_desc, + [MAX8997_LDO6] = &ldo_voltage_map_desc, + [MAX8997_LDO7] = &ldo_voltage_map_desc, + [MAX8997_LDO8] = &ldo_voltage_map_desc, + [MAX8997_LDO9] = &ldo_voltage_map_desc, + [MAX8997_LDO10] = &ldo_voltage_map_desc, + [MAX8997_LDO11] = &ldo_voltage_map_desc, + [MAX8997_LDO12] = &ldo_voltage_map_desc, + [MAX8997_LDO13] = &ldo_voltage_map_desc, + [MAX8997_LDO14] = &ldo_voltage_map_desc, + [MAX8997_LDO15] = &ldo_voltage_map_desc, + [MAX8997_LDO16] = &ldo_voltage_map_desc, + [MAX8997_LDO17] = &ldo_voltage_map_desc, + [MAX8997_LDO18] = &ldo_voltage_map_desc, + [MAX8997_LDO21] = &ldo_voltage_map_desc, + [MAX8997_BUCK1] = &buck1245_voltage_map_desc, + [MAX8997_BUCK2] = &buck1245_voltage_map_desc, + [MAX8997_BUCK3] = &buck37_voltage_map_desc, + [MAX8997_BUCK4] = &buck1245_voltage_map_desc, + [MAX8997_BUCK5] = &buck1245_voltage_map_desc, + [MAX8997_BUCK6] = NULL, + [MAX8997_BUCK7] = &buck37_voltage_map_desc, + [MAX8997_EN32KHZ_AP] = NULL, + [MAX8997_EN32KHZ_CP] = NULL, + [MAX8997_ENVICHG] = NULL, + [MAX8997_ESAFEOUT1] = NULL, + [MAX8997_ESAFEOUT2] = NULL, + [MAX8997_CHARGER_CV] = NULL, + [MAX8997_CHARGER] = &charger_current_map_desc, + [MAX8997_CHARGER_TOPOFF] = &topoff_current_map_desc, +}; + +static int max8997_list_voltage_charger_cv(struct regulator_dev *rdev, + unsigned int selector) +{ + int rid = rdev_get_id(rdev); + + if (rid != MAX8997_CHARGER_CV) + goto err; + + switch (selector) { + case 0x00: + return 4200000; + case 0x01 ... 0x0E: + return 4000000 + 20000 * (selector - 0x01); + case 0x0F: + return 4350000; + default: + return -EINVAL; + } +err: + return -EINVAL; +} + +static int max8997_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct voltage_map_desc *desc; + int rid = rdev_get_id(rdev); + int val; + + if (rid >= ARRAY_SIZE(reg_voltage_map) || + rid < 0) + return -EINVAL; + + desc = reg_voltage_map[rid]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val; +} + +static int max8997_get_enable_register(struct regulator_dev *rdev, + int *reg, int *mask, int *pattern) +{ + int rid = rdev_get_id(rdev); + + switch (rid) { + case MAX8997_LDO1 ... MAX8997_LDO21: + *reg = MAX8997_REG_LDO1CTRL + (rid - MAX8997_LDO1); + *mask = 0xC0; + *pattern = 0xC0; + break; + case MAX8997_BUCK1: + *reg = MAX8997_REG_BUCK1CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK2: + *reg = MAX8997_REG_BUCK2CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK3: + *reg = MAX8997_REG_BUCK3CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK4: + *reg = MAX8997_REG_BUCK4CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK5: + *reg = MAX8997_REG_BUCK5CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK6: + *reg = MAX8997_REG_BUCK6CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK7: + *reg = MAX8997_REG_BUCK7CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_EN32KHZ_AP ... MAX8997_EN32KHZ_CP: + *reg = MAX8997_REG_MAINCON1; + *mask = 0x01 << (rid - MAX8997_EN32KHZ_AP); + *pattern = 0x01 << (rid - MAX8997_EN32KHZ_AP); + break; + case MAX8997_ENVICHG: + *reg = MAX8997_REG_MBCCTRL1; + *mask = 0x80; + *pattern = 0x80; + break; + case MAX8997_ESAFEOUT1 ... MAX8997_ESAFEOUT2: + *reg = MAX8997_REG_SAFEOUTCTRL; + *mask = 0x40 << (rid - MAX8997_ESAFEOUT1); + *pattern = 0x40 << (rid - MAX8997_ESAFEOUT1); + break; + case MAX8997_CHARGER: + *reg = MAX8997_REG_MBCCTRL2; + *mask = 0x40; + *pattern = 0x40; + break; + default: + /* Not controllable or not exists */ + return -EINVAL; + } + + return 0; +} + +static int max8997_reg_is_enabled(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + u8 val; + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + ret = max8997_read_reg(i2c, reg, &val); + if (ret) + return ret; + + return (val & mask) == pattern; +} + +static int max8997_reg_enable(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + return max8997_update_reg(i2c, reg, pattern, mask); +} + +static int max8997_reg_disable(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + return max8997_update_reg(i2c, reg, ~pattern, mask); +} + +static int max8997_get_voltage_register(struct regulator_dev *rdev, + int *_reg, int *_shift, int *_mask) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + int reg, shift = 0, mask = 0x3f; + + switch (rid) { + case MAX8997_LDO1 ... MAX8997_LDO21: + reg = MAX8997_REG_LDO1CTRL + (rid - MAX8997_LDO1); + break; + case MAX8997_BUCK1: + reg = MAX8997_REG_BUCK1DVS1; + if (max8997->buck1_gpiodvs) + reg += max8997->buck125_gpioindex; + break; + case MAX8997_BUCK2: + reg = MAX8997_REG_BUCK2DVS1; + if (max8997->buck2_gpiodvs) + reg += max8997->buck125_gpioindex; + break; + case MAX8997_BUCK3: + reg = MAX8997_REG_BUCK3DVS; + break; + case MAX8997_BUCK4: + reg = MAX8997_REG_BUCK4DVS; + break; + case MAX8997_BUCK5: + reg = MAX8997_REG_BUCK5DVS1; + if (max8997->buck5_gpiodvs) + reg += max8997->buck125_gpioindex; + break; + case MAX8997_BUCK7: + reg = MAX8997_REG_BUCK7DVS; + break; + case MAX8997_ESAFEOUT1 ... MAX8997_ESAFEOUT2: + reg = MAX8997_REG_SAFEOUTCTRL; + shift = (rid == MAX8997_ESAFEOUT2) ? 2 : 0; + mask = 0x3; + break; + case MAX8997_CHARGER_CV: + reg = MAX8997_REG_MBCCTRL3; + shift = 0; + mask = 0xf; + break; + case MAX8997_CHARGER: + reg = MAX8997_REG_MBCCTRL4; + shift = 0; + mask = 0xf; + break; + case MAX8997_CHARGER_TOPOFF: + reg = MAX8997_REG_MBCCTRL5; + shift = 0; + mask = 0xf; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max8997_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int reg, shift, mask, ret; + u8 val; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8997_read_reg(i2c, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + return val; +} + +static inline int max8997_get_voltage_proper_val( + const struct voltage_map_desc *desc, + int min_vol, int max_vol) +{ + int i; + + if (desc == NULL) + return -EINVAL; + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + if (min_vol < desc->min) + min_vol = desc->min; + + i = DIV_ROUND_UP(min_vol - desc->min, desc->step); + + if (desc->min + desc->step * i > max_vol) + return -EINVAL; + + return i; +} + +static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int rid = rdev_get_id(rdev); + int lb, ub; + int reg, shift = 0, mask, ret = 0; + u8 val = 0x0; + + if (rid != MAX8997_CHARGER_CV) + return -EINVAL; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + if (max_uV < 4000000 || min_uV > 4350000) + return -EINVAL; + + if (min_uV <= 4000000) { + if (max_uV >= 4000000) + return -EINVAL; + else + val = 0x1; + } else if (min_uV <= 4200000 && max_uV >= 4200000) + val = 0x0; + else { + lb = (min_uV - 4000001) / 20000 + 2; + ub = (max_uV - 4000000) / 20000 + 1; + + if (lb > ub) + return -EINVAL; + + if (lb < 0xf) + val = lb; + else { + if (ub >= 0xf) + val = 0xf; + else + return -EINVAL; + } + } + + *selector = val; + + ret = max8997_update_reg(i2c, reg, val << shift, mask); + + return ret; +} + +/* + * For LDO1 ~ LDO21, BUCK1~5, BUCK7, CHARGER, CHARGER_TOPOFF + * BUCK1, 2, and 5 are available if they are not controlled by gpio + */ +static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + const struct voltage_map_desc *desc; + int rid = rdev_get_id(rdev); + int i, reg, shift, mask, ret; + + switch (rid) { + case MAX8997_LDO1 ... MAX8997_LDO21: + break; + case MAX8997_BUCK1 ... MAX8997_BUCK5: + break; + case MAX8997_BUCK6: + return -EINVAL; + case MAX8997_BUCK7: + break; + case MAX8997_CHARGER: + break; + case MAX8997_CHARGER_TOPOFF: + break; + default: + return -EINVAL; + } + + desc = reg_voltage_map[rid]; + + i = max8997_get_voltage_proper_val(desc, min_uV, max_uV); + if (i < 0) + return i; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8997_update_reg(i2c, reg, i << shift, mask << shift); + *selector = i; + + return ret; +} + +static int max8997_set_voltage_buck_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + const struct voltage_map_desc *desc = reg_voltage_map[rid]; + + /* Delay is required only if the voltage is increasing */ + if (old_selector >= new_selector) + return 0; + + /* No need to delay if gpio_dvs_mode */ + switch (rid) { + case MAX8997_BUCK1: + if (max8997->buck1_gpiodvs) + return 0; + break; + case MAX8997_BUCK2: + if (max8997->buck2_gpiodvs) + return 0; + break; + case MAX8997_BUCK5: + if (max8997->buck5_gpiodvs) + return 0; + break; + } + + switch (rid) { + case MAX8997_BUCK1: + case MAX8997_BUCK2: + case MAX8997_BUCK4: + case MAX8997_BUCK5: + return DIV_ROUND_UP(desc->step * (new_selector - old_selector), + max8997->ramp_delay * 1000); + } + + return 0; +} + +/* + * Assess the damage on the voltage setting of BUCK1,2,5 by the change. + * + * When GPIO-DVS mode is used for multiple bucks, changing the voltage value + * of one of the bucks may affect that of another buck, which is the side + * effect of the change (set_voltage). This function examines the GPIO-DVS + * configurations and checks whether such side-effect exists. + */ +static int max8997_assess_side_effect(struct regulator_dev *rdev, + u8 new_val, int *best) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + u8 *buckx_val[3]; + bool buckx_gpiodvs[3]; + int side_effect[8]; + int min_side_effect = INT_MAX; + int i; + + *best = -1; + + switch (rid) { + case MAX8997_BUCK1: + rid = 0; + break; + case MAX8997_BUCK2: + rid = 1; + break; + case MAX8997_BUCK5: + rid = 2; + break; + default: + return -EINVAL; + } + + buckx_val[0] = max8997->buck1_vol; + buckx_val[1] = max8997->buck2_vol; + buckx_val[2] = max8997->buck5_vol; + buckx_gpiodvs[0] = max8997->buck1_gpiodvs; + buckx_gpiodvs[1] = max8997->buck2_gpiodvs; + buckx_gpiodvs[2] = max8997->buck5_gpiodvs; + + for (i = 0; i < 8; i++) { + int others; + + if (new_val != (buckx_val[rid])[i]) { + side_effect[i] = -1; + continue; + } + + side_effect[i] = 0; + for (others = 0; others < 3; others++) { + int diff; + + if (others == rid) + continue; + if (buckx_gpiodvs[others] == false) + continue; /* Not affected */ + diff = (buckx_val[others])[i] - + (buckx_val[others])[max8997->buck125_gpioindex]; + if (diff > 0) + side_effect[i] += diff; + else if (diff < 0) + side_effect[i] -= diff; + } + if (side_effect[i] == 0) { + *best = i; + return 0; /* NO SIDE EFFECT! Use This! */ + } + if (side_effect[i] < min_side_effect) { + min_side_effect = side_effect[i]; + *best = i; + } + } + + if (*best == -1) + return -EINVAL; + + return side_effect[*best]; +} + +/* + * For Buck 1 ~ 5 and 7. If it is not controlled by GPIO, this calls + * max8997_set_voltage_ldobuck to do the job. + */ +static int max8997_set_voltage_buck(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + const struct voltage_map_desc *desc; + int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg; + bool gpio_dvs_mode = false; + + if (rid < MAX8997_BUCK1 || rid > MAX8997_BUCK7) + return -EINVAL; + + switch (rid) { + case MAX8997_BUCK1: + if (max8997->buck1_gpiodvs) + gpio_dvs_mode = true; + break; + case MAX8997_BUCK2: + if (max8997->buck2_gpiodvs) + gpio_dvs_mode = true; + break; + case MAX8997_BUCK5: + if (max8997->buck5_gpiodvs) + gpio_dvs_mode = true; + break; + } + + if (!gpio_dvs_mode) + return max8997_set_voltage_ldobuck(rdev, min_uV, max_uV, + selector); + + desc = reg_voltage_map[rid]; + new_val = max8997_get_voltage_proper_val(desc, min_uV, max_uV); + if (new_val < 0) + return new_val; + + tmp_dmg = INT_MAX; + tmp_idx = -1; + tmp_val = -1; + do { + damage = max8997_assess_side_effect(rdev, new_val, &new_idx); + if (damage == 0) + goto out; + + if (tmp_dmg > damage) { + tmp_idx = new_idx; + tmp_val = new_val; + tmp_dmg = damage; + } + + new_val++; + } while (desc->min + desc->step * new_val <= desc->max); + + new_idx = tmp_idx; + new_val = tmp_val; + + if (max8997->ignore_gpiodvs_side_effect == false) + return -EINVAL; + + dev_warn(&rdev->dev, "MAX8997 GPIO-DVS Side Effect Warning: GPIO SET:" + " %d -> %d\n", max8997->buck125_gpioindex, tmp_idx); + +out: + if (new_idx < 0 || new_val < 0) + return -EINVAL; + + max8997->buck125_gpioindex = new_idx; + max8997_set_gpio(max8997); + *selector = new_val; + + return 0; +} + +/* For SAFEOUT1 and SAFEOUT2 */ +static int max8997_set_voltage_safeout_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int rid = rdev_get_id(rdev); + int reg, shift = 0, mask, ret; + + if (rid != MAX8997_ESAFEOUT1 && rid != MAX8997_ESAFEOUT2) + return -EINVAL; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + return max8997_update_reg(i2c, reg, selector << shift, mask << shift); +} + +static int max8997_reg_disable_suspend(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + int rid = rdev_get_id(rdev); + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + max8997_read_reg(i2c, reg, &max8997->saved_states[rid]); + + if (rid == MAX8997_LDO1 || + rid == MAX8997_LDO10 || + rid == MAX8997_LDO21) { + dev_dbg(&rdev->dev, "Conditional Power-Off for %s\n", + rdev->desc->name); + return max8997_update_reg(i2c, reg, 0x40, mask); + } + + dev_dbg(&rdev->dev, "Full Power-Off for %s (%xh -> %xh)\n", + rdev->desc->name, max8997->saved_states[rid] & mask, + (~pattern) & mask); + return max8997_update_reg(i2c, reg, ~pattern, mask); +} + +static struct regulator_ops max8997_ldo_ops = { + .list_voltage = max8997_list_voltage, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_voltage_sel = max8997_get_voltage_sel, + .set_voltage = max8997_set_voltage_ldobuck, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_buck_ops = { + .list_voltage = max8997_list_voltage, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_voltage_sel = max8997_get_voltage_sel, + .set_voltage = max8997_set_voltage_buck, + .set_voltage_time_sel = max8997_set_voltage_buck_time_sel, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_fixedvolt_ops = { + .list_voltage = max8997_list_voltage, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_safeout_ops = { + .list_voltage = regulator_list_voltage_table, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_voltage_sel = max8997_get_voltage_sel, + .set_voltage_sel = max8997_set_voltage_safeout_sel, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_fixedstate_ops = { + .list_voltage = max8997_list_voltage_charger_cv, + .get_voltage_sel = max8997_get_voltage_sel, + .set_voltage = max8997_set_voltage_charger_cv, +}; + +static int max8997_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + unsigned dummy; + int rid = rdev_get_id(rdev); + + if (rid != MAX8997_CHARGER && rid != MAX8997_CHARGER_TOPOFF) + return -EINVAL; + + /* Reuse max8997_set_voltage_ldobuck to set current_limit. */ + return max8997_set_voltage_ldobuck(rdev, min_uA, max_uA, &dummy); +} + +static int max8997_get_current_limit(struct regulator_dev *rdev) +{ + int sel, rid = rdev_get_id(rdev); + + if (rid != MAX8997_CHARGER && rid != MAX8997_CHARGER_TOPOFF) + return -EINVAL; + + sel = max8997_get_voltage_sel(rdev); + if (sel < 0) + return sel; + + /* Reuse max8997_list_voltage to get current_limit. */ + return max8997_list_voltage(rdev, sel); +} + +static struct regulator_ops max8997_charger_ops = { + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_current_limit = max8997_get_current_limit, + .set_current_limit = max8997_set_current_limit, +}; + +static struct regulator_ops max8997_charger_fixedstate_ops = { + .get_current_limit = max8997_get_current_limit, + .set_current_limit = max8997_set_current_limit, +}; + +#define MAX8997_VOLTAGE_REGULATOR(_name, _ops) {\ + .name = #_name, \ + .id = MAX8997_##_name, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + +#define MAX8997_CURRENT_REGULATOR(_name, _ops) {\ + .name = #_name, \ + .id = MAX8997_##_name, \ + .ops = &_ops, \ + .type = REGULATOR_CURRENT, \ + .owner = THIS_MODULE, \ +} + +static struct regulator_desc regulators[] = { + MAX8997_VOLTAGE_REGULATOR(LDO1, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO2, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO3, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO4, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO5, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO6, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO7, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO8, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO9, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO10, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO11, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO12, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO13, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO14, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO15, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO16, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO17, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO18, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(LDO21, max8997_ldo_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK1, max8997_buck_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK2, max8997_buck_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK3, max8997_buck_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK4, max8997_buck_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK5, max8997_buck_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK6, max8997_fixedvolt_ops), + MAX8997_VOLTAGE_REGULATOR(BUCK7, max8997_buck_ops), + MAX8997_VOLTAGE_REGULATOR(EN32KHZ_AP, max8997_fixedvolt_ops), + MAX8997_VOLTAGE_REGULATOR(EN32KHZ_CP, max8997_fixedvolt_ops), + MAX8997_VOLTAGE_REGULATOR(ENVICHG, max8997_fixedvolt_ops), + MAX8997_VOLTAGE_REGULATOR(ESAFEOUT1, max8997_safeout_ops), + MAX8997_VOLTAGE_REGULATOR(ESAFEOUT2, max8997_safeout_ops), + MAX8997_VOLTAGE_REGULATOR(CHARGER_CV, max8997_fixedstate_ops), + MAX8997_CURRENT_REGULATOR(CHARGER, max8997_charger_ops), + MAX8997_CURRENT_REGULATOR(CHARGER_TOPOFF, + max8997_charger_fixedstate_ops), +}; + +#ifdef CONFIG_OF +static int max8997_pmic_dt_parse_dvs_gpio(struct platform_device *pdev, + struct max8997_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "max8997,pmic-buck125-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck125_gpios[i] = gpio; + } + return 0; +} + +static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, + struct max8997_platform_data *pdata) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct device_node *pmic_np, *regulators_np, *reg_np; + struct max8997_regulator_data *rdata; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = of_node_get(iodev->dev->of_node); + if (!pmic_np) { + dev_err(&pdev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(&pdev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = of_get_child_count(regulators_np); + + rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + of_node_put(regulators_np); + dev_err(&pdev->dev, "could not allocate memory for regulator data\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(&pdev->dev, "don't know how to configure regulator %s\n", + reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data(&pdev->dev, + reg_np); + rdata->reg_node = reg_np; + rdata++; + } + of_node_put(regulators_np); + + if (of_get_property(pmic_np, "max8997,pmic-buck1-uses-gpio-dvs", NULL)) + pdata->buck1_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck5-uses-gpio-dvs", NULL)) + pdata->buck5_gpiodvs = true; + + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + ret = max8997_pmic_dt_parse_dvs_gpio(pdev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32(pmic_np, + "max8997,pmic-buck125-default-dvs-idx", + &pdata->buck125_default_idx)) { + pdata->buck125_default_idx = 0; + } else { + if (pdata->buck125_default_idx >= 8) { + pdata->buck125_default_idx = 0; + dev_info(&pdev->dev, "invalid value for default dvs index, using 0 instead\n"); + } + } + + if (of_get_property(pmic_np, + "max8997,pmic-ignore-gpiodvs-side-effect", NULL)) + pdata->ignore_gpiodvs_side_effect = true; + + dvs_voltage_nr = 8; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck1-dvs-voltage", + pdata->buck1_voltage, dvs_voltage_nr)) { + dev_err(&pdev->dev, "buck1 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(&pdev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck5-dvs-voltage", + pdata->buck5_voltage, dvs_voltage_nr)) { + dev_err(&pdev->dev, "buck5 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, + struct max8997_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int max8997_pmic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8997_platform_data *pdata = iodev->pdata; + struct regulator_config config = { }; + struct regulator_dev **rdev; + struct max8997_data *max8997; + struct i2c_client *i2c; + int i, ret, size, nr_dvs; + u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + + if (iodev->dev->of_node) { + ret = max8997_pmic_dt_parse_pdata(pdev, pdata); + if (ret) + return ret; + } + + max8997 = devm_kzalloc(&pdev->dev, sizeof(struct max8997_data), + GFP_KERNEL); + if (!max8997) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * pdata->num_regulators; + max8997->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!max8997->rdev) + return -ENOMEM; + + rdev = max8997->rdev; + max8997->dev = &pdev->dev; + max8997->iodev = iodev; + max8997->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, max8997); + i2c = max8997->iodev->i2c; + + max8997->buck125_gpioindex = pdata->buck125_default_idx; + max8997->buck1_gpiodvs = pdata->buck1_gpiodvs; + max8997->buck2_gpiodvs = pdata->buck2_gpiodvs; + max8997->buck5_gpiodvs = pdata->buck5_gpiodvs; + memcpy(max8997->buck125_gpios, pdata->buck125_gpios, sizeof(int) * 3); + max8997->ignore_gpiodvs_side_effect = pdata->ignore_gpiodvs_side_effect; + + nr_dvs = (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) ? 8 : 1; + + for (i = 0; i < nr_dvs; i++) { + max8997->buck1_vol[i] = ret = + max8997_get_voltage_proper_val( + &buck1245_voltage_map_desc, + pdata->buck1_voltage[i], + pdata->buck1_voltage[i] + + buck1245_voltage_map_desc.step); + if (ret < 0) + goto err_out; + + max8997->buck2_vol[i] = ret = + max8997_get_voltage_proper_val( + &buck1245_voltage_map_desc, + pdata->buck2_voltage[i], + pdata->buck2_voltage[i] + + buck1245_voltage_map_desc.step); + if (ret < 0) + goto err_out; + + max8997->buck5_vol[i] = ret = + max8997_get_voltage_proper_val( + &buck1245_voltage_map_desc, + pdata->buck5_voltage[i], + pdata->buck5_voltage[i] + + buck1245_voltage_map_desc.step); + if (ret < 0) + goto err_out; + + if (max_buck1 < max8997->buck1_vol[i]) + max_buck1 = max8997->buck1_vol[i]; + if (max_buck2 < max8997->buck2_vol[i]) + max_buck2 = max8997->buck2_vol[i]; + if (max_buck5 < max8997->buck5_vol[i]) + max_buck5 = max8997->buck5_vol[i]; + } + + /* For the safety, set max voltage before setting up */ + for (i = 0; i < 8; i++) { + max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i, + max_buck1, 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i, + max_buck2, 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i, + max_buck5, 0x3f); + } + + /* Initialize all the DVS related BUCK registers */ + for (i = 0; i < nr_dvs; i++) { + max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i, + max8997->buck1_vol[i], + 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i, + max8997->buck2_vol[i], + 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i, + max8997->buck5_vol[i], + 0x3f); + } + + /* + * If buck 1, 2, and 5 do not care DVS GPIO settings, ignore them. + * If at least one of them cares, set gpios. + */ + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + + if (!gpio_is_valid(pdata->buck125_gpios[0]) || + !gpio_is_valid(pdata->buck125_gpios[1]) || + !gpio_is_valid(pdata->buck125_gpios[2])) { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + ret = -EINVAL; + goto err_out; + } + + ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[0], + "MAX8997 SET1"); + if (ret) + goto err_out; + + ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[1], + "MAX8997 SET2"); + if (ret) + goto err_out; + + ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[2], + "MAX8997 SET3"); + if (ret) + goto err_out; + + gpio_direction_output(pdata->buck125_gpios[0], + (max8997->buck125_gpioindex >> 2) + & 0x1); /* SET1 */ + gpio_direction_output(pdata->buck125_gpios[1], + (max8997->buck125_gpioindex >> 1) + & 0x1); /* SET2 */ + gpio_direction_output(pdata->buck125_gpios[2], + (max8997->buck125_gpioindex >> 0) + & 0x1); /* SET3 */ + } + + /* DVS-GPIO disabled */ + max8997_update_reg(i2c, MAX8997_REG_BUCK1CTRL, (pdata->buck1_gpiodvs) ? + (1 << 1) : (0 << 1), 1 << 1); + max8997_update_reg(i2c, MAX8997_REG_BUCK2CTRL, (pdata->buck2_gpiodvs) ? + (1 << 1) : (0 << 1), 1 << 1); + max8997_update_reg(i2c, MAX8997_REG_BUCK5CTRL, (pdata->buck5_gpiodvs) ? + (1 << 1) : (0 << 1), 1 << 1); + + /* Misc Settings */ + max8997->ramp_delay = 10; /* set 10mV/us, which is the default */ + max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9); + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_desc *desc; + int id = pdata->regulators[i].id; + + desc = reg_voltage_map[id]; + if (desc) { + regulators[id].n_voltages = + (desc->max - desc->min) / desc->step + 1; + } else if (id == MAX8997_ESAFEOUT1 || id == MAX8997_ESAFEOUT2) { + regulators[id].volt_table = safeoutvolt; + regulators[id].n_voltages = ARRAY_SIZE(safeoutvolt); + } else if (id == MAX8997_CHARGER_CV) { + regulators[id].n_voltages = 16; + } + + config.dev = max8997->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = max8997; + config.of_node = pdata->regulators[i].reg_node; + + rdev[i] = regulator_register(®ulators[id], &config); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max8997->dev, "regulator init failed for %d\n", + id); + rdev[i] = NULL; + goto err; + } + } + + return 0; +err: + while (--i >= 0) + regulator_unregister(rdev[i]); +err_out: + return ret; +} + +static int max8997_pmic_remove(struct platform_device *pdev) +{ + struct max8997_data *max8997 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max8997->rdev; + int i; + + for (i = 0; i < max8997->num_regulators; i++) + regulator_unregister(rdev[i]); + return 0; +} + +static const struct platform_device_id max8997_pmic_id[] = { + { "max8997-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max8997_pmic_id); + +static struct platform_driver max8997_pmic_driver = { + .driver = { + .name = "max8997-pmic", + .owner = THIS_MODULE, + }, + .probe = max8997_pmic_probe, + .remove = max8997_pmic_remove, + .id_table = max8997_pmic_id, +}; + +static int __init max8997_pmic_init(void) +{ + return platform_driver_register(&max8997_pmic_driver); +} +subsys_initcall(max8997_pmic_init); + +static void __exit max8997_pmic_cleanup(void) +{ + platform_driver_unregister(&max8997_pmic_driver); +} +module_exit(max8997_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 8997/8966 Regulator Driver"); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c new file mode 100644 index 0000000..a57a1b1 --- /dev/null +++ b/drivers/regulator/max8998.c @@ -0,0 +1,854 @@ +/* + * max8998.c - Voltage regulator driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electronics + * Kyungmin Park + * Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct max8998_data { + struct device *dev; + struct max8998_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + u8 buck1_vol[4]; /* voltages for selection */ + u8 buck2_vol[2]; + unsigned int buck1_idx; /* index to last changed voltage */ + /* value in a set */ + unsigned int buck2_idx; +}; + +struct voltage_map_desc { + int min; + int max; + int step; +}; + +/* Voltage maps in uV*/ +static const struct voltage_map_desc ldo23_voltage_map_desc = { + .min = 800000, .step = 50000, .max = 1300000, +}; +static const struct voltage_map_desc ldo456711_voltage_map_desc = { + .min = 1600000, .step = 100000, .max = 3600000, +}; +static const struct voltage_map_desc ldo8_voltage_map_desc = { + .min = 3000000, .step = 100000, .max = 3600000, +}; +static const struct voltage_map_desc ldo9_voltage_map_desc = { + .min = 2800000, .step = 100000, .max = 3100000, +}; +static const struct voltage_map_desc ldo10_voltage_map_desc = { + .min = 950000, .step = 50000, .max = 1300000, +}; +static const struct voltage_map_desc ldo1213_voltage_map_desc = { + .min = 800000, .step = 100000, .max = 3300000, +}; +static const struct voltage_map_desc ldo1415_voltage_map_desc = { + .min = 1200000, .step = 100000, .max = 3300000, +}; +static const struct voltage_map_desc ldo1617_voltage_map_desc = { + .min = 1600000, .step = 100000, .max = 3600000, +}; +static const struct voltage_map_desc buck12_voltage_map_desc = { + .min = 750000, .step = 25000, .max = 1525000, +}; +static const struct voltage_map_desc buck3_voltage_map_desc = { + .min = 1600000, .step = 100000, .max = 3600000, +}; +static const struct voltage_map_desc buck4_voltage_map_desc = { + .min = 800000, .step = 100000, .max = 2300000, +}; + +static const struct voltage_map_desc *ldo_voltage_map[] = { + NULL, + NULL, + &ldo23_voltage_map_desc, /* LDO2 */ + &ldo23_voltage_map_desc, /* LDO3 */ + &ldo456711_voltage_map_desc, /* LDO4 */ + &ldo456711_voltage_map_desc, /* LDO5 */ + &ldo456711_voltage_map_desc, /* LDO6 */ + &ldo456711_voltage_map_desc, /* LDO7 */ + &ldo8_voltage_map_desc, /* LDO8 */ + &ldo9_voltage_map_desc, /* LDO9 */ + &ldo10_voltage_map_desc, /* LDO10 */ + &ldo456711_voltage_map_desc, /* LDO11 */ + &ldo1213_voltage_map_desc, /* LDO12 */ + &ldo1213_voltage_map_desc, /* LDO13 */ + &ldo1415_voltage_map_desc, /* LDO14 */ + &ldo1415_voltage_map_desc, /* LDO15 */ + &ldo1617_voltage_map_desc, /* LDO16 */ + &ldo1617_voltage_map_desc, /* LDO17 */ + &buck12_voltage_map_desc, /* BUCK1 */ + &buck12_voltage_map_desc, /* BUCK2 */ + &buck3_voltage_map_desc, /* BUCK3 */ + &buck4_voltage_map_desc, /* BUCK4 */ +}; + +static int max8998_get_enable_register(struct regulator_dev *rdev, + int *reg, int *shift) +{ + int ldo = rdev_get_id(rdev); + + switch (ldo) { + case MAX8998_LDO2 ... MAX8998_LDO5: + *reg = MAX8998_REG_ONOFF1; + *shift = 3 - (ldo - MAX8998_LDO2); + break; + case MAX8998_LDO6 ... MAX8998_LDO13: + *reg = MAX8998_REG_ONOFF2; + *shift = 7 - (ldo - MAX8998_LDO6); + break; + case MAX8998_LDO14 ... MAX8998_LDO17: + *reg = MAX8998_REG_ONOFF3; + *shift = 7 - (ldo - MAX8998_LDO14); + break; + case MAX8998_BUCK1 ... MAX8998_BUCK4: + *reg = MAX8998_REG_ONOFF1; + *shift = 7 - (ldo - MAX8998_BUCK1); + break; + case MAX8998_EN32KHZ_AP ... MAX8998_ENVICHG: + *reg = MAX8998_REG_ONOFF4; + *shift = 7 - (ldo - MAX8998_EN32KHZ_AP); + break; + case MAX8998_ESAFEOUT1 ... MAX8998_ESAFEOUT2: + *reg = MAX8998_REG_CHGR2; + *shift = 7 - (ldo - MAX8998_ESAFEOUT1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max8998_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; + int ret, reg, shift = 8; + u8 val; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + ret = max8998_read_reg(i2c, reg, &val); + if (ret) + return ret; + + return val & (1 << shift); +} + +static int max8998_ldo_enable(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; + int reg, shift = 8, ret; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8998_update_reg(i2c, reg, 1<iodev->i2c; + int reg, shift = 8, ret; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8998_update_reg(i2c, reg, 0, 1<buck1_idx; + break; + case MAX8998_BUCK2: + reg = MAX8998_REG_BUCK2_VOLTAGE1 + max8998->buck2_idx; + break; + case MAX8998_BUCK3: + reg = MAX8998_REG_BUCK3; + break; + case MAX8998_BUCK4: + reg = MAX8998_REG_BUCK4; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max8998_get_voltage_sel(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; + int reg, shift = 0, mask, ret; + u8 val; + + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8998_read_reg(i2c, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + return val; +} + +static int max8998_set_voltage_ldo_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; + int reg, shift = 0, mask, ret; + + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8998_update_reg(i2c, reg, selector<> 1) & 0x1); +} + +static inline void buck2_gpio_set(int gpio, int v) +{ + gpio_set_value(gpio, v & 0x1); +} + +static int max8998_set_voltage_buck_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct max8998_platform_data *pdata = + dev_get_platdata(max8998->iodev->dev); + struct i2c_client *i2c = max8998->iodev->i2c; + int buck = rdev_get_id(rdev); + int reg, shift = 0, mask, ret, j; + static u8 buck1_last_val; + + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + switch (buck) { + case MAX8998_BUCK1: + dev_dbg(max8998->dev, + "BUCK1, selector:%d, buck1_vol1:%d, buck1_vol2:%d\n" + "buck1_vol3:%d, buck1_vol4:%d\n", + selector, max8998->buck1_vol[0], max8998->buck1_vol[1], + max8998->buck1_vol[2], max8998->buck1_vol[3]); + + if (gpio_is_valid(pdata->buck1_set1) && + gpio_is_valid(pdata->buck1_set2)) { + + /* check if requested voltage */ + /* value is already defined */ + for (j = 0; j < ARRAY_SIZE(max8998->buck1_vol); j++) { + if (max8998->buck1_vol[j] == selector) { + max8998->buck1_idx = j; + buck1_gpio_set(pdata->buck1_set1, + pdata->buck1_set2, j); + goto buck1_exit; + } + } + + if (pdata->buck_voltage_lock) + return -EINVAL; + + /* no predefine regulator found */ + max8998->buck1_idx = (buck1_last_val % 2) + 2; + dev_dbg(max8998->dev, "max8998->buck1_idx:%d\n", + max8998->buck1_idx); + max8998->buck1_vol[max8998->buck1_idx] = selector; + ret = max8998_get_voltage_register(rdev, ®, + &shift, + &mask); + ret = max8998_write_reg(i2c, reg, selector); + buck1_gpio_set(pdata->buck1_set1, + pdata->buck1_set2, max8998->buck1_idx); + buck1_last_val++; +buck1_exit: + dev_dbg(max8998->dev, "%s: SET1:%d, SET2:%d\n", + i2c->name, gpio_get_value(pdata->buck1_set1), + gpio_get_value(pdata->buck1_set2)); + break; + } else { + ret = max8998_write_reg(i2c, reg, selector); + } + break; + + case MAX8998_BUCK2: + dev_dbg(max8998->dev, + "BUCK2, selector:%d buck2_vol1:%d, buck2_vol2:%d\n", + selector, max8998->buck2_vol[0], max8998->buck2_vol[1]); + if (gpio_is_valid(pdata->buck2_set3)) { + + /* check if requested voltage */ + /* value is already defined */ + for (j = 0; j < ARRAY_SIZE(max8998->buck2_vol); j++) { + if (max8998->buck2_vol[j] == selector) { + max8998->buck2_idx = j; + buck2_gpio_set(pdata->buck2_set3, j); + goto buck2_exit; + } + } + + if (pdata->buck_voltage_lock) + return -EINVAL; + + max8998_get_voltage_register(rdev, + ®, &shift, &mask); + ret = max8998_write_reg(i2c, reg, selector); + max8998->buck2_vol[max8998->buck2_idx] = selector; + buck2_gpio_set(pdata->buck2_set3, max8998->buck2_idx); +buck2_exit: + dev_dbg(max8998->dev, "%s: SET3:%d\n", i2c->name, + gpio_get_value(pdata->buck2_set3)); + } else { + ret = max8998_write_reg(i2c, reg, selector); + } + break; + + case MAX8998_BUCK3: + case MAX8998_BUCK4: + ret = max8998_update_reg(i2c, reg, selector<iodev->i2c; + const struct voltage_map_desc *desc; + int buck = rdev_get_id(rdev); + u8 val = 0; + int difference, ret; + + if (buck < MAX8998_BUCK1 || buck > MAX8998_BUCK4) + return -EINVAL; + + desc = ldo_voltage_map[buck]; + + /* Voltage stabilization */ + ret = max8998_read_reg(i2c, MAX8998_REG_ONOFF4, &val); + if (ret) + return ret; + + /* lp3974 hasn't got ENRAMP bit - ramp is assumed as true */ + /* MAX8998 has ENRAMP bit implemented, so test it*/ + if (max8998->iodev->type == TYPE_MAX8998 && !(val & MAX8998_ENRAMP)) + return 0; + + difference = (new_selector - old_selector) * desc->step / 1000; + if (difference > 0) + return DIV_ROUND_UP(difference, (val & 0x0f) + 1); + + return 0; +} + +static struct regulator_ops max8998_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = max8998_ldo_is_enabled, + .enable = max8998_ldo_enable, + .disable = max8998_ldo_disable, + .get_voltage_sel = max8998_get_voltage_sel, + .set_voltage_sel = max8998_set_voltage_ldo_sel, +}; + +static struct regulator_ops max8998_buck_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = max8998_ldo_is_enabled, + .enable = max8998_ldo_enable, + .disable = max8998_ldo_disable, + .get_voltage_sel = max8998_get_voltage_sel, + .set_voltage_sel = max8998_set_voltage_buck_sel, + .set_voltage_time_sel = max8998_set_voltage_buck_time_sel, +}; + +static struct regulator_ops max8998_others_ops = { + .is_enabled = max8998_ldo_is_enabled, + .enable = max8998_ldo_enable, + .disable = max8998_ldo_disable, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO2", + .id = MAX8998_LDO2, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO3", + .id = MAX8998_LDO3, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO4", + .id = MAX8998_LDO4, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO5", + .id = MAX8998_LDO5, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO6", + .id = MAX8998_LDO6, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO7", + .id = MAX8998_LDO7, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO8", + .id = MAX8998_LDO8, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO9", + .id = MAX8998_LDO9, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO10", + .id = MAX8998_LDO10, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO11", + .id = MAX8998_LDO11, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO12", + .id = MAX8998_LDO12, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO13", + .id = MAX8998_LDO13, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO14", + .id = MAX8998_LDO14, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO15", + .id = MAX8998_LDO15, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO16", + .id = MAX8998_LDO16, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO17", + .id = MAX8998_LDO17, + .ops = &max8998_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK1", + .id = MAX8998_BUCK1, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK2", + .id = MAX8998_BUCK2, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK3", + .id = MAX8998_BUCK3, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "BUCK4", + .id = MAX8998_BUCK4, + .ops = &max8998_buck_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz AP", + .id = MAX8998_EN32KHZ_AP, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz CP", + .id = MAX8998_EN32KHZ_CP, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ENVICHG", + .id = MAX8998_ENVICHG, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT1", + .id = MAX8998_ESAFEOUT1, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT2", + .id = MAX8998_ESAFEOUT2, + .ops = &max8998_others_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + } +}; + +static int max8998_pmic_probe(struct platform_device *pdev) +{ + struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_config config = { }; + struct regulator_dev **rdev; + struct max8998_data *max8998; + struct i2c_client *i2c; + int i, ret, size; + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied\n"); + return -ENODEV; + } + + max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_data), + GFP_KERNEL); + if (!max8998) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * pdata->num_regulators; + max8998->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!max8998->rdev) + return -ENOMEM; + + rdev = max8998->rdev; + max8998->dev = &pdev->dev; + max8998->iodev = iodev; + max8998->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, max8998); + i2c = max8998->iodev->i2c; + + max8998->buck1_idx = pdata->buck1_default_idx; + max8998->buck2_idx = pdata->buck2_default_idx; + + /* NOTE: */ + /* For unused GPIO NOT marked as -1 (thereof equal to 0) WARN_ON */ + /* will be displayed */ + + /* Check if MAX8998 voltage selection GPIOs are defined */ + if (gpio_is_valid(pdata->buck1_set1) && + gpio_is_valid(pdata->buck1_set2)) { + /* Check if SET1 is not equal to 0 */ + if (!pdata->buck1_set1) { + dev_err(&pdev->dev, + "MAX8998 SET1 GPIO defined as 0 !\n"); + WARN_ON(!pdata->buck1_set1); + ret = -EIO; + goto err_out; + } + /* Check if SET2 is not equal to 0 */ + if (!pdata->buck1_set2) { + dev_err(&pdev->dev, + "MAX8998 SET2 GPIO defined as 0 !\n"); + WARN_ON(!pdata->buck1_set2); + ret = -EIO; + goto err_out; + } + + gpio_request(pdata->buck1_set1, "MAX8998 BUCK1_SET1"); + gpio_direction_output(pdata->buck1_set1, + max8998->buck1_idx & 0x1); + + + gpio_request(pdata->buck1_set2, "MAX8998 BUCK1_SET2"); + gpio_direction_output(pdata->buck1_set2, + (max8998->buck1_idx >> 1) & 0x1); + /* Set predefined value for BUCK1 register 1 */ + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck1_voltage1) + i++; + max8998->buck1_vol[0] = i; + ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i); + if (ret) + goto err_out; + + /* Set predefined value for BUCK1 register 2 */ + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck1_voltage2) + i++; + + max8998->buck1_vol[1] = i; + ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE2, i); + if (ret) + goto err_out; + + /* Set predefined value for BUCK1 register 3 */ + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck1_voltage3) + i++; + + max8998->buck1_vol[2] = i; + ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE3, i); + if (ret) + goto err_out; + + /* Set predefined value for BUCK1 register 4 */ + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck1_voltage4) + i++; + + max8998->buck1_vol[3] = i; + ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE4, i); + if (ret) + goto err_out; + + } + + if (gpio_is_valid(pdata->buck2_set3)) { + /* Check if SET3 is not equal to 0 */ + if (!pdata->buck2_set3) { + dev_err(&pdev->dev, + "MAX8998 SET3 GPIO defined as 0 !\n"); + WARN_ON(!pdata->buck2_set3); + ret = -EIO; + goto err_out; + } + gpio_request(pdata->buck2_set3, "MAX8998 BUCK2_SET3"); + gpio_direction_output(pdata->buck2_set3, + max8998->buck2_idx & 0x1); + + /* BUCK2 register 1 */ + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck2_voltage1) + i++; + max8998->buck2_vol[0] = i; + ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i); + if (ret) + goto err_out; + + /* BUCK2 register 2 */ + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck2_voltage2) + i++; + max8998->buck2_vol[1] = i; + ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i); + if (ret) + goto err_out; + } + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_desc *desc; + int id = pdata->regulators[i].id; + int index = id - MAX8998_LDO2; + + desc = ldo_voltage_map[id]; + if (desc && regulators[index].ops != &max8998_others_ops) { + int count = (desc->max - desc->min) / desc->step + 1; + + regulators[index].n_voltages = count; + regulators[index].min_uV = desc->min; + regulators[index].uV_step = desc->step; + } + + config.dev = max8998->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = max8998; + + rdev[i] = regulator_register(®ulators[index], &config); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max8998->dev, "regulator init failed\n"); + rdev[i] = NULL; + goto err; + } + } + + + return 0; +err: + while (--i >= 0) + regulator_unregister(rdev[i]); +err_out: + return ret; +} + +static int max8998_pmic_remove(struct platform_device *pdev) +{ + struct max8998_data *max8998 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max8998->rdev; + int i; + + for (i = 0; i < max8998->num_regulators; i++) + regulator_unregister(rdev[i]); + return 0; +} + +static const struct platform_device_id max8998_pmic_id[] = { + { "max8998-pmic", TYPE_MAX8998 }, + { "lp3974-pmic", TYPE_LP3974 }, + { } +}; +MODULE_DEVICE_TABLE(platform, max8998_pmic_id); + +static struct platform_driver max8998_pmic_driver = { + .driver = { + .name = "max8998-pmic", + .owner = THIS_MODULE, + }, + .probe = max8998_pmic_probe, + .remove = max8998_pmic_remove, + .id_table = max8998_pmic_id, +}; + +static int __init max8998_pmic_init(void) +{ + return platform_driver_register(&max8998_pmic_driver); +} +subsys_initcall(max8998_pmic_init); + +static void __exit max8998_pmic_cleanup(void) +{ + platform_driver_unregister(&max8998_pmic_driver); +} +module_exit(max8998_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 8998 voltage regulator driver"); +MODULE_AUTHOR("Kyungmin Park "); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c new file mode 100644 index 0000000..fdf7f0a --- /dev/null +++ b/drivers/regulator/mc13783-regulator.c @@ -0,0 +1,501 @@ +/* + * Regulator Driver for Freescale MC13783 PMIC + * + * Copyright 2010 Yong Shen + * Copyright (C) 2008 Sascha Hauer, Pengutronix + * Copyright 2009 Alberto Panizzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mc13xxx.h" + +#define MC13783_REG_SWITCHERS0 24 +/* Enable does not exist for SW1A */ +#define MC13783_REG_SWITCHERS0_SW1AEN 0 +#define MC13783_REG_SWITCHERS0_SW1AVSEL 0 +#define MC13783_REG_SWITCHERS0_SW1AVSEL_M (63 << 0) + +#define MC13783_REG_SWITCHERS1 25 +/* Enable does not exist for SW1B */ +#define MC13783_REG_SWITCHERS1_SW1BEN 0 +#define MC13783_REG_SWITCHERS1_SW1BVSEL 0 +#define MC13783_REG_SWITCHERS1_SW1BVSEL_M (63 << 0) + +#define MC13783_REG_SWITCHERS2 26 +/* Enable does not exist for SW2A */ +#define MC13783_REG_SWITCHERS2_SW2AEN 0 +#define MC13783_REG_SWITCHERS2_SW2AVSEL 0 +#define MC13783_REG_SWITCHERS2_SW2AVSEL_M (63 << 0) + +#define MC13783_REG_SWITCHERS3 27 +/* Enable does not exist for SW2B */ +#define MC13783_REG_SWITCHERS3_SW2BEN 0 +#define MC13783_REG_SWITCHERS3_SW2BVSEL 0 +#define MC13783_REG_SWITCHERS3_SW2BVSEL_M (63 << 0) + +#define MC13783_REG_SWITCHERS5 29 +#define MC13783_REG_SWITCHERS5_SW3EN (1 << 20) +#define MC13783_REG_SWITCHERS5_SW3VSEL 18 +#define MC13783_REG_SWITCHERS5_SW3VSEL_M (3 << 18) + +#define MC13783_REG_REGULATORSETTING0 30 +#define MC13783_REG_REGULATORSETTING0_VIOLOVSEL 2 +#define MC13783_REG_REGULATORSETTING0_VDIGVSEL 4 +#define MC13783_REG_REGULATORSETTING0_VGENVSEL 6 +#define MC13783_REG_REGULATORSETTING0_VRFDIGVSEL 9 +#define MC13783_REG_REGULATORSETTING0_VRFREFVSEL 11 +#define MC13783_REG_REGULATORSETTING0_VRFCPVSEL 13 +#define MC13783_REG_REGULATORSETTING0_VSIMVSEL 14 +#define MC13783_REG_REGULATORSETTING0_VESIMVSEL 15 +#define MC13783_REG_REGULATORSETTING0_VCAMVSEL 16 + +#define MC13783_REG_REGULATORSETTING0_VIOLOVSEL_M (3 << 2) +#define MC13783_REG_REGULATORSETTING0_VDIGVSEL_M (3 << 4) +#define MC13783_REG_REGULATORSETTING0_VGENVSEL_M (7 << 6) +#define MC13783_REG_REGULATORSETTING0_VRFDIGVSEL_M (3 << 9) +#define MC13783_REG_REGULATORSETTING0_VRFREFVSEL_M (3 << 11) +#define MC13783_REG_REGULATORSETTING0_VRFCPVSEL_M (1 << 13) +#define MC13783_REG_REGULATORSETTING0_VSIMVSEL_M (1 << 14) +#define MC13783_REG_REGULATORSETTING0_VESIMVSEL_M (1 << 15) +#define MC13783_REG_REGULATORSETTING0_VCAMVSEL_M (7 << 16) + +#define MC13783_REG_REGULATORSETTING1 31 +#define MC13783_REG_REGULATORSETTING1_VVIBVSEL 0 +#define MC13783_REG_REGULATORSETTING1_VRF1VSEL 2 +#define MC13783_REG_REGULATORSETTING1_VRF2VSEL 4 +#define MC13783_REG_REGULATORSETTING1_VMMC1VSEL 6 +#define MC13783_REG_REGULATORSETTING1_VMMC2VSEL 9 + +#define MC13783_REG_REGULATORSETTING1_VVIBVSEL_M (3 << 0) +#define MC13783_REG_REGULATORSETTING1_VRF1VSEL_M (3 << 2) +#define MC13783_REG_REGULATORSETTING1_VRF2VSEL_M (3 << 4) +#define MC13783_REG_REGULATORSETTING1_VMMC1VSEL_M (7 << 6) +#define MC13783_REG_REGULATORSETTING1_VMMC2VSEL_M (7 << 9) + +#define MC13783_REG_REGULATORMODE0 32 +#define MC13783_REG_REGULATORMODE0_VAUDIOEN (1 << 0) +#define MC13783_REG_REGULATORMODE0_VIOHIEN (1 << 3) +#define MC13783_REG_REGULATORMODE0_VIOLOEN (1 << 6) +#define MC13783_REG_REGULATORMODE0_VDIGEN (1 << 9) +#define MC13783_REG_REGULATORMODE0_VGENEN (1 << 12) +#define MC13783_REG_REGULATORMODE0_VRFDIGEN (1 << 15) +#define MC13783_REG_REGULATORMODE0_VRFREFEN (1 << 18) +#define MC13783_REG_REGULATORMODE0_VRFCPEN (1 << 21) + +#define MC13783_REG_REGULATORMODE1 33 +#define MC13783_REG_REGULATORMODE1_VSIMEN (1 << 0) +#define MC13783_REG_REGULATORMODE1_VESIMEN (1 << 3) +#define MC13783_REG_REGULATORMODE1_VCAMEN (1 << 6) +#define MC13783_REG_REGULATORMODE1_VRFBGEN (1 << 9) +#define MC13783_REG_REGULATORMODE1_VVIBEN (1 << 11) +#define MC13783_REG_REGULATORMODE1_VRF1EN (1 << 12) +#define MC13783_REG_REGULATORMODE1_VRF2EN (1 << 15) +#define MC13783_REG_REGULATORMODE1_VMMC1EN (1 << 18) +#define MC13783_REG_REGULATORMODE1_VMMC2EN (1 << 21) + +#define MC13783_REG_POWERMISC 34 +#define MC13783_REG_POWERMISC_GPO1EN (1 << 6) +#define MC13783_REG_POWERMISC_GPO2EN (1 << 8) +#define MC13783_REG_POWERMISC_GPO3EN (1 << 10) +#define MC13783_REG_POWERMISC_GPO4EN (1 << 12) +#define MC13783_REG_POWERMISC_PWGT1SPIEN (1 << 15) +#define MC13783_REG_POWERMISC_PWGT2SPIEN (1 << 16) + +#define MC13783_REG_POWERMISC_PWGTSPI_M (3 << 15) + + +/* Voltage Values */ +static const int mc13783_sw1x_val[] = { + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, + 1600000, 1625000, 1650000, 1675000, + 1700000, 1700000, 1700000, 1700000, + 1800000, 1800000, 1800000, 1800000, + 1850000, 1850000, 1850000, 1850000, + 2000000, 2000000, 2000000, 2000000, + 2100000, 2100000, 2100000, 2100000, + 2200000, 2200000, 2200000, 2200000, + 2200000, 2200000, 2200000, 2200000, + 2200000, 2200000, 2200000, 2200000, +}; + +static const int mc13783_sw2x_val[] = { + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, + 1600000, 1625000, 1650000, 1675000, + 1700000, 1700000, 1700000, 1700000, + 1800000, 1800000, 1800000, 1800000, + 1900000, 1900000, 1900000, 1900000, + 2000000, 2000000, 2000000, 2000000, + 2100000, 2100000, 2100000, 2100000, + 2200000, 2200000, 2200000, 2200000, + 2200000, 2200000, 2200000, 2200000, + 2200000, 2200000, 2200000, 2200000, +}; + +static const unsigned int mc13783_sw3_val[] = { + 5000000, 5000000, 5000000, 5500000, +}; + +static const unsigned int mc13783_vaudio_val[] = { + 2775000, +}; + +static const unsigned int mc13783_viohi_val[] = { + 2775000, +}; + +static const unsigned int mc13783_violo_val[] = { + 1200000, 1300000, 1500000, 1800000, +}; + +static const unsigned int mc13783_vdig_val[] = { + 1200000, 1300000, 1500000, 1800000, +}; + +static const unsigned int mc13783_vgen_val[] = { + 1200000, 1300000, 1500000, 1800000, + 1100000, 2000000, 2775000, 2400000, +}; + +static const unsigned int mc13783_vrfdig_val[] = { + 1200000, 1500000, 1800000, 1875000, +}; + +static const unsigned int mc13783_vrfref_val[] = { + 2475000, 2600000, 2700000, 2775000, +}; + +static const unsigned int mc13783_vrfcp_val[] = { + 2700000, 2775000, +}; + +static const unsigned int mc13783_vsim_val[] = { + 1800000, 2900000, 3000000, +}; + +static const unsigned int mc13783_vesim_val[] = { + 1800000, 2900000, +}; + +static const unsigned int mc13783_vcam_val[] = { + 1500000, 1800000, 2500000, 2550000, + 2600000, 2750000, 2800000, 3000000, +}; + +static const unsigned int mc13783_vrfbg_val[] = { + 1250000, +}; + +static const unsigned int mc13783_vvib_val[] = { + 1300000, 1800000, 2000000, 3000000, +}; + +static const unsigned int mc13783_vmmc_val[] = { + 1600000, 1800000, 2000000, 2600000, + 2700000, 2800000, 2900000, 3000000, +}; + +static const unsigned int mc13783_vrf_val[] = { + 1500000, 1875000, 2700000, 2775000, +}; + +static const unsigned int mc13783_gpo_val[] = { + 3100000, +}; + +static const unsigned int mc13783_pwgtdrv_val[] = { + 5500000, +}; + +static struct regulator_ops mc13783_gpo_regulator_ops; + +#define MC13783_DEFINE(prefix, name, reg, vsel_reg, voltages) \ + MC13xxx_DEFINE(MC13783_REG_, name, reg, vsel_reg, voltages, \ + mc13xxx_regulator_ops) + +#define MC13783_FIXED_DEFINE(prefix, name, reg, voltages) \ + MC13xxx_FIXED_DEFINE(MC13783_REG_, name, reg, voltages, \ + mc13xxx_fixed_regulator_ops) + +#define MC13783_GPO_DEFINE(prefix, name, reg, voltages) \ + MC13xxx_GPO_DEFINE(MC13783_REG_, name, reg, voltages, \ + mc13783_gpo_regulator_ops) + +#define MC13783_DEFINE_SW(_name, _reg, _vsel_reg, _voltages) \ + MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages) +#define MC13783_DEFINE_REGU(_name, _reg, _vsel_reg, _voltages) \ + MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages) + +static struct mc13xxx_regulator mc13783_regulators[] = { + MC13783_DEFINE_SW(SW1A, SWITCHERS0, SWITCHERS0, mc13783_sw1x_val), + MC13783_DEFINE_SW(SW1B, SWITCHERS1, SWITCHERS1, mc13783_sw1x_val), + MC13783_DEFINE_SW(SW2A, SWITCHERS2, SWITCHERS2, mc13783_sw2x_val), + MC13783_DEFINE_SW(SW2B, SWITCHERS3, SWITCHERS3, mc13783_sw2x_val), + MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val), + + MC13783_FIXED_DEFINE(REG, VAUDIO, REGULATORMODE0, mc13783_vaudio_val), + MC13783_FIXED_DEFINE(REG, VIOHI, REGULATORMODE0, mc13783_viohi_val), + MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_violo_val), + MC13783_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vdig_val), + MC13783_DEFINE_REGU(VGEN, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vgen_val), + MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vrfdig_val), + MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vrfref_val), + MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vrfcp_val), + MC13783_DEFINE_REGU(VSIM, REGULATORMODE1, REGULATORSETTING0, \ + mc13783_vsim_val), + MC13783_DEFINE_REGU(VESIM, REGULATORMODE1, REGULATORSETTING0, \ + mc13783_vesim_val), + MC13783_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, \ + mc13783_vcam_val), + MC13783_FIXED_DEFINE(REG, VRFBG, REGULATORMODE1, mc13783_vrfbg_val), + MC13783_DEFINE_REGU(VVIB, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vvib_val), + MC13783_DEFINE_REGU(VRF1, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vrf_val), + MC13783_DEFINE_REGU(VRF2, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vrf_val), + MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vmmc_val), + MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vmmc_val), + MC13783_GPO_DEFINE(REG, GPO1, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REG, GPO2, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REG, GPO3, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REG, GPO4, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REG, PWGT1SPI, POWERMISC, mc13783_pwgtdrv_val), + MC13783_GPO_DEFINE(REG, PWGT2SPI, POWERMISC, mc13783_pwgtdrv_val), +}; + +static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask, + u32 val) +{ + struct mc13xxx *mc13783 = priv->mc13xxx; + int ret; + u32 valread; + + BUG_ON(val & ~mask); + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(mc13783, MC13783_REG_POWERMISC, &valread); + if (ret) + goto out; + + /* Update the stored state for Power Gates. */ + priv->powermisc_pwgt_state = + (priv->powermisc_pwgt_state & ~mask) | val; + priv->powermisc_pwgt_state &= MC13783_REG_POWERMISC_PWGTSPI_M; + + /* Construct the new register value */ + valread = (valread & ~mask) | val; + /* Overwrite the PWGTxEN with the stored version */ + valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) | + priv->powermisc_pwgt_state; + + ret = mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread); +out: + mc13xxx_unlock(priv->mc13xxx); + return ret; +} + +static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int id = rdev_get_id(rdev); + u32 en_val = mc13xxx_regulators[id].enable_bit; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + /* Power Gate enable value is 0 */ + if (id == MC13783_REG_PWGT1SPI || + id == MC13783_REG_PWGT2SPI) + en_val = 0; + + return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit, + en_val); +} + +static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int id = rdev_get_id(rdev); + u32 dis_val = 0; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + /* Power Gate disable value is 1 */ + if (id == MC13783_REG_PWGT1SPI || + id == MC13783_REG_PWGT2SPI) + dis_val = mc13xxx_regulators[id].enable_bit; + + return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit, + dis_val); +} + +static int mc13783_gpo_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int ret, id = rdev_get_id(rdev); + unsigned int val; + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(priv->mc13xxx, mc13xxx_regulators[id].reg, &val); + mc13xxx_unlock(priv->mc13xxx); + + if (ret) + return ret; + + /* Power Gates state is stored in powermisc_pwgt_state + * where the meaning of bits is negated */ + val = (val & ~MC13783_REG_POWERMISC_PWGTSPI_M) | + (priv->powermisc_pwgt_state ^ MC13783_REG_POWERMISC_PWGTSPI_M); + + return (val & mc13xxx_regulators[id].enable_bit) != 0; +} + +static struct regulator_ops mc13783_gpo_regulator_ops = { + .enable = mc13783_gpo_regulator_enable, + .disable = mc13783_gpo_regulator_disable, + .is_enabled = mc13783_gpo_regulator_is_enabled, + .list_voltage = regulator_list_voltage_table, + .set_voltage = mc13xxx_fixed_regulator_set_voltage, +}; + +static int mc13783_regulator_probe(struct platform_device *pdev) +{ + struct mc13xxx_regulator_priv *priv; + struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); + struct mc13xxx_regulator_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct mc13xxx_regulator_init_data *mc13xxx_data; + struct regulator_config config = { }; + int i, ret, num_regulators; + + num_regulators = mc13xxx_get_num_regulators_dt(pdev); + + if (num_regulators <= 0 && pdata) + num_regulators = pdata->num_regulators; + if (num_regulators <= 0) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv) + + num_regulators * sizeof(priv->regulators[0]), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num_regulators = num_regulators; + priv->mc13xxx_regulators = mc13783_regulators; + priv->mc13xxx = mc13783; + platform_set_drvdata(pdev, priv); + + mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13783_regulators, + ARRAY_SIZE(mc13783_regulators)); + + for (i = 0; i < priv->num_regulators; i++) { + struct regulator_init_data *init_data; + struct regulator_desc *desc; + struct device_node *node = NULL; + int id; + + if (mc13xxx_data) { + id = mc13xxx_data[i].id; + init_data = mc13xxx_data[i].init_data; + node = mc13xxx_data[i].node; + } else { + id = pdata->regulators[i].id; + init_data = pdata->regulators[i].init_data; + } + desc = &mc13783_regulators[id].desc; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = priv; + config.of_node = node; + + priv->regulators[i] = regulator_register(desc, &config); + if (IS_ERR(priv->regulators[i])) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + mc13783_regulators[i].desc.name); + ret = PTR_ERR(priv->regulators[i]); + goto err; + } + } + + return 0; +err: + while (--i >= 0) + regulator_unregister(priv->regulators[i]); + + return ret; +} + +static int mc13783_regulator_remove(struct platform_device *pdev) +{ + struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); + int i; + + platform_set_drvdata(pdev, NULL); + + for (i = 0; i < priv->num_regulators; i++) + regulator_unregister(priv->regulators[i]); + + return 0; +} + +static struct platform_driver mc13783_regulator_driver = { + .driver = { + .name = "mc13783-regulator", + .owner = THIS_MODULE, + }, + .remove = mc13783_regulator_remove, + .probe = mc13783_regulator_probe, +}; + +static int __init mc13783_regulator_init(void) +{ + return platform_driver_register(&mc13783_regulator_driver); +} +subsys_initcall(mc13783_regulator_init); + +static void __exit mc13783_regulator_exit(void) +{ + platform_driver_unregister(&mc13783_regulator_driver); +} +module_exit(mc13783_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer "); +MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC"); +MODULE_ALIAS("platform:mc13783-regulator"); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c new file mode 100644 index 0000000..b716283 --- /dev/null +++ b/drivers/regulator/mc13892-regulator.c @@ -0,0 +1,671 @@ +/* + * Regulator Driver for Freescale MC13892 PMIC + * + * Copyright 2010 Yong Shen + * + * Based on draft driver from Arnaud Patard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mc13xxx.h" + +#define MC13892_REVISION 7 + +#define MC13892_POWERCTL0 13 +#define MC13892_POWERCTL0_USEROFFSPI 3 +#define MC13892_POWERCTL0_VCOINCELLVSEL 20 +#define MC13892_POWERCTL0_VCOINCELLVSEL_M (7<<20) +#define MC13892_POWERCTL0_VCOINCELLEN (1<<23) + +#define MC13892_SWITCHERS0_SWxHI (1<<23) + +#define MC13892_SWITCHERS0 24 +#define MC13892_SWITCHERS0_SW1VSEL 0 +#define MC13892_SWITCHERS0_SW1VSEL_M (0x1f<<0) +#define MC13892_SWITCHERS0_SW1HI (1<<23) +#define MC13892_SWITCHERS0_SW1EN 0 + +#define MC13892_SWITCHERS1 25 +#define MC13892_SWITCHERS1_SW2VSEL 0 +#define MC13892_SWITCHERS1_SW2VSEL_M (0x1f<<0) +#define MC13892_SWITCHERS1_SW2HI (1<<23) +#define MC13892_SWITCHERS1_SW2EN 0 + +#define MC13892_SWITCHERS2 26 +#define MC13892_SWITCHERS2_SW3VSEL 0 +#define MC13892_SWITCHERS2_SW3VSEL_M (0x1f<<0) +#define MC13892_SWITCHERS2_SW3HI (1<<23) +#define MC13892_SWITCHERS2_SW3EN 0 + +#define MC13892_SWITCHERS3 27 +#define MC13892_SWITCHERS3_SW4VSEL 0 +#define MC13892_SWITCHERS3_SW4VSEL_M (0x1f<<0) +#define MC13892_SWITCHERS3_SW4HI (1<<23) +#define MC13892_SWITCHERS3_SW4EN 0 + +#define MC13892_SWITCHERS4 28 +#define MC13892_SWITCHERS4_SW1MODE 0 +#define MC13892_SWITCHERS4_SW1MODE_AUTO (8<<0) +#define MC13892_SWITCHERS4_SW1MODE_M (0xf<<0) +#define MC13892_SWITCHERS4_SW2MODE 10 +#define MC13892_SWITCHERS4_SW2MODE_AUTO (8<<10) +#define MC13892_SWITCHERS4_SW2MODE_M (0xf<<10) + +#define MC13892_SWITCHERS5 29 +#define MC13892_SWITCHERS5_SW3MODE 0 +#define MC13892_SWITCHERS5_SW3MODE_AUTO (8<<0) +#define MC13892_SWITCHERS5_SW3MODE_M (0xf<<0) +#define MC13892_SWITCHERS5_SW4MODE 8 +#define MC13892_SWITCHERS5_SW4MODE_AUTO (8<<8) +#define MC13892_SWITCHERS5_SW4MODE_M (0xf<<8) +#define MC13892_SWITCHERS5_SWBSTEN (1<<20) + +#define MC13892_REGULATORSETTING0 30 +#define MC13892_REGULATORSETTING0_VGEN1VSEL 0 +#define MC13892_REGULATORSETTING0_VDIGVSEL 4 +#define MC13892_REGULATORSETTING0_VGEN2VSEL 6 +#define MC13892_REGULATORSETTING0_VPLLVSEL 9 +#define MC13892_REGULATORSETTING0_VUSB2VSEL 11 +#define MC13892_REGULATORSETTING0_VGEN3VSEL 14 +#define MC13892_REGULATORSETTING0_VCAMVSEL 16 + +#define MC13892_REGULATORSETTING0_VGEN1VSEL_M (3<<0) +#define MC13892_REGULATORSETTING0_VDIGVSEL_M (3<<4) +#define MC13892_REGULATORSETTING0_VGEN2VSEL_M (7<<6) +#define MC13892_REGULATORSETTING0_VPLLVSEL_M (3<<9) +#define MC13892_REGULATORSETTING0_VUSB2VSEL_M (3<<11) +#define MC13892_REGULATORSETTING0_VGEN3VSEL_M (1<<14) +#define MC13892_REGULATORSETTING0_VCAMVSEL_M (3<<16) + +#define MC13892_REGULATORSETTING1 31 +#define MC13892_REGULATORSETTING1_VVIDEOVSEL 2 +#define MC13892_REGULATORSETTING1_VAUDIOVSEL 4 +#define MC13892_REGULATORSETTING1_VSDVSEL 6 + +#define MC13892_REGULATORSETTING1_VVIDEOVSEL_M (3<<2) +#define MC13892_REGULATORSETTING1_VAUDIOVSEL_M (3<<4) +#define MC13892_REGULATORSETTING1_VSDVSEL_M (7<<6) + +#define MC13892_REGULATORMODE0 32 +#define MC13892_REGULATORMODE0_VGEN1EN (1<<0) +#define MC13892_REGULATORMODE0_VGEN1STDBY (1<<1) +#define MC13892_REGULATORMODE0_VGEN1MODE (1<<2) +#define MC13892_REGULATORMODE0_VIOHIEN (1<<3) +#define MC13892_REGULATORMODE0_VIOHISTDBY (1<<4) +#define MC13892_REGULATORMODE0_VIOHIMODE (1<<5) +#define MC13892_REGULATORMODE0_VDIGEN (1<<9) +#define MC13892_REGULATORMODE0_VDIGSTDBY (1<<10) +#define MC13892_REGULATORMODE0_VDIGMODE (1<<11) +#define MC13892_REGULATORMODE0_VGEN2EN (1<<12) +#define MC13892_REGULATORMODE0_VGEN2STDBY (1<<13) +#define MC13892_REGULATORMODE0_VGEN2MODE (1<<14) +#define MC13892_REGULATORMODE0_VPLLEN (1<<15) +#define MC13892_REGULATORMODE0_VPLLSTDBY (1<<16) +#define MC13892_REGULATORMODE0_VPLLMODE (1<<17) +#define MC13892_REGULATORMODE0_VUSB2EN (1<<18) +#define MC13892_REGULATORMODE0_VUSB2STDBY (1<<19) +#define MC13892_REGULATORMODE0_VUSB2MODE (1<<20) + +#define MC13892_REGULATORMODE1 33 +#define MC13892_REGULATORMODE1_VGEN3EN (1<<0) +#define MC13892_REGULATORMODE1_VGEN3STDBY (1<<1) +#define MC13892_REGULATORMODE1_VGEN3MODE (1<<2) +#define MC13892_REGULATORMODE1_VCAMEN (1<<6) +#define MC13892_REGULATORMODE1_VCAMSTDBY (1<<7) +#define MC13892_REGULATORMODE1_VCAMMODE (1<<8) +#define MC13892_REGULATORMODE1_VCAMCONFIGEN (1<<9) +#define MC13892_REGULATORMODE1_VVIDEOEN (1<<12) +#define MC13892_REGULATORMODE1_VVIDEOSTDBY (1<<13) +#define MC13892_REGULATORMODE1_VVIDEOMODE (1<<14) +#define MC13892_REGULATORMODE1_VAUDIOEN (1<<15) +#define MC13892_REGULATORMODE1_VAUDIOSTDBY (1<<16) +#define MC13892_REGULATORMODE1_VAUDIOMODE (1<<17) +#define MC13892_REGULATORMODE1_VSDEN (1<<18) +#define MC13892_REGULATORMODE1_VSDSTDBY (1<<19) +#define MC13892_REGULATORMODE1_VSDMODE (1<<20) + +#define MC13892_POWERMISC 34 +#define MC13892_POWERMISC_GPO1EN (1<<6) +#define MC13892_POWERMISC_GPO2EN (1<<8) +#define MC13892_POWERMISC_GPO3EN (1<<10) +#define MC13892_POWERMISC_GPO4EN (1<<12) +#define MC13892_POWERMISC_PWGT1SPIEN (1<<15) +#define MC13892_POWERMISC_PWGT2SPIEN (1<<16) +#define MC13892_POWERMISC_GPO4ADINEN (1<<21) + +#define MC13892_POWERMISC_PWGTSPI_M (3 << 15) + +#define MC13892_USB1 50 +#define MC13892_USB1_VUSBEN (1<<3) + +static const unsigned int mc13892_vcoincell[] = { + 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, + 3200000, 3300000, +}; + +static const unsigned int mc13892_sw1[] = { + 600000, 625000, 650000, 675000, 700000, 725000, + 750000, 775000, 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, 1000000, 1025000, + 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, + 1350000, 1375000 +}; + +/* + * Note: this table is used to derive SWxVSEL by index into + * the array. Offset the values by the index of 1100000uV + * to get the actual register value for that voltage selector + * if the HI bit is to be set as well. + */ +#define MC13892_SWxHI_SEL_OFFSET 20 + +static const unsigned int mc13892_sw[] = { + 600000, 625000, 650000, 675000, 700000, 725000, + 750000, 775000, 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, 1000000, 1025000, + 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, 1300000, 1325000, + 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, 1600000, 1625000, + 1650000, 1675000, 1700000, 1725000, 1750000, 1775000, + 1800000, 1825000, 1850000, 1875000 +}; + +static const unsigned int mc13892_swbst[] = { + 5000000, +}; + +static const unsigned int mc13892_viohi[] = { + 2775000, +}; + +static const unsigned int mc13892_vpll[] = { + 1050000, 1250000, 1650000, 1800000, +}; + +static const unsigned int mc13892_vdig[] = { + 1050000, 1250000, 1650000, 1800000, +}; + +static const unsigned int mc13892_vsd[] = { + 1800000, 2000000, 2600000, 2700000, + 2800000, 2900000, 3000000, 3150000, +}; + +static const unsigned int mc13892_vusb2[] = { + 2400000, 2600000, 2700000, 2775000, +}; + +static const unsigned int mc13892_vvideo[] = { + 2700000, 2775000, 2500000, 2600000, +}; + +static const unsigned int mc13892_vaudio[] = { + 2300000, 2500000, 2775000, 3000000, +}; + +static const unsigned int mc13892_vcam[] = { + 2500000, 2600000, 2750000, 3000000, +}; + +static const unsigned int mc13892_vgen1[] = { + 1200000, 1500000, 2775000, 3150000, +}; + +static const unsigned int mc13892_vgen2[] = { + 1200000, 1500000, 1600000, 1800000, + 2700000, 2800000, 3000000, 3150000, +}; + +static const unsigned int mc13892_vgen3[] = { + 1800000, 2900000, +}; + +static const unsigned int mc13892_vusb[] = { + 3300000, +}; + +static const unsigned int mc13892_gpo[] = { + 2750000, +}; + +static const unsigned int mc13892_pwgtdrv[] = { + 5000000, +}; + +static struct regulator_ops mc13892_gpo_regulator_ops; +static struct regulator_ops mc13892_sw_regulator_ops; + + +#define MC13892_FIXED_DEFINE(name, reg, voltages) \ + MC13xxx_FIXED_DEFINE(MC13892_, name, reg, voltages, \ + mc13xxx_fixed_regulator_ops) + +#define MC13892_GPO_DEFINE(name, reg, voltages) \ + MC13xxx_GPO_DEFINE(MC13892_, name, reg, voltages, \ + mc13892_gpo_regulator_ops) + +#define MC13892_SW_DEFINE(name, reg, vsel_reg, voltages) \ + MC13xxx_DEFINE(MC13892_, name, reg, vsel_reg, voltages, \ + mc13892_sw_regulator_ops) + +#define MC13892_DEFINE_REGU(name, reg, vsel_reg, voltages) \ + MC13xxx_DEFINE(MC13892_, name, reg, vsel_reg, voltages, \ + mc13xxx_regulator_ops) + +static struct mc13xxx_regulator mc13892_regulators[] = { + MC13892_DEFINE_REGU(VCOINCELL, POWERCTL0, POWERCTL0, mc13892_vcoincell), + MC13892_SW_DEFINE(SW1, SWITCHERS0, SWITCHERS0, mc13892_sw1), + MC13892_SW_DEFINE(SW2, SWITCHERS1, SWITCHERS1, mc13892_sw), + MC13892_SW_DEFINE(SW3, SWITCHERS2, SWITCHERS2, mc13892_sw), + MC13892_SW_DEFINE(SW4, SWITCHERS3, SWITCHERS3, mc13892_sw), + MC13892_FIXED_DEFINE(SWBST, SWITCHERS5, mc13892_swbst), + MC13892_FIXED_DEFINE(VIOHI, REGULATORMODE0, mc13892_viohi), + MC13892_DEFINE_REGU(VPLL, REGULATORMODE0, REGULATORSETTING0, \ + mc13892_vpll), + MC13892_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, \ + mc13892_vdig), + MC13892_DEFINE_REGU(VSD, REGULATORMODE1, REGULATORSETTING1, \ + mc13892_vsd), + MC13892_DEFINE_REGU(VUSB2, REGULATORMODE0, REGULATORSETTING0, \ + mc13892_vusb2), + MC13892_DEFINE_REGU(VVIDEO, REGULATORMODE1, REGULATORSETTING1, \ + mc13892_vvideo), + MC13892_DEFINE_REGU(VAUDIO, REGULATORMODE1, REGULATORSETTING1, \ + mc13892_vaudio), + MC13892_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, \ + mc13892_vcam), + MC13892_DEFINE_REGU(VGEN1, REGULATORMODE0, REGULATORSETTING0, \ + mc13892_vgen1), + MC13892_DEFINE_REGU(VGEN2, REGULATORMODE0, REGULATORSETTING0, \ + mc13892_vgen2), + MC13892_DEFINE_REGU(VGEN3, REGULATORMODE1, REGULATORSETTING0, \ + mc13892_vgen3), + MC13892_FIXED_DEFINE(VUSB, USB1, mc13892_vusb), + MC13892_GPO_DEFINE(GPO1, POWERMISC, mc13892_gpo), + MC13892_GPO_DEFINE(GPO2, POWERMISC, mc13892_gpo), + MC13892_GPO_DEFINE(GPO3, POWERMISC, mc13892_gpo), + MC13892_GPO_DEFINE(GPO4, POWERMISC, mc13892_gpo), + MC13892_GPO_DEFINE(PWGT1SPI, POWERMISC, mc13892_pwgtdrv), + MC13892_GPO_DEFINE(PWGT2SPI, POWERMISC, mc13892_pwgtdrv), +}; + +static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask, + u32 val) +{ + struct mc13xxx *mc13892 = priv->mc13xxx; + int ret; + u32 valread; + + BUG_ON(val & ~mask); + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(mc13892, MC13892_POWERMISC, &valread); + if (ret) + goto out; + + /* Update the stored state for Power Gates. */ + priv->powermisc_pwgt_state = + (priv->powermisc_pwgt_state & ~mask) | val; + priv->powermisc_pwgt_state &= MC13892_POWERMISC_PWGTSPI_M; + + /* Construct the new register value */ + valread = (valread & ~mask) | val; + /* Overwrite the PWGTxEN with the stored version */ + valread = (valread & ~MC13892_POWERMISC_PWGTSPI_M) | + priv->powermisc_pwgt_state; + + ret = mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread); +out: + mc13xxx_unlock(priv->mc13xxx); + return ret; +} + +static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + u32 en_val = mc13892_regulators[id].enable_bit; + u32 mask = mc13892_regulators[id].enable_bit; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + /* Power Gate enable value is 0 */ + if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI) + en_val = 0; + + if (id == MC13892_GPO4) + mask |= MC13892_POWERMISC_GPO4ADINEN; + + return mc13892_powermisc_rmw(priv, mask, en_val); +} + +static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + u32 dis_val = 0; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + /* Power Gate disable value is 1 */ + if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI) + dis_val = mc13892_regulators[id].enable_bit; + + return mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit, + dis_val); +} + +static int mc13892_gpo_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int val; + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(priv->mc13xxx, mc13892_regulators[id].reg, &val); + mc13xxx_unlock(priv->mc13xxx); + + if (ret) + return ret; + + /* Power Gates state is stored in powermisc_pwgt_state + * where the meaning of bits is negated */ + val = (val & ~MC13892_POWERMISC_PWGTSPI_M) | + (priv->powermisc_pwgt_state ^ MC13892_POWERMISC_PWGTSPI_M); + + return (val & mc13892_regulators[id].enable_bit) != 0; +} + + +static struct regulator_ops mc13892_gpo_regulator_ops = { + .enable = mc13892_gpo_regulator_enable, + .disable = mc13892_gpo_regulator_disable, + .is_enabled = mc13892_gpo_regulator_is_enabled, + .list_voltage = regulator_list_voltage_table, + .set_voltage = mc13xxx_fixed_regulator_set_voltage, +}; + +static int mc13892_sw_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int val, selector; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(priv->mc13xxx, + mc13892_regulators[id].vsel_reg, &val); + mc13xxx_unlock(priv->mc13xxx); + if (ret) + return ret; + + /* + * Figure out if the HI bit is set inside the switcher mode register + * since this means the selector value we return is at a different + * offset into the selector table. + * + * According to the MC13892 documentation note 59 (Table 47) the SW1 + * buck switcher does not support output range programming therefore + * the HI bit must always remain 0. So do not do anything strange if + * our register is MC13892_SWITCHERS0. + */ + + selector = val & mc13892_regulators[id].vsel_mask; + + if ((mc13892_regulators[id].vsel_reg != MC13892_SWITCHERS0) && + (val & MC13892_SWITCHERS0_SWxHI)) { + selector += MC13892_SWxHI_SEL_OFFSET; + } + + dev_dbg(rdev_get_dev(rdev), "%s id: %d val: 0x%08x selector: %d\n", + __func__, id, val, selector); + + return selector; +} + +static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int volt, mask, id = rdev_get_id(rdev); + u32 reg_value; + int ret; + + volt = rdev->desc->volt_table[selector]; + mask = mc13892_regulators[id].vsel_mask; + reg_value = selector; + + /* + * Don't mess with the HI bit or support HI voltage offsets for SW1. + * + * Since the get_voltage_sel callback has given a fudged value for + * the selector offset, we need to back out that offset if HI is + * to be set so we write the correct value to the register. + * + * The HI bit addition and selector offset handling COULD be more + * complicated by shifting and masking off the voltage selector part + * of the register then logical OR it back in, but since the selector + * is at bits 4:0 there is very little point. This makes the whole + * thing more readable and we do far less work. + */ + + if (mc13892_regulators[id].vsel_reg != MC13892_SWITCHERS0) { + mask |= MC13892_SWITCHERS0_SWxHI; + + if (volt > 1375000) { + reg_value -= MC13892_SWxHI_SEL_OFFSET; + reg_value |= MC13892_SWITCHERS0_SWxHI; + } else { + reg_value &= ~MC13892_SWITCHERS0_SWxHI; + } + } + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].vsel_reg, mask, + reg_value); + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static struct regulator_ops mc13892_sw_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = mc13892_sw_regulator_set_voltage_sel, + .get_voltage_sel = mc13892_sw_regulator_get_voltage_sel, +}; + +static int mc13892_vcam_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + unsigned int en_val = 0; + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + + if (mode == REGULATOR_MODE_FAST) + en_val = MC13892_REGULATORMODE1_VCAMCONFIGEN; + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].reg, + MC13892_REGULATORMODE1_VCAMCONFIGEN, en_val); + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static unsigned int mc13892_vcam_get_mode(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int val; + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(priv->mc13xxx, mc13892_regulators[id].reg, &val); + mc13xxx_unlock(priv->mc13xxx); + + if (ret) + return ret; + + if (val & MC13892_REGULATORMODE1_VCAMCONFIGEN) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + + +static int mc13892_regulator_probe(struct platform_device *pdev) +{ + struct mc13xxx_regulator_priv *priv; + struct mc13xxx *mc13892 = dev_get_drvdata(pdev->dev.parent); + struct mc13xxx_regulator_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct mc13xxx_regulator_init_data *mc13xxx_data; + struct regulator_config config = { }; + int i, ret; + int num_regulators = 0; + u32 val; + + num_regulators = mc13xxx_get_num_regulators_dt(pdev); + + if (num_regulators <= 0 && pdata) + num_regulators = pdata->num_regulators; + if (num_regulators <= 0) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv) + + num_regulators * sizeof(priv->regulators[0]), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num_regulators = num_regulators; + priv->mc13xxx_regulators = mc13892_regulators; + priv->mc13xxx = mc13892; + platform_set_drvdata(pdev, priv); + + mc13xxx_lock(mc13892); + ret = mc13xxx_reg_read(mc13892, MC13892_REVISION, &val); + if (ret) + goto err_unlock; + + /* enable switch auto mode (on 2.0A silicon only) */ + if ((val & 0x0000FFFF) == 0x45d0) { + ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS4, + MC13892_SWITCHERS4_SW1MODE_M | + MC13892_SWITCHERS4_SW2MODE_M, + MC13892_SWITCHERS4_SW1MODE_AUTO | + MC13892_SWITCHERS4_SW2MODE_AUTO); + if (ret) + goto err_unlock; + + ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS5, + MC13892_SWITCHERS5_SW3MODE_M | + MC13892_SWITCHERS5_SW4MODE_M, + MC13892_SWITCHERS5_SW3MODE_AUTO | + MC13892_SWITCHERS5_SW4MODE_AUTO); + if (ret) + goto err_unlock; + } + mc13xxx_unlock(mc13892); + + mc13892_regulators[MC13892_VCAM].desc.ops->set_mode + = mc13892_vcam_set_mode; + mc13892_regulators[MC13892_VCAM].desc.ops->get_mode + = mc13892_vcam_get_mode; + + mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators, + ARRAY_SIZE(mc13892_regulators)); + + for (i = 0; i < priv->num_regulators; i++) { + struct regulator_init_data *init_data; + struct regulator_desc *desc; + struct device_node *node = NULL; + int id; + + if (mc13xxx_data) { + id = mc13xxx_data[i].id; + init_data = mc13xxx_data[i].init_data; + node = mc13xxx_data[i].node; + } else { + id = pdata->regulators[i].id; + init_data = pdata->regulators[i].init_data; + } + desc = &mc13892_regulators[id].desc; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = priv; + config.of_node = node; + + priv->regulators[i] = regulator_register(desc, &config); + if (IS_ERR(priv->regulators[i])) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + mc13892_regulators[i].desc.name); + ret = PTR_ERR(priv->regulators[i]); + goto err; + } + } + + return 0; +err: + while (--i >= 0) + regulator_unregister(priv->regulators[i]); + return ret; + +err_unlock: + mc13xxx_unlock(mc13892); + return ret; +} + +static int mc13892_regulator_remove(struct platform_device *pdev) +{ + struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); + int i; + + platform_set_drvdata(pdev, NULL); + + for (i = 0; i < priv->num_regulators; i++) + regulator_unregister(priv->regulators[i]); + + return 0; +} + +static struct platform_driver mc13892_regulator_driver = { + .driver = { + .name = "mc13892-regulator", + .owner = THIS_MODULE, + }, + .remove = mc13892_regulator_remove, + .probe = mc13892_regulator_probe, +}; + +static int __init mc13892_regulator_init(void) +{ + return platform_driver_register(&mc13892_regulator_driver); +} +subsys_initcall(mc13892_regulator_init); + +static void __exit mc13892_regulator_exit(void) +{ + platform_driver_unregister(&mc13892_regulator_driver); +} +module_exit(mc13892_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Yong Shen "); +MODULE_DESCRIPTION("Regulator Driver for Freescale MC13892 PMIC"); +MODULE_ALIAS("platform:mc13892-regulator"); diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c new file mode 100644 index 0000000..da48592 --- /dev/null +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -0,0 +1,240 @@ +/* + * Regulator Driver for Freescale MC13xxx PMIC + * + * Copyright 2010 Yong Shen + * + * Based on mc13783 regulator driver : + * Copyright (C) 2008 Sascha Hauer, Pengutronix + * Copyright 2009 Alberto Panizzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Regs infos taken from mc13xxx drivers from freescale and mc13xxx.pdf file + * from freescale + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mc13xxx.h" + +static int mc13xxx_regulator_enable(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int id = rdev_get_id(rdev); + int ret; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg, + mc13xxx_regulators[id].enable_bit, + mc13xxx_regulators[id].enable_bit); + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static int mc13xxx_regulator_disable(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int id = rdev_get_id(rdev); + int ret; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg, + mc13xxx_regulators[id].enable_bit, 0); + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static int mc13xxx_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int ret, id = rdev_get_id(rdev); + unsigned int val; + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(priv->mc13xxx, mc13xxx_regulators[id].reg, &val); + mc13xxx_unlock(priv->mc13xxx); + + if (ret) + return ret; + + return (val & mc13xxx_regulators[id].enable_bit) != 0; +} + +static int mc13xxx_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int id = rdev_get_id(rdev); + int ret; + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].vsel_reg, + mc13xxx_regulators[id].vsel_mask, + selector << mc13xxx_regulators[id].vsel_shift); + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static int mc13xxx_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); + struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; + int ret, id = rdev_get_id(rdev); + unsigned int val; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + mc13xxx_lock(priv->mc13xxx); + ret = mc13xxx_reg_read(priv->mc13xxx, + mc13xxx_regulators[id].vsel_reg, &val); + mc13xxx_unlock(priv->mc13xxx); + + if (ret) + return ret; + + val = (val & mc13xxx_regulators[id].vsel_mask) + >> mc13xxx_regulators[id].vsel_shift; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val); + + BUG_ON(val >= mc13xxx_regulators[id].desc.n_voltages); + + return rdev->desc->volt_table[val]; +} + +struct regulator_ops mc13xxx_regulator_ops = { + .enable = mc13xxx_regulator_enable, + .disable = mc13xxx_regulator_disable, + .is_enabled = mc13xxx_regulator_is_enabled, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = mc13xxx_regulator_set_voltage_sel, + .get_voltage = mc13xxx_regulator_get_voltage, +}; +EXPORT_SYMBOL_GPL(mc13xxx_regulator_ops); + +int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + int id = rdev_get_id(rdev); + + dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n", + __func__, id, min_uV, max_uV); + + if (min_uV <= rdev->desc->volt_table[0] && + rdev->desc->volt_table[0] <= max_uV) { + *selector = 0; + return 0; + } else { + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage); + +struct regulator_ops mc13xxx_fixed_regulator_ops = { + .enable = mc13xxx_regulator_enable, + .disable = mc13xxx_regulator_disable, + .is_enabled = mc13xxx_regulator_is_enabled, + .list_voltage = regulator_list_voltage_table, + .set_voltage = mc13xxx_fixed_regulator_set_voltage, +}; +EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops); + +#ifdef CONFIG_OF +int mc13xxx_get_num_regulators_dt(struct platform_device *pdev) +{ + struct device_node *parent; + int num; + + of_node_get(pdev->dev.parent->of_node); + parent = of_find_node_by_name(pdev->dev.parent->of_node, "regulators"); + if (!parent) + return -ENODEV; + + num = of_get_child_count(parent); + of_node_put(parent); + return num; +} +EXPORT_SYMBOL_GPL(mc13xxx_get_num_regulators_dt); + +struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( + struct platform_device *pdev, struct mc13xxx_regulator *regulators, + int num_regulators) +{ + struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); + struct mc13xxx_regulator_init_data *data, *p; + struct device_node *parent, *child; + int i, parsed = 0; + + of_node_get(pdev->dev.parent->of_node); + parent = of_find_node_by_name(pdev->dev.parent->of_node, "regulators"); + if (!parent) + return NULL; + + data = devm_kzalloc(&pdev->dev, sizeof(*data) * priv->num_regulators, + GFP_KERNEL); + if (!data) { + of_node_put(parent); + return NULL; + } + + p = data; + + for_each_child_of_node(parent, child) { + int found = 0; + + for (i = 0; i < num_regulators; i++) { + if (!regulators[i].desc.name) + continue; + if (!of_node_cmp(child->name, + regulators[i].desc.name)) { + p->id = i; + p->init_data = of_get_regulator_init_data( + &pdev->dev, child); + p->node = child; + p++; + + parsed++; + found = 1; + break; + } + } + + if (!found) + dev_warn(&pdev->dev, + "Unknown regulator: %s\n", child->name); + } + of_node_put(parent); + + priv->num_regulators = parsed; + + return data; +} +EXPORT_SYMBOL_GPL(mc13xxx_parse_regulators_dt); +#endif + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Yong Shen "); +MODULE_DESCRIPTION("Regulator Driver for Freescale MC13xxx PMIC"); +MODULE_ALIAS("mc13xxx-regulator-core"); diff --git a/drivers/regulator/mc13xxx.h b/drivers/regulator/mc13xxx.h new file mode 100644 index 0000000..06c8903 --- /dev/null +++ b/drivers/regulator/mc13xxx.h @@ -0,0 +1,113 @@ +/* + * mc13xxx.h - regulators for the Freescale mc13xxx PMIC + * + * Copyright (C) 2010 Yong Shen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_REGULATOR_MC13XXX_H +#define __LINUX_REGULATOR_MC13XXX_H + +#include + +struct mc13xxx_regulator { + struct regulator_desc desc; + int reg; + int enable_bit; + int vsel_reg; + int vsel_shift; + int vsel_mask; + int hi_bit; +}; + +struct mc13xxx_regulator_priv { + struct mc13xxx *mc13xxx; + u32 powermisc_pwgt_state; + struct mc13xxx_regulator *mc13xxx_regulators; + int num_regulators; + struct regulator_dev *regulators[]; +}; + +extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector); + +#ifdef CONFIG_OF +extern int mc13xxx_get_num_regulators_dt(struct platform_device *pdev); +extern struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( + struct platform_device *pdev, struct mc13xxx_regulator *regulators, + int num_regulators); +#else +static inline int mc13xxx_get_num_regulators_dt(struct platform_device *pdev) +{ + return -ENODEV; +} + +static inline struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( + struct platform_device *pdev, struct mc13xxx_regulator *regulators, + int num_regulators) +{ + return NULL; +} +#endif + +extern struct regulator_ops mc13xxx_regulator_ops; +extern struct regulator_ops mc13xxx_fixed_regulator_ops; + +#define MC13xxx_DEFINE(prefix, _name, _reg, _vsel_reg, _voltages, _ops) \ + [prefix ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(_voltages), \ + .volt_table = _voltages, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = prefix ## _name, \ + .owner = THIS_MODULE, \ + }, \ + .reg = prefix ## _reg, \ + .enable_bit = prefix ## _reg ## _ ## _name ## EN, \ + .vsel_reg = prefix ## _vsel_reg, \ + .vsel_shift = prefix ## _vsel_reg ## _ ## _name ## VSEL,\ + .vsel_mask = prefix ## _vsel_reg ## _ ## _name ## VSEL_M,\ + } + +#define MC13xxx_FIXED_DEFINE(prefix, _name, _reg, _voltages, _ops) \ + [prefix ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(_voltages), \ + .volt_table = _voltages, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = prefix ## _name, \ + .owner = THIS_MODULE, \ + }, \ + .reg = prefix ## _reg, \ + .enable_bit = prefix ## _reg ## _ ## _name ## EN, \ + } + +#define MC13xxx_GPO_DEFINE(prefix, _name, _reg, _voltages, _ops) \ + [prefix ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(_voltages), \ + .volt_table = _voltages, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = prefix ## _name, \ + .owner = THIS_MODULE, \ + }, \ + .reg = prefix ## _reg, \ + .enable_bit = prefix ## _reg ## _ ## _name ## EN, \ + } + +#define MC13xxx_DEFINE_SW(_name, _reg, _vsel_reg, _voltages, ops) \ + MC13xxx_DEFINE(SW, _name, _reg, _vsel_reg, _voltages, ops) +#define MC13xxx_DEFINE_REGU(_name, _reg, _vsel_reg, _voltages, ops) \ + MC13xxx_DEFINE(REGU, _name, _reg, _vsel_reg, _voltages, ops) + +#endif diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c new file mode 100644 index 0000000..66ca769 --- /dev/null +++ b/drivers/regulator/of_regulator.c @@ -0,0 +1,158 @@ +/* + * OF helpers for regulator framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Rajendra Nayak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +static void of_get_regulation_constraints(struct device_node *np, + struct regulator_init_data **init_data) +{ + const __be32 *min_uV, *max_uV, *uV_offset; + const __be32 *min_uA, *max_uA, *ramp_delay; + struct regulation_constraints *constraints = &(*init_data)->constraints; + + constraints->name = of_get_property(np, "regulator-name", NULL); + + min_uV = of_get_property(np, "regulator-min-microvolt", NULL); + if (min_uV) + constraints->min_uV = be32_to_cpu(*min_uV); + max_uV = of_get_property(np, "regulator-max-microvolt", NULL); + if (max_uV) + constraints->max_uV = be32_to_cpu(*max_uV); + + /* Voltage change possible? */ + if (constraints->min_uV != constraints->max_uV) + constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; + /* Only one voltage? Then make sure it's set. */ + if (min_uV && max_uV && constraints->min_uV == constraints->max_uV) + constraints->apply_uV = true; + + uV_offset = of_get_property(np, "regulator-microvolt-offset", NULL); + if (uV_offset) + constraints->uV_offset = be32_to_cpu(*uV_offset); + min_uA = of_get_property(np, "regulator-min-microamp", NULL); + if (min_uA) + constraints->min_uA = be32_to_cpu(*min_uA); + max_uA = of_get_property(np, "regulator-max-microamp", NULL); + if (max_uA) + constraints->max_uA = be32_to_cpu(*max_uA); + + /* Current change possible? */ + if (constraints->min_uA != constraints->max_uA) + constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT; + + if (of_find_property(np, "regulator-boot-on", NULL)) + constraints->boot_on = true; + + if (of_find_property(np, "regulator-always-on", NULL)) + constraints->always_on = true; + else /* status change should be possible if not always on. */ + constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + + ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); + if (ramp_delay) + constraints->ramp_delay = be32_to_cpu(*ramp_delay); +} + +/** + * of_get_regulator_init_data - extract regulator_init_data structure info + * @dev: device requesting for regulator_init_data + * + * Populates regulator_init_data structure by extracting data from device + * tree node, returns a pointer to the populated struture or NULL if memory + * alloc fails. + */ +struct regulator_init_data *of_get_regulator_init_data(struct device *dev, + struct device_node *node) +{ + struct regulator_init_data *init_data; + + if (!node) + return NULL; + + init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL); + if (!init_data) + return NULL; /* Out of memory? */ + + of_get_regulation_constraints(node, &init_data); + return init_data; +} +EXPORT_SYMBOL_GPL(of_get_regulator_init_data); + +/** + * of_regulator_match - extract multiple regulator init data from device tree. + * @dev: device requesting the data + * @node: parent device node of the regulators + * @matches: match table for the regulators + * @num_matches: number of entries in match table + * + * This function uses a match table specified by the regulator driver to + * parse regulator init data from the device tree. @node is expected to + * contain a set of child nodes, each providing the init data for one + * regulator. The data parsed from a child node will be matched to a regulator + * based on either the deprecated property regulator-compatible if present, + * or otherwise the child node's name. Note that the match table is modified + * in place. + * + * Returns the number of matches found or a negative error code on failure. + */ +int of_regulator_match(struct device *dev, struct device_node *node, + struct of_regulator_match *matches, + unsigned int num_matches) +{ + unsigned int count = 0; + unsigned int i; + const char *name; + struct device_node *child; + + if (!dev || !node) + return -EINVAL; + + for (i = 0; i < num_matches; i++) { + struct of_regulator_match *match = &matches[i]; + match->init_data = NULL; + match->of_node = NULL; + } + + for_each_child_of_node(node, child) { + name = of_get_property(child, + "regulator-compatible", NULL); + if (!name) + name = child->name; + for (i = 0; i < num_matches; i++) { + struct of_regulator_match *match = &matches[i]; + if (match->of_node) + continue; + + if (strcmp(match->name, name)) + continue; + + match->init_data = + of_get_regulator_init_data(dev, child); + if (!match->init_data) { + dev_err(dev, + "failed to parse DT for regulator %s\n", + child->name); + return -EINVAL; + } + match->of_node = child; + count++; + break; + } + } + + return count; +} +EXPORT_SYMBOL_GPL(of_regulator_match); diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c new file mode 100644 index 0000000..3ae44ac --- /dev/null +++ b/drivers/regulator/palmas-regulator.c @@ -0,0 +1,1083 @@ +/* + * Driver for Regulator part of Palmas PMIC Chips + * + * Copyright 2011-2013 Texas Instruments Inc. + * + * Author: Graeme Gregory + * Author: Ian Lartey + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct regs_info { + char *name; + char *sname; + u8 vsel_addr; + u8 ctrl_addr; + u8 tstep_addr; +}; + +static const struct regs_info palmas_regs_info[] = { + { + .name = "SMPS12", + .sname = "smps1-in", + .vsel_addr = PALMAS_SMPS12_VOLTAGE, + .ctrl_addr = PALMAS_SMPS12_CTRL, + .tstep_addr = PALMAS_SMPS12_TSTEP, + }, + { + .name = "SMPS123", + .sname = "smps1-in", + .vsel_addr = PALMAS_SMPS12_VOLTAGE, + .ctrl_addr = PALMAS_SMPS12_CTRL, + .tstep_addr = PALMAS_SMPS12_TSTEP, + }, + { + .name = "SMPS3", + .sname = "smps3-in", + .vsel_addr = PALMAS_SMPS3_VOLTAGE, + .ctrl_addr = PALMAS_SMPS3_CTRL, + }, + { + .name = "SMPS45", + .sname = "smps4-in", + .vsel_addr = PALMAS_SMPS45_VOLTAGE, + .ctrl_addr = PALMAS_SMPS45_CTRL, + .tstep_addr = PALMAS_SMPS45_TSTEP, + }, + { + .name = "SMPS457", + .sname = "smps4-in", + .vsel_addr = PALMAS_SMPS45_VOLTAGE, + .ctrl_addr = PALMAS_SMPS45_CTRL, + .tstep_addr = PALMAS_SMPS45_TSTEP, + }, + { + .name = "SMPS6", + .sname = "smps6-in", + .vsel_addr = PALMAS_SMPS6_VOLTAGE, + .ctrl_addr = PALMAS_SMPS6_CTRL, + .tstep_addr = PALMAS_SMPS6_TSTEP, + }, + { + .name = "SMPS7", + .sname = "smps7-in", + .vsel_addr = PALMAS_SMPS7_VOLTAGE, + .ctrl_addr = PALMAS_SMPS7_CTRL, + }, + { + .name = "SMPS8", + .sname = "smps8-in", + .vsel_addr = PALMAS_SMPS8_VOLTAGE, + .ctrl_addr = PALMAS_SMPS8_CTRL, + .tstep_addr = PALMAS_SMPS8_TSTEP, + }, + { + .name = "SMPS9", + .sname = "smps9-in", + .vsel_addr = PALMAS_SMPS9_VOLTAGE, + .ctrl_addr = PALMAS_SMPS9_CTRL, + }, + { + .name = "SMPS10", + .sname = "smps10-in", + .ctrl_addr = PALMAS_SMPS10_CTRL, + }, + { + .name = "LDO1", + .sname = "ldo1-in", + .vsel_addr = PALMAS_LDO1_VOLTAGE, + .ctrl_addr = PALMAS_LDO1_CTRL, + }, + { + .name = "LDO2", + .sname = "ldo2-in", + .vsel_addr = PALMAS_LDO2_VOLTAGE, + .ctrl_addr = PALMAS_LDO2_CTRL, + }, + { + .name = "LDO3", + .sname = "ldo3-in", + .vsel_addr = PALMAS_LDO3_VOLTAGE, + .ctrl_addr = PALMAS_LDO3_CTRL, + }, + { + .name = "LDO4", + .sname = "ldo4-in", + .vsel_addr = PALMAS_LDO4_VOLTAGE, + .ctrl_addr = PALMAS_LDO4_CTRL, + }, + { + .name = "LDO5", + .sname = "ldo5-in", + .vsel_addr = PALMAS_LDO5_VOLTAGE, + .ctrl_addr = PALMAS_LDO5_CTRL, + }, + { + .name = "LDO6", + .sname = "ldo6-in", + .vsel_addr = PALMAS_LDO6_VOLTAGE, + .ctrl_addr = PALMAS_LDO6_CTRL, + }, + { + .name = "LDO7", + .sname = "ldo7-in", + .vsel_addr = PALMAS_LDO7_VOLTAGE, + .ctrl_addr = PALMAS_LDO7_CTRL, + }, + { + .name = "LDO8", + .sname = "ldo8-in", + .vsel_addr = PALMAS_LDO8_VOLTAGE, + .ctrl_addr = PALMAS_LDO8_CTRL, + }, + { + .name = "LDO9", + .sname = "ldo9-in", + .vsel_addr = PALMAS_LDO9_VOLTAGE, + .ctrl_addr = PALMAS_LDO9_CTRL, + }, + { + .name = "LDOLN", + .sname = "ldoln-in", + .vsel_addr = PALMAS_LDOLN_VOLTAGE, + .ctrl_addr = PALMAS_LDOLN_CTRL, + }, + { + .name = "LDOUSB", + .sname = "ldousb-in", + .vsel_addr = PALMAS_LDOUSB_VOLTAGE, + .ctrl_addr = PALMAS_LDOUSB_CTRL, + }, + { + .name = "REGEN1", + .ctrl_addr = PALMAS_REGEN1_CTRL, + }, + { + .name = "REGEN2", + .ctrl_addr = PALMAS_REGEN2_CTRL, + }, + { + .name = "REGEN3", + .ctrl_addr = PALMAS_REGEN3_CTRL, + }, + { + .name = "SYSEN1", + .ctrl_addr = PALMAS_SYSEN1_CTRL, + }, + { + .name = "SYSEN2", + .ctrl_addr = PALMAS_SYSEN2_CTRL, + }, +}; + +static unsigned int palmas_smps_ramp_delay[4] = {0, 10000, 5000, 2500}; + +#define SMPS_CTRL_MODE_OFF 0x00 +#define SMPS_CTRL_MODE_ON 0x01 +#define SMPS_CTRL_MODE_ECO 0x02 +#define SMPS_CTRL_MODE_PWM 0x03 + +/* These values are derived from the data sheet. And are the number of steps + * where there is a voltage change, the ranges at beginning and end of register + * max/min values where there are no change are ommitted. + * + * So they are basically (maxV-minV)/stepV + */ +#define PALMAS_SMPS_NUM_VOLTAGES 117 +#define PALMAS_SMPS10_NUM_VOLTAGES 2 +#define PALMAS_LDO_NUM_VOLTAGES 50 + +#define SMPS10_VSEL (1<<3) +#define SMPS10_BOOST_EN (1<<2) +#define SMPS10_BYPASS_EN (1<<1) +#define SMPS10_SWITCH_EN (1<<0) + +#define REGULATOR_SLAVE 0 + +static int palmas_smps_read(struct palmas *palmas, unsigned int reg, + unsigned int *dest) +{ + unsigned int addr; + + addr = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, reg); + + return regmap_read(palmas->regmap[REGULATOR_SLAVE], addr, dest); +} + +static int palmas_smps_write(struct palmas *palmas, unsigned int reg, + unsigned int value) +{ + unsigned int addr; + + addr = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, reg); + + return regmap_write(palmas->regmap[REGULATOR_SLAVE], addr, value); +} + +static int palmas_ldo_read(struct palmas *palmas, unsigned int reg, + unsigned int *dest) +{ + unsigned int addr; + + addr = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, reg); + + return regmap_read(palmas->regmap[REGULATOR_SLAVE], addr, dest); +} + +static int palmas_ldo_write(struct palmas *palmas, unsigned int reg, + unsigned int value) +{ + unsigned int addr; + + addr = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, reg); + + return regmap_write(palmas->regmap[REGULATOR_SLAVE], addr, value); +} + +static int palmas_is_enabled_smps(struct regulator_dev *dev) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + unsigned int reg; + + palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); + + reg &= PALMAS_SMPS12_CTRL_STATUS_MASK; + reg >>= PALMAS_SMPS12_CTRL_STATUS_SHIFT; + + return !!(reg); +} + +static int palmas_enable_smps(struct regulator_dev *dev) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + unsigned int reg; + + palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); + + reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; + if (pmic->current_reg_mode[id]) + reg |= pmic->current_reg_mode[id]; + else + reg |= SMPS_CTRL_MODE_ON; + + palmas_smps_write(pmic->palmas, palmas_regs_info[id].ctrl_addr, reg); + + return 0; +} + +static int palmas_disable_smps(struct regulator_dev *dev) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + unsigned int reg; + + palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); + + reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; + + palmas_smps_write(pmic->palmas, palmas_regs_info[id].ctrl_addr, reg); + + return 0; +} + +static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + unsigned int reg; + bool rail_enable = true; + + palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); + reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; + + if (reg == SMPS_CTRL_MODE_OFF) + rail_enable = false; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + reg |= SMPS_CTRL_MODE_ON; + break; + case REGULATOR_MODE_IDLE: + reg |= SMPS_CTRL_MODE_ECO; + break; + case REGULATOR_MODE_FAST: + reg |= SMPS_CTRL_MODE_PWM; + break; + default: + return -EINVAL; + } + + pmic->current_reg_mode[id] = reg & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; + if (rail_enable) + palmas_smps_write(pmic->palmas, + palmas_regs_info[id].ctrl_addr, reg); + return 0; +} + +static unsigned int palmas_get_mode_smps(struct regulator_dev *dev) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + unsigned int reg; + + reg = pmic->current_reg_mode[id] & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; + + switch (reg) { + case SMPS_CTRL_MODE_ON: + return REGULATOR_MODE_NORMAL; + case SMPS_CTRL_MODE_ECO: + return REGULATOR_MODE_IDLE; + case SMPS_CTRL_MODE_PWM: + return REGULATOR_MODE_FAST; + } + + return 0; +} + +static int palmas_list_voltage_smps(struct regulator_dev *dev, + unsigned selector) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + int mult = 1; + + /* Read the multiplier set in VSEL register to return + * the correct voltage. + */ + if (pmic->range[id]) + mult = 2; + + if (selector == 0) + return 0; + else if (selector < 6) + return 500000 * mult; + else + /* Voltage is linear mapping starting from selector 6, + * volt = (0.49V + ((selector - 5) * 0.01V)) * RANGE + * RANGE is either x1 or x2 + */ + return (490000 + ((selector - 5) * 10000)) * mult; +} + +static int palmas_map_voltage_smps(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + int ret, voltage; + + if (min_uV == 0) + return 0; + + if (pmic->range[id]) { /* RANGE is x2 */ + if (min_uV < 1000000) + min_uV = 1000000; + ret = DIV_ROUND_UP(min_uV - 1000000, 20000) + 6; + } else { /* RANGE is x1 */ + if (min_uV < 500000) + min_uV = 500000; + ret = DIV_ROUND_UP(min_uV - 500000, 10000) + 6; + } + + /* Map back into a voltage to verify we're still in bounds */ + voltage = palmas_list_voltage_smps(rdev, ret); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return ret; +} + +static int palma_smps_set_voltage_smps_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + int old_uv, new_uv; + unsigned int ramp_delay = pmic->ramp_delay[id]; + + if (!ramp_delay) + return 0; + + old_uv = palmas_list_voltage_smps(rdev, old_selector); + if (old_uv < 0) + return old_uv; + + new_uv = palmas_list_voltage_smps(rdev, new_selector); + if (new_uv < 0) + return new_uv; + + return DIV_ROUND_UP(abs(old_uv - new_uv), ramp_delay); +} + +static int palmas_smps_set_ramp_delay(struct regulator_dev *rdev, + int ramp_delay) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int reg = 0; + unsigned int addr = palmas_regs_info[id].tstep_addr; + int ret; + + /* SMPS3 and SMPS7 do not have tstep_addr setting */ + switch (id) { + case PALMAS_REG_SMPS3: + case PALMAS_REG_SMPS7: + return 0; + } + + if (ramp_delay <= 0) + reg = 0; + else if (ramp_delay <= 2500) + reg = 3; + else if (ramp_delay <= 5000) + reg = 2; + else + reg = 1; + + ret = palmas_smps_write(pmic->palmas, addr, reg); + if (ret < 0) { + dev_err(pmic->palmas->dev, "TSTEP write failed: %d\n", ret); + return ret; + } + + pmic->ramp_delay[id] = palmas_smps_ramp_delay[reg]; + return ret; +} + +static struct regulator_ops palmas_ops_smps = { + .is_enabled = palmas_is_enabled_smps, + .enable = palmas_enable_smps, + .disable = palmas_disable_smps, + .set_mode = palmas_set_mode_smps, + .get_mode = palmas_get_mode_smps, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = palmas_list_voltage_smps, + .map_voltage = palmas_map_voltage_smps, + .set_voltage_time_sel = palma_smps_set_voltage_smps_time_sel, + .set_ramp_delay = palmas_smps_set_ramp_delay, +}; + +static struct regulator_ops palmas_ops_smps10 = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static int palmas_is_enabled_ldo(struct regulator_dev *dev) +{ + struct palmas_pmic *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + unsigned int reg; + + palmas_ldo_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); + + reg &= PALMAS_LDO1_CTRL_STATUS; + + return !!(reg); +} + +static struct regulator_ops palmas_ops_ldo = { + .is_enabled = palmas_is_enabled_ldo, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static struct regulator_ops palmas_ops_extreg = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +/* + * setup the hardware based sleep configuration of the SMPS/LDO regulators + * from the platform data. This is different to the software based control + * supported by the regulator framework as it is controlled by toggling + * pins on the PMIC such as PREQ, SYSEN, ... + */ +static int palmas_smps_init(struct palmas *palmas, int id, + struct palmas_reg_init *reg_init) +{ + unsigned int reg; + unsigned int addr; + int ret; + + addr = palmas_regs_info[id].ctrl_addr; + + ret = palmas_smps_read(palmas, addr, ®); + if (ret) + return ret; + + switch (id) { + case PALMAS_REG_SMPS10: + reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK; + if (reg_init->mode_sleep) + reg |= reg_init->mode_sleep << + PALMAS_SMPS10_CTRL_MODE_SLEEP_SHIFT; + break; + default: + if (reg_init->warm_reset) + reg |= PALMAS_SMPS12_CTRL_WR_S; + else + reg &= ~PALMAS_SMPS12_CTRL_WR_S; + + if (reg_init->roof_floor) + reg |= PALMAS_SMPS12_CTRL_ROOF_FLOOR_EN; + else + reg &= ~PALMAS_SMPS12_CTRL_ROOF_FLOOR_EN; + + reg &= ~PALMAS_SMPS12_CTRL_MODE_SLEEP_MASK; + if (reg_init->mode_sleep) + reg |= reg_init->mode_sleep << + PALMAS_SMPS12_CTRL_MODE_SLEEP_SHIFT; + } + + ret = palmas_smps_write(palmas, addr, reg); + if (ret) + return ret; + + if (palmas_regs_info[id].vsel_addr && reg_init->vsel) { + addr = palmas_regs_info[id].vsel_addr; + + reg = reg_init->vsel; + + ret = palmas_smps_write(palmas, addr, reg); + if (ret) + return ret; + } + + + return 0; +} + +static int palmas_ldo_init(struct palmas *palmas, int id, + struct palmas_reg_init *reg_init) +{ + unsigned int reg; + unsigned int addr; + int ret; + + addr = palmas_regs_info[id].ctrl_addr; + + ret = palmas_ldo_read(palmas, addr, ®); + if (ret) + return ret; + + if (reg_init->warm_reset) + reg |= PALMAS_LDO1_CTRL_WR_S; + else + reg &= ~PALMAS_LDO1_CTRL_WR_S; + + if (reg_init->mode_sleep) + reg |= PALMAS_LDO1_CTRL_MODE_SLEEP; + else + reg &= ~PALMAS_LDO1_CTRL_MODE_SLEEP; + + ret = palmas_ldo_write(palmas, addr, reg); + if (ret) + return ret; + + return 0; +} + +static int palmas_extreg_init(struct palmas *palmas, int id, + struct palmas_reg_init *reg_init) +{ + unsigned int addr; + int ret; + unsigned int val = 0; + + addr = palmas_regs_info[id].ctrl_addr; + + if (reg_init->mode_sleep) + val = PALMAS_REGEN1_CTRL_MODE_SLEEP; + + ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE, + addr, PALMAS_REGEN1_CTRL_MODE_SLEEP, val); + if (ret < 0) { + dev_err(palmas->dev, "Resource reg 0x%02x update failed %d\n", + addr, ret); + return ret; + } + return 0; +} + +static void palmas_enable_ldo8_track(struct palmas *palmas) +{ + unsigned int reg; + unsigned int addr; + int ret; + + addr = palmas_regs_info[PALMAS_REG_LDO8].ctrl_addr; + + ret = palmas_ldo_read(palmas, addr, ®); + if (ret) { + dev_err(palmas->dev, "Error in reading ldo8 control reg\n"); + return; + } + + reg |= PALMAS_LDO8_CTRL_LDO_TRACKING_EN; + ret = palmas_ldo_write(palmas, addr, reg); + if (ret < 0) { + dev_err(palmas->dev, "Error in enabling tracking mode\n"); + return; + } + /* + * When SMPS45 is set to off and LDO8 tracking is enabled, the LDO8 + * output is defined by the LDO8_VOLTAGE.VSEL register divided by two, + * and can be set from 0.45 to 1.65 V. + */ + addr = palmas_regs_info[PALMAS_REG_LDO8].vsel_addr; + ret = palmas_ldo_read(palmas, addr, ®); + if (ret) { + dev_err(palmas->dev, "Error in reading ldo8 voltage reg\n"); + return; + } + + reg = (reg << 1) & PALMAS_LDO8_VOLTAGE_VSEL_MASK; + ret = palmas_ldo_write(palmas, addr, reg); + if (ret < 0) + dev_err(palmas->dev, "Error in setting ldo8 voltage reg\n"); + + return; +} + +static struct of_regulator_match palmas_matches[] = { + { .name = "smps12", }, + { .name = "smps123", }, + { .name = "smps3", }, + { .name = "smps45", }, + { .name = "smps457", }, + { .name = "smps6", }, + { .name = "smps7", }, + { .name = "smps8", }, + { .name = "smps9", }, + { .name = "smps10", }, + { .name = "ldo1", }, + { .name = "ldo2", }, + { .name = "ldo3", }, + { .name = "ldo4", }, + { .name = "ldo5", }, + { .name = "ldo6", }, + { .name = "ldo7", }, + { .name = "ldo8", }, + { .name = "ldo9", }, + { .name = "ldoln", }, + { .name = "ldousb", }, + { .name = "regen1", }, + { .name = "regen2", }, + { .name = "regen3", }, + { .name = "sysen1", }, + { .name = "sysen2", }, +}; + +static void palmas_dt_to_pdata(struct device *dev, + struct device_node *node, + struct palmas_pmic_platform_data *pdata) +{ + struct device_node *regulators; + u32 prop; + int idx, ret; + + node = of_node_get(node); + regulators = of_find_node_by_name(node, "regulators"); + if (!regulators) { + dev_info(dev, "regulator node not found\n"); + return; + } + + ret = of_regulator_match(dev, regulators, palmas_matches, + PALMAS_NUM_REGS); + of_node_put(regulators); + if (ret < 0) { + dev_err(dev, "Error parsing regulator init data: %d\n", ret); + return; + } + + for (idx = 0; idx < PALMAS_NUM_REGS; idx++) { + if (!palmas_matches[idx].init_data || + !palmas_matches[idx].of_node) + continue; + + pdata->reg_data[idx] = palmas_matches[idx].init_data; + + pdata->reg_init[idx] = devm_kzalloc(dev, + sizeof(struct palmas_reg_init), GFP_KERNEL); + + pdata->reg_init[idx]->warm_reset = + of_property_read_bool(palmas_matches[idx].of_node, + "ti,warm-reset"); + + pdata->reg_init[idx]->roof_floor = + of_property_read_bool(palmas_matches[idx].of_node, + "ti,roof-floor"); + + ret = of_property_read_u32(palmas_matches[idx].of_node, + "ti,mode-sleep", &prop); + if (!ret) + pdata->reg_init[idx]->mode_sleep = prop; + + ret = of_property_read_bool(palmas_matches[idx].of_node, + "ti,smps-range"); + if (ret) + pdata->reg_init[idx]->vsel = + PALMAS_SMPS12_VOLTAGE_RANGE; + + if (idx == PALMAS_REG_LDO8) + pdata->enable_ldo8_tracking = of_property_read_bool( + palmas_matches[idx].of_node, + "ti,enable-ldo8-tracking"); + } + + pdata->ldo6_vibrator = of_property_read_bool(node, "ti,ldo6-vibrator"); +} + + +static int palmas_regulators_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_pmic_platform_data *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; + struct regulator_dev *rdev; + struct regulator_config config = { }; + struct palmas_pmic *pmic; + struct palmas_reg_init *reg_init; + int id = 0, ret; + unsigned int addr, reg; + + if (node && !pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + + if (!pdata) + return -ENOMEM; + + palmas_dt_to_pdata(&pdev->dev, node, pdata); + } + + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + pmic->dev = &pdev->dev; + pmic->palmas = palmas; + palmas->pmic = pmic; + platform_set_drvdata(pdev, pmic); + + ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, ®); + if (ret) + return ret; + + if (reg & PALMAS_SMPS_CTRL_SMPS12_SMPS123_EN) + pmic->smps123 = 1; + + if (reg & PALMAS_SMPS_CTRL_SMPS45_SMPS457_EN) + pmic->smps457 = 1; + + config.regmap = palmas->regmap[REGULATOR_SLAVE]; + config.dev = &pdev->dev; + config.driver_data = pmic; + + for (id = 0; id < PALMAS_REG_LDO1; id++) { + bool ramp_delay_support = false; + + /* + * Miss out regulators which are not available due + * to slaving configurations. + */ + switch (id) { + case PALMAS_REG_SMPS12: + case PALMAS_REG_SMPS3: + if (pmic->smps123) + continue; + if (id == PALMAS_REG_SMPS12) + ramp_delay_support = true; + break; + case PALMAS_REG_SMPS123: + if (!pmic->smps123) + continue; + ramp_delay_support = true; + break; + case PALMAS_REG_SMPS45: + case PALMAS_REG_SMPS7: + if (pmic->smps457) + continue; + if (id == PALMAS_REG_SMPS45) + ramp_delay_support = true; + break; + case PALMAS_REG_SMPS457: + if (!pmic->smps457) + continue; + ramp_delay_support = true; + break; + } + + if ((id == PALMAS_REG_SMPS6) || (id == PALMAS_REG_SMPS8)) + ramp_delay_support = true; + + if (ramp_delay_support) { + addr = palmas_regs_info[id].tstep_addr; + ret = palmas_smps_read(pmic->palmas, addr, ®); + if (ret < 0) { + dev_err(&pdev->dev, + "reading TSTEP reg failed: %d\n", ret); + goto err_unregister_regulator; + } + pmic->desc[id].ramp_delay = + palmas_smps_ramp_delay[reg & 0x3]; + pmic->ramp_delay[id] = pmic->desc[id].ramp_delay; + } + + /* Initialise sleep/init values from platform data */ + if (pdata && pdata->reg_init[id]) { + reg_init = pdata->reg_init[id]; + ret = palmas_smps_init(palmas, id, reg_init); + if (ret) + goto err_unregister_regulator; + } + + /* Register the regulators */ + pmic->desc[id].name = palmas_regs_info[id].name; + pmic->desc[id].id = id; + + switch (id) { + case PALMAS_REG_SMPS10: + pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES; + pmic->desc[id].ops = &palmas_ops_smps10; + pmic->desc[id].vsel_reg = + PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, + PALMAS_SMPS10_CTRL); + pmic->desc[id].vsel_mask = SMPS10_VSEL; + pmic->desc[id].enable_reg = + PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, + PALMAS_SMPS10_CTRL); + pmic->desc[id].enable_mask = SMPS10_BOOST_EN; + pmic->desc[id].min_uV = 3750000; + pmic->desc[id].uV_step = 1250000; + break; + default: + /* + * Read and store the RANGE bit for later use + * This must be done before regulator is probed, + * otherwise we error in probe with unsupportable + * ranges. Read the current smps mode for later use. + */ + addr = palmas_regs_info[id].vsel_addr; + + ret = palmas_smps_read(pmic->palmas, addr, ®); + if (ret) + goto err_unregister_regulator; + if (reg & PALMAS_SMPS12_VOLTAGE_RANGE) + pmic->range[id] = 1; + + pmic->desc[id].ops = &palmas_ops_smps; + pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES; + pmic->desc[id].vsel_reg = + PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, + palmas_regs_info[id].vsel_addr); + pmic->desc[id].vsel_mask = + PALMAS_SMPS12_VOLTAGE_VSEL_MASK; + + /* Read the smps mode for later use. */ + addr = palmas_regs_info[id].ctrl_addr; + ret = palmas_smps_read(pmic->palmas, addr, ®); + if (ret) + goto err_unregister_regulator; + pmic->current_reg_mode[id] = reg & + PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; + } + + pmic->desc[id].type = REGULATOR_VOLTAGE; + pmic->desc[id].owner = THIS_MODULE; + + if (pdata) + config.init_data = pdata->reg_data[id]; + else + config.init_data = NULL; + + pmic->desc[id].supply_name = palmas_regs_info[id].sname; + config.of_node = palmas_matches[id].of_node; + + rdev = regulator_register(&pmic->desc[id], &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "failed to register %s regulator\n", + pdev->name); + ret = PTR_ERR(rdev); + goto err_unregister_regulator; + } + + /* Save regulator for cleanup */ + pmic->rdev[id] = rdev; + } + + /* Start this loop from the id left from previous loop */ + for (; id < PALMAS_NUM_REGS; id++) { + + /* Miss out regulators which are not available due + * to alternate functions. + */ + + /* Register the regulators */ + pmic->desc[id].name = palmas_regs_info[id].name; + pmic->desc[id].id = id; + pmic->desc[id].type = REGULATOR_VOLTAGE; + pmic->desc[id].owner = THIS_MODULE; + + if (id < PALMAS_REG_REGEN1) { + pmic->desc[id].n_voltages = PALMAS_LDO_NUM_VOLTAGES; + pmic->desc[id].ops = &palmas_ops_ldo; + pmic->desc[id].min_uV = 900000; + pmic->desc[id].uV_step = 50000; + pmic->desc[id].linear_min_sel = 1; + pmic->desc[id].vsel_reg = + PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, + palmas_regs_info[id].vsel_addr); + pmic->desc[id].vsel_mask = + PALMAS_LDO1_VOLTAGE_VSEL_MASK; + pmic->desc[id].enable_reg = + PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, + palmas_regs_info[id].ctrl_addr); + pmic->desc[id].enable_mask = + PALMAS_LDO1_CTRL_MODE_ACTIVE; + + /* Check if LDO8 is in tracking mode or not */ + if (pdata && (id == PALMAS_REG_LDO8) && + pdata->enable_ldo8_tracking) { + palmas_enable_ldo8_track(palmas); + pmic->desc[id].min_uV = 450000; + pmic->desc[id].uV_step = 25000; + } + } else { + pmic->desc[id].n_voltages = 1; + pmic->desc[id].ops = &palmas_ops_extreg; + pmic->desc[id].enable_reg = + PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE, + palmas_regs_info[id].ctrl_addr); + pmic->desc[id].enable_mask = + PALMAS_REGEN1_CTRL_MODE_ACTIVE; + } + + if (pdata) + config.init_data = pdata->reg_data[id]; + else + config.init_data = NULL; + + pmic->desc[id].supply_name = palmas_regs_info[id].sname; + config.of_node = palmas_matches[id].of_node; + + rdev = regulator_register(&pmic->desc[id], &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "failed to register %s regulator\n", + pdev->name); + ret = PTR_ERR(rdev); + goto err_unregister_regulator; + } + + /* Save regulator for cleanup */ + pmic->rdev[id] = rdev; + + /* Initialise sleep/init values from platform data */ + if (pdata) { + reg_init = pdata->reg_init[id]; + if (reg_init) { + if (id < PALMAS_REG_REGEN1) + ret = palmas_ldo_init(palmas, + id, reg_init); + else + ret = palmas_extreg_init(palmas, + id, reg_init); + if (ret) { + regulator_unregister(pmic->rdev[id]); + goto err_unregister_regulator; + } + } + } + } + + + return 0; + +err_unregister_regulator: + while (--id >= 0) + regulator_unregister(pmic->rdev[id]); + return ret; +} + +static int palmas_regulators_remove(struct platform_device *pdev) +{ + struct palmas_pmic *pmic = platform_get_drvdata(pdev); + int id; + + for (id = 0; id < PALMAS_NUM_REGS; id++) + regulator_unregister(pmic->rdev[id]); + return 0; +} + +static struct of_device_id of_palmas_match_tbl[] = { + { .compatible = "ti,palmas-pmic", }, + { .compatible = "ti,twl6035-pmic", }, + { .compatible = "ti,twl6036-pmic", }, + { .compatible = "ti,twl6037-pmic", }, + { .compatible = "ti,tps65913-pmic", }, + { .compatible = "ti,tps65914-pmic", }, + { .compatible = "ti,tps80036-pmic", }, + { /* end */ } +}; + +static struct platform_driver palmas_driver = { + .driver = { + .name = "palmas-pmic", + .of_match_table = of_palmas_match_tbl, + .owner = THIS_MODULE, + }, + .probe = palmas_regulators_probe, + .remove = palmas_regulators_remove, +}; + +static int __init palmas_init(void) +{ + return platform_driver_register(&palmas_driver); +} +subsys_initcall(palmas_init); + +static void __exit palmas_exit(void) +{ + platform_driver_unregister(&palmas_driver); +} +module_exit(palmas_exit); + +MODULE_AUTHOR("Graeme Gregory "); +MODULE_DESCRIPTION("Palmas voltage regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:palmas-pmic"); +MODULE_DEVICE_TABLE(of, of_palmas_match_tbl); diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c new file mode 100644 index 0000000..4899342 --- /dev/null +++ b/drivers/regulator/pcap-regulator.c @@ -0,0 +1,292 @@ +/* + * PCAP2 Regulator Driver + * + * Copyright (c) 2009 Daniel Ribeiro + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const unsigned int V1_table[] = { + 2775000, 1275000, 1600000, 1725000, 1825000, 1925000, 2075000, 2275000, +}; + +static const unsigned int V2_table[] = { + 2500000, 2775000, +}; + +static const unsigned int V3_table[] = { + 1075000, 1275000, 1550000, 1725000, 1876000, 1950000, 2075000, 2275000, +}; + +static const unsigned int V4_table[] = { + 1275000, 1550000, 1725000, 1875000, 1950000, 2075000, 2275000, 2775000, +}; + +static const unsigned int V5_table[] = { + 1875000, 2275000, 2475000, 2775000, +}; + +static const unsigned int V6_table[] = { + 2475000, 2775000, +}; + +static const unsigned int V7_table[] = { + 1875000, 2775000, +}; + +#define V8_table V4_table + +static const unsigned int V9_table[] = { + 1575000, 1875000, 2475000, 2775000, +}; + +static const unsigned int V10_table[] = { + 5000000, +}; + +static const unsigned int VAUX1_table[] = { + 1875000, 2475000, 2775000, 3000000, +}; + +#define VAUX2_table VAUX1_table + +static const unsigned int VAUX3_table[] = { + 1200000, 1200000, 1200000, 1200000, 1400000, 1600000, 1800000, 2000000, + 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, +}; + +static const unsigned int VAUX4_table[] = { + 1800000, 1800000, 3000000, 5000000, +}; + +static const unsigned int VSIM_table[] = { + 1875000, 3000000, +}; + +static const unsigned int VSIM2_table[] = { + 1875000, +}; + +static const unsigned int VVIB_table[] = { + 1300000, 1800000, 2000000, 3000000, +}; + +static const unsigned int SW1_table[] = { + 900000, 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, + 1300000, 1350000, 1400000, 1450000, 1500000, 1600000, 1875000, 2250000, +}; + +#define SW2_table SW1_table + +static const unsigned int SW3_table[] = { + 4000000, 4500000, 5000000, 5500000, +}; + +struct pcap_regulator { + const u8 reg; + const u8 en; + const u8 index; + const u8 stby; + const u8 lowpwr; +}; + +#define NA 0xff + +#define VREG_INFO(_vreg, _reg, _en, _index, _stby, _lowpwr) \ + [_vreg] = { \ + .reg = _reg, \ + .en = _en, \ + .index = _index, \ + .stby = _stby, \ + .lowpwr = _lowpwr, \ + } + +static struct pcap_regulator vreg_table[] = { + VREG_INFO(V1, PCAP_REG_VREG1, 1, 2, 18, 0), + VREG_INFO(V2, PCAP_REG_VREG1, 5, 6, 19, 22), + VREG_INFO(V3, PCAP_REG_VREG1, 7, 8, 20, 23), + VREG_INFO(V4, PCAP_REG_VREG1, 11, 12, 21, 24), + /* V5 STBY and LOWPWR are on PCAP_REG_VREG2 */ + VREG_INFO(V5, PCAP_REG_VREG1, 15, 16, 12, 19), + + VREG_INFO(V6, PCAP_REG_VREG2, 1, 2, 14, 20), + VREG_INFO(V7, PCAP_REG_VREG2, 3, 4, 15, 21), + VREG_INFO(V8, PCAP_REG_VREG2, 5, 6, 16, 22), + VREG_INFO(V9, PCAP_REG_VREG2, 9, 10, 17, 23), + VREG_INFO(V10, PCAP_REG_VREG2, 10, NA, 18, 24), + + VREG_INFO(VAUX1, PCAP_REG_AUXVREG, 1, 2, 22, 23), + /* VAUX2 ... VSIM2 STBY and LOWPWR are on PCAP_REG_LOWPWR */ + VREG_INFO(VAUX2, PCAP_REG_AUXVREG, 4, 5, 0, 1), + VREG_INFO(VAUX3, PCAP_REG_AUXVREG, 7, 8, 2, 3), + VREG_INFO(VAUX4, PCAP_REG_AUXVREG, 12, 13, 4, 5), + VREG_INFO(VSIM, PCAP_REG_AUXVREG, 17, 18, NA, 6), + VREG_INFO(VSIM2, PCAP_REG_AUXVREG, 16, NA, NA, 7), + VREG_INFO(VVIB, PCAP_REG_AUXVREG, 19, 20, NA, NA), + + VREG_INFO(SW1, PCAP_REG_SWCTRL, 1, 2, NA, NA), + VREG_INFO(SW2, PCAP_REG_SWCTRL, 6, 7, NA, NA), + /* SW3 STBY is on PCAP_REG_AUXVREG */ + VREG_INFO(SW3, PCAP_REG_SWCTRL, 11, 12, 24, NA), + + /* SWxS used to control SWx voltage on standby */ +/* VREG_INFO(SW1S, PCAP_REG_LOWPWR, NA, 12, NA, NA), + VREG_INFO(SW2S, PCAP_REG_LOWPWR, NA, 20, NA, NA), */ +}; + +static int pcap_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; + void *pcap = rdev_get_drvdata(rdev); + + /* the regulator doesn't support voltage switching */ + if (rdev->desc->n_voltages == 1) + return -EINVAL; + + return ezx_pcap_set_bits(pcap, vreg->reg, + (rdev->desc->n_voltages - 1) << vreg->index, + selector << vreg->index); +} + +static int pcap_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; + void *pcap = rdev_get_drvdata(rdev); + u32 tmp; + + if (rdev->desc->n_voltages == 1) + return 0; + + ezx_pcap_read(pcap, vreg->reg, &tmp); + tmp = ((tmp >> vreg->index) & (rdev->desc->n_voltages - 1)); + return tmp; +} + +static int pcap_regulator_enable(struct regulator_dev *rdev) +{ + struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; + void *pcap = rdev_get_drvdata(rdev); + + if (vreg->en == NA) + return -EINVAL; + + return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 1 << vreg->en); +} + +static int pcap_regulator_disable(struct regulator_dev *rdev) +{ + struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; + void *pcap = rdev_get_drvdata(rdev); + + if (vreg->en == NA) + return -EINVAL; + + return ezx_pcap_set_bits(pcap, vreg->reg, 1 << vreg->en, 0); +} + +static int pcap_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; + void *pcap = rdev_get_drvdata(rdev); + u32 tmp; + + if (vreg->en == NA) + return -EINVAL; + + ezx_pcap_read(pcap, vreg->reg, &tmp); + return (tmp >> vreg->en) & 1; +} + +static struct regulator_ops pcap_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = pcap_regulator_set_voltage_sel, + .get_voltage_sel = pcap_regulator_get_voltage_sel, + .enable = pcap_regulator_enable, + .disable = pcap_regulator_disable, + .is_enabled = pcap_regulator_is_enabled, +}; + +#define VREG(_vreg) \ + [_vreg] = { \ + .name = #_vreg, \ + .id = _vreg, \ + .n_voltages = ARRAY_SIZE(_vreg##_table), \ + .volt_table = _vreg##_table, \ + .ops = &pcap_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static const struct regulator_desc pcap_regulators[] = { + VREG(V1), VREG(V2), VREG(V3), VREG(V4), VREG(V5), VREG(V6), VREG(V7), + VREG(V8), VREG(V9), VREG(V10), VREG(VAUX1), VREG(VAUX2), VREG(VAUX3), + VREG(VAUX4), VREG(VSIM), VREG(VSIM2), VREG(VVIB), VREG(SW1), VREG(SW2), +}; + +static int pcap_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + void *pcap = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = pcap; + + rdev = regulator_register(&pcap_regulators[pdev->id], &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int pcap_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver pcap_regulator_driver = { + .driver = { + .name = "pcap-regulator", + .owner = THIS_MODULE, + }, + .probe = pcap_regulator_probe, + .remove = pcap_regulator_remove, +}; + +static int __init pcap_regulator_init(void) +{ + return platform_driver_register(&pcap_regulator_driver); +} + +static void __exit pcap_regulator_exit(void) +{ + platform_driver_unregister(&pcap_regulator_driver); +} + +subsys_initcall(pcap_regulator_init); +module_exit(pcap_regulator_exit); + +MODULE_AUTHOR("Daniel Ribeiro "); +MODULE_DESCRIPTION("PCAP2 Regulator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c new file mode 100644 index 0000000..534075e --- /dev/null +++ b/drivers/regulator/pcf50633-regulator.c @@ -0,0 +1,138 @@ +/* NXP PCF50633 PMIC Driver + * + * (C) 2006-2008 by Openmoko, Inc. + * Author: Balaji Rao + * All rights reserved. + * + * Broken down from monstrous PCF50633 driver mainly by + * Harald Welte and Andy Green and Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PCF50633_REGULATOR(_name, _id, _min_uV, _uV_step, _min_sel, _n) \ + { \ + .name = _name, \ + .id = PCF50633_REGULATOR_##_id, \ + .ops = &pcf50633_regulator_ops, \ + .n_voltages = _n, \ + .min_uV = _min_uV, \ + .uV_step = _uV_step, \ + .linear_min_sel = _min_sel, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = PCF50633_REG_##_id##OUT, \ + .vsel_mask = 0xff, \ + .enable_reg = PCF50633_REG_##_id##OUT + 1, \ + .enable_mask = PCF50633_REGULATOR_ON, \ + } + +static struct regulator_ops pcf50633_regulator_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc regulators[] = { + [PCF50633_REGULATOR_AUTO] = + PCF50633_REGULATOR("auto", AUTO, 1800000, 25000, 0x2f, 128), + [PCF50633_REGULATOR_DOWN1] = + PCF50633_REGULATOR("down1", DOWN1, 625000, 25000, 0, 96), + [PCF50633_REGULATOR_DOWN2] = + PCF50633_REGULATOR("down2", DOWN2, 625000, 25000, 0, 96), + [PCF50633_REGULATOR_LDO1] = + PCF50633_REGULATOR("ldo1", LDO1, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_LDO2] = + PCF50633_REGULATOR("ldo2", LDO2, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_LDO3] = + PCF50633_REGULATOR("ldo3", LDO3, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_LDO4] = + PCF50633_REGULATOR("ldo4", LDO4, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_LDO5] = + PCF50633_REGULATOR("ldo5", LDO5, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_LDO6] = + PCF50633_REGULATOR("ldo6", LDO6, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_HCLDO] = + PCF50633_REGULATOR("hcldo", HCLDO, 900000, 100000, 0, 28), + [PCF50633_REGULATOR_MEMLDO] = + PCF50633_REGULATOR("memldo", MEMLDO, 900000, 100000, 0, 28), +}; + +static int pcf50633_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + struct pcf50633 *pcf; + struct regulator_config config = { }; + + /* Already set by core driver */ + pcf = dev_to_pcf50633(pdev->dev.parent); + + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = pcf; + config.regmap = pcf->regmap; + + rdev = regulator_register(®ulators[pdev->id], &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + platform_set_drvdata(pdev, rdev); + + if (pcf->pdata->regulator_registered) + pcf->pdata->regulator_registered(pcf, pdev->id); + + return 0; +} + +static int pcf50633_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver pcf50633_regulator_driver = { + .driver = { + .name = "pcf50633-regltr", + }, + .probe = pcf50633_regulator_probe, + .remove = pcf50633_regulator_remove, +}; + +static int __init pcf50633_regulator_init(void) +{ + return platform_driver_register(&pcf50633_regulator_driver); +} +subsys_initcall(pcf50633_regulator_init); + +static void __exit pcf50633_regulator_exit(void) +{ + platform_driver_unregister(&pcf50633_regulator_driver); +} +module_exit(pcf50633_regulator_exit); + +MODULE_AUTHOR("Balaji Rao "); +MODULE_DESCRIPTION("PCF50633 regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcf50633-regulator"); diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c new file mode 100644 index 0000000..5885b45 --- /dev/null +++ b/drivers/regulator/rc5t583-regulator.c @@ -0,0 +1,229 @@ +/* + * Regulator driver for RICOH RC5T583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan + * + * based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rc5t583_regulator_info { + int deepsleep_id; + + /* Regulator register address.*/ + uint8_t reg_disc_reg; + uint8_t disc_bit; + uint8_t deepsleep_reg; + + /* Regulator specific turn-on delay and voltage settling time*/ + int enable_uv_per_us; + + /* Used by regulator core */ + struct regulator_desc desc; +}; + +struct rc5t583_regulator { + struct rc5t583_regulator_info *reg_info; + struct regulator_dev *rdev; +}; + +static int rc5t583_regulator_enable_time(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int vsel = regulator_get_voltage_sel_regmap(rdev); + int curr_uV = regulator_list_voltage_linear(rdev, vsel); + + return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us); +} + +static struct regulator_ops rc5t583_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .enable_time = rc5t583_regulator_enable_time, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, \ + _vout_mask, _min_mv, _max_mv, _step_uV, _enable_mv) \ +{ \ + .reg_disc_reg = RC5T583_REG_##_disc_reg, \ + .disc_bit = _disc_bit, \ + .deepsleep_reg = RC5T583_REG_##_id##DAC_DS, \ + .enable_uv_per_us = _enable_mv * 1000, \ + .deepsleep_id = RC5T583_DS_##_id, \ + .desc = { \ + .name = "rc5t583-regulator-"#_id, \ + .id = RC5T583_REGULATOR_##_id, \ + .n_voltages = (_max_mv - _min_mv) * 1000 / _step_uV + 1, \ + .ops = &rc5t583_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = RC5T583_REG_##_id##DAC, \ + .vsel_mask = _vout_mask, \ + .enable_reg = RC5T583_REG_##_en_reg, \ + .enable_mask = BIT(_en_bit), \ + .min_uV = _min_mv * 1000, \ + .uV_step = _step_uV, \ + .ramp_delay = 40 * 1000, \ + }, \ +} + +static struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = { + RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, 0x7F, 700, 1500, 12500, 4), + RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, 0x7F, 700, 1500, 12500, 14), + RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, 0x7F, 900, 2400, 12500, 14), + RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, 0x7F, 900, 2400, 12500, 14), + RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, 0x7F, 900, 3400, 25000, 160), + RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, 0x7F, 900, 3400, 25000, 160), + RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, 0x7F, 900, 3400, 25000, 160), + RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, 0x7F, 900, 3400, 25000, 160), + RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, 0x3F, 750, 1500, 12500, 133), + RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, 0x7F, 900, 3400, 25000, 267), + RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, 0x7F, 900, 3400, 25000, 133), + RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, 0x7F, 900, 3400, 25000, 233), + RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, 0x7F, 900, 3400, 25000, 233), + RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, 0x7F, 900, 3400, 25000, 133), +}; + +static int rc5t583_regulator_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct regulator_init_data *reg_data; + struct regulator_config config = { }; + struct rc5t583_regulator *reg = NULL; + struct rc5t583_regulator *regs; + struct regulator_dev *rdev; + struct rc5t583_regulator_info *ri; + int ret; + int id; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting...\n"); + return -ENODEV; + } + + regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX * + sizeof(struct rc5t583_regulator), GFP_KERNEL); + if (!regs) { + dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + return -ENOMEM; + } + + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { + reg_data = pdata->reg_init_data[id]; + + /* No need to register if there is no regulator data */ + if (!reg_data) + continue; + + reg = ®s[id]; + ri = &rc5t583_reg_info[id]; + reg->reg_info = ri; + + if (ri->deepsleep_id == RC5T583_DS_NONE) + goto skip_ext_pwr_config; + + ret = rc5t583_ext_power_req_config(rc5t583->dev, + ri->deepsleep_id, + pdata->regulator_ext_pwr_control[id], + pdata->regulator_deepsleep_slot[id]); + /* + * Configuring external control is not a major issue, + * just give warning. + */ + if (ret < 0) + dev_warn(&pdev->dev, + "Failed to configure ext control %d\n", id); + +skip_ext_pwr_config: + config.dev = &pdev->dev; + config.init_data = reg_data; + config.driver_data = reg; + config.regmap = rc5t583->regmap; + + rdev = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + ret = PTR_ERR(rdev); + goto clean_exit; + } + reg->rdev = rdev; + } + platform_set_drvdata(pdev, regs); + return 0; + +clean_exit: + while (--id >= 0) + regulator_unregister(regs[id].rdev); + + return ret; +} + +static int rc5t583_regulator_remove(struct platform_device *pdev) +{ + struct rc5t583_regulator *regs = platform_get_drvdata(pdev); + int id; + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) + regulator_unregister(regs[id].rdev); + return 0; +} + +static struct platform_driver rc5t583_regulator_driver = { + .driver = { + .name = "rc5t583-regulator", + .owner = THIS_MODULE, + }, + .probe = rc5t583_regulator_probe, + .remove = rc5t583_regulator_remove, +}; + +static int __init rc5t583_regulator_init(void) +{ + return platform_driver_register(&rc5t583_regulator_driver); +} +subsys_initcall(rc5t583_regulator_init); + +static void __exit rc5t583_regulator_exit(void) +{ + platform_driver_unregister(&rc5t583_regulator_driver); +} +module_exit(rc5t583_regulator_exit); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("RC5T583 regulator driver"); +MODULE_ALIAS("platform:rc5t583-regulator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c new file mode 100644 index 0000000..cd9ea2e --- /dev/null +++ b/drivers/regulator/s2mps11.c @@ -0,0 +1,352 @@ +/* + * s2mps11.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct s2mps11_info { + struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX]; + + int ramp_delay2; + int ramp_delay34; + int ramp_delay5; + int ramp_delay16; + int ramp_delay7810; + int ramp_delay9; + + bool buck6_ramp; + bool buck2_ramp; + bool buck3_ramp; + bool buck4_ramp; +}; + +static int get_ramp_delay(int ramp_delay) +{ + unsigned char cnt = 0; + + ramp_delay /= 6; + + while (true) { + ramp_delay = ramp_delay >> 1; + if (ramp_delay == 0) + break; + cnt++; + } + return cnt; +} + +static struct regulator_ops s2mps11_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +static struct regulator_ops s2mps11_buck_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +#define regulator_desc_ldo1(num) { \ + .name = "LDO"#num, \ + .id = S2MPS11_LDO##num, \ + .ops = &s2mps11_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_LDO_MIN, \ + .uV_step = S2MPS11_LDO_STEP1, \ + .n_voltages = S2MPS11_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS11_LDO_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} +#define regulator_desc_ldo2(num) { \ + .name = "LDO"#num, \ + .id = S2MPS11_LDO##num, \ + .ops = &s2mps11_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_LDO_MIN, \ + .uV_step = S2MPS11_LDO_STEP2, \ + .n_voltages = S2MPS11_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS11_LDO_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + +#define regulator_desc_buck1_4(num) { \ + .name = "BUCK"#num, \ + .id = S2MPS11_BUCK##num, \ + .ops = &s2mps11_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_BUCK_MIN1, \ + .uV_step = S2MPS11_BUCK_STEP1, \ + .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_B1CTRL2 + (num - 1) * 2, \ + .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_B1CTRL1 + (num - 1) * 2, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + +#define regulator_desc_buck5 { \ + .name = "BUCK5", \ + .id = S2MPS11_BUCK5, \ + .ops = &s2mps11_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_BUCK_MIN1, \ + .uV_step = S2MPS11_BUCK_STEP1, \ + .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_B5CTRL2, \ + .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_B5CTRL1, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + +#define regulator_desc_buck6_8(num) { \ + .name = "BUCK"#num, \ + .id = S2MPS11_BUCK##num, \ + .ops = &s2mps11_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_BUCK_MIN1, \ + .uV_step = S2MPS11_BUCK_STEP1, \ + .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_B6CTRL2 + (num - 6) * 2, \ + .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_B6CTRL1 + (num - 6) * 2, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + +#define regulator_desc_buck9 { \ + .name = "BUCK9", \ + .id = S2MPS11_BUCK9, \ + .ops = &s2mps11_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_BUCK_MIN3, \ + .uV_step = S2MPS11_BUCK_STEP3, \ + .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_B9CTRL2, \ + .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_B9CTRL1, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + +#define regulator_desc_buck10 { \ + .name = "BUCK10", \ + .id = S2MPS11_BUCK10, \ + .ops = &s2mps11_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS11_BUCK_MIN2, \ + .uV_step = S2MPS11_BUCK_STEP2, \ + .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPS11_REG_B10CTRL2, \ + .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS11_REG_B10CTRL1, \ + .enable_mask = S2MPS11_ENABLE_MASK \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_ldo2(1), + regulator_desc_ldo1(2), + regulator_desc_ldo1(3), + regulator_desc_ldo1(4), + regulator_desc_ldo1(5), + regulator_desc_ldo2(6), + regulator_desc_ldo1(7), + regulator_desc_ldo1(8), + regulator_desc_ldo1(9), + regulator_desc_ldo1(10), + regulator_desc_ldo2(11), + regulator_desc_ldo1(12), + regulator_desc_ldo1(13), + regulator_desc_ldo1(14), + regulator_desc_ldo1(15), + regulator_desc_ldo1(16), + regulator_desc_ldo1(17), + regulator_desc_ldo1(18), + regulator_desc_ldo1(19), + regulator_desc_ldo1(20), + regulator_desc_ldo1(21), + regulator_desc_ldo2(22), + regulator_desc_ldo2(23), + regulator_desc_ldo1(24), + regulator_desc_ldo1(25), + regulator_desc_ldo1(26), + regulator_desc_ldo2(27), + regulator_desc_ldo1(28), + regulator_desc_ldo1(29), + regulator_desc_ldo1(30), + regulator_desc_ldo1(31), + regulator_desc_ldo1(32), + regulator_desc_ldo1(33), + regulator_desc_ldo1(34), + regulator_desc_ldo1(35), + regulator_desc_ldo1(36), + regulator_desc_ldo1(37), + regulator_desc_ldo1(38), + regulator_desc_buck1_4(1), + regulator_desc_buck1_4(2), + regulator_desc_buck1_4(3), + regulator_desc_buck1_4(4), + regulator_desc_buck5, + regulator_desc_buck6_8(6), + regulator_desc_buck6_8(7), + regulator_desc_buck6_8(8), + regulator_desc_buck9, + regulator_desc_buck10, +}; + +static int s2mps11_pmic_probe(struct platform_device *pdev) +{ + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_config config = { }; + struct s2mps11_info *s2mps11; + int i, ret; + unsigned char ramp_enable, ramp_reg = 0; + + if (!pdata) { + dev_err(pdev->dev.parent, "Platform data not supplied\n"); + return -ENODEV; + } + + s2mps11 = devm_kzalloc(&pdev->dev, sizeof(struct s2mps11_info), + GFP_KERNEL); + if (!s2mps11) + return -ENOMEM; + + platform_set_drvdata(pdev, s2mps11); + + s2mps11->ramp_delay2 = pdata->buck2_ramp_delay; + s2mps11->ramp_delay34 = pdata->buck34_ramp_delay; + s2mps11->ramp_delay5 = pdata->buck5_ramp_delay; + s2mps11->ramp_delay16 = pdata->buck16_ramp_delay; + s2mps11->ramp_delay7810 = pdata->buck7810_ramp_delay; + s2mps11->ramp_delay9 = pdata->buck9_ramp_delay; + + s2mps11->buck6_ramp = pdata->buck6_ramp_enable; + s2mps11->buck2_ramp = pdata->buck2_ramp_enable; + s2mps11->buck3_ramp = pdata->buck3_ramp_enable; + s2mps11->buck4_ramp = pdata->buck4_ramp_enable; + + ramp_enable = (s2mps11->buck2_ramp << 3) | (s2mps11->buck3_ramp << 2) | + (s2mps11->buck4_ramp << 1) | s2mps11->buck6_ramp ; + + if (ramp_enable) { + if (s2mps11->buck2_ramp) + ramp_reg |= get_ramp_delay(s2mps11->ramp_delay2) << 6; + if (s2mps11->buck3_ramp || s2mps11->buck4_ramp) + ramp_reg |= get_ramp_delay(s2mps11->ramp_delay34) << 4; + sec_reg_write(iodev, S2MPS11_REG_RAMP, ramp_reg | ramp_enable); + } + + ramp_reg &= 0x00; + ramp_reg |= get_ramp_delay(s2mps11->ramp_delay5) << 6; + ramp_reg |= get_ramp_delay(s2mps11->ramp_delay16) << 4; + ramp_reg |= get_ramp_delay(s2mps11->ramp_delay7810) << 2; + ramp_reg |= get_ramp_delay(s2mps11->ramp_delay9); + sec_reg_write(iodev, S2MPS11_REG_RAMP_BUCK, ramp_reg); + + for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) { + + config.dev = &pdev->dev; + config.regmap = iodev->regmap; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = s2mps11; + + s2mps11->rdev[i] = regulator_register(®ulators[i], &config); + if (IS_ERR(s2mps11->rdev[i])) { + ret = PTR_ERR(s2mps11->rdev[i]); + dev_err(&pdev->dev, "regulator init failed for %d\n", + i); + s2mps11->rdev[i] = NULL; + goto err; + } + } + + return 0; +err: + for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) + regulator_unregister(s2mps11->rdev[i]); + + return ret; +} + +static int s2mps11_pmic_remove(struct platform_device *pdev) +{ + struct s2mps11_info *s2mps11 = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) + regulator_unregister(s2mps11->rdev[i]); + + return 0; +} + +static const struct platform_device_id s2mps11_pmic_id[] = { + { "s2mps11-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id); + +static struct platform_driver s2mps11_pmic_driver = { + .driver = { + .name = "s2mps11-pmic", + .owner = THIS_MODULE, + }, + .probe = s2mps11_pmic_probe, + .remove = s2mps11_pmic_remove, + .id_table = s2mps11_pmic_id, +}; + +static int __init s2mps11_pmic_init(void) +{ + return platform_driver_register(&s2mps11_pmic_driver); +} +subsys_initcall(s2mps11_pmic_init); + +static void __exit s2mps11_pmic_exit(void) +{ + platform_driver_unregister(&s2mps11_pmic_driver); +} +module_exit(s2mps11_pmic_exit); + +/* Module information */ +MODULE_AUTHOR("Sangbeom Kim "); +MODULE_DESCRIPTION("SAMSUNG S2MPS11 Regulator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c new file mode 100644 index 0000000..c24448b --- /dev/null +++ b/drivers/regulator/s5m8767.c @@ -0,0 +1,974 @@ +/* + * s5m8767.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S5M8767_OPMODE_NORMAL_MODE 0x1 + +struct s5m8767_info { + struct device *dev; + struct sec_pmic_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + struct sec_opmode_data *opmode; + + int ramp_delay; + bool buck2_ramp; + bool buck3_ramp; + bool buck4_ramp; + + bool buck2_gpiodvs; + bool buck3_gpiodvs; + bool buck4_gpiodvs; + u8 buck2_vol[8]; + u8 buck3_vol[8]; + u8 buck4_vol[8]; + int buck_gpios[3]; + int buck_ds[3]; + int buck_gpioindex; +}; + +struct sec_voltage_desc { + int max; + int min; + int step; +}; + +static const struct sec_voltage_desc buck_voltage_val1 = { + .max = 2225000, + .min = 650000, + .step = 6250, +}; + +static const struct sec_voltage_desc buck_voltage_val2 = { + .max = 1600000, + .min = 600000, + .step = 6250, +}; + +static const struct sec_voltage_desc buck_voltage_val3 = { + .max = 3000000, + .min = 750000, + .step = 12500, +}; + +static const struct sec_voltage_desc ldo_voltage_val1 = { + .max = 3950000, + .min = 800000, + .step = 50000, +}; + +static const struct sec_voltage_desc ldo_voltage_val2 = { + .max = 2375000, + .min = 800000, + .step = 25000, +}; + +static const struct sec_voltage_desc *reg_voltage_map[] = { + [S5M8767_LDO1] = &ldo_voltage_val2, + [S5M8767_LDO2] = &ldo_voltage_val2, + [S5M8767_LDO3] = &ldo_voltage_val1, + [S5M8767_LDO4] = &ldo_voltage_val1, + [S5M8767_LDO5] = &ldo_voltage_val1, + [S5M8767_LDO6] = &ldo_voltage_val2, + [S5M8767_LDO7] = &ldo_voltage_val2, + [S5M8767_LDO8] = &ldo_voltage_val2, + [S5M8767_LDO9] = &ldo_voltage_val1, + [S5M8767_LDO10] = &ldo_voltage_val1, + [S5M8767_LDO11] = &ldo_voltage_val1, + [S5M8767_LDO12] = &ldo_voltage_val1, + [S5M8767_LDO13] = &ldo_voltage_val1, + [S5M8767_LDO14] = &ldo_voltage_val1, + [S5M8767_LDO15] = &ldo_voltage_val2, + [S5M8767_LDO16] = &ldo_voltage_val1, + [S5M8767_LDO17] = &ldo_voltage_val1, + [S5M8767_LDO18] = &ldo_voltage_val1, + [S5M8767_LDO19] = &ldo_voltage_val1, + [S5M8767_LDO20] = &ldo_voltage_val1, + [S5M8767_LDO21] = &ldo_voltage_val1, + [S5M8767_LDO22] = &ldo_voltage_val1, + [S5M8767_LDO23] = &ldo_voltage_val1, + [S5M8767_LDO24] = &ldo_voltage_val1, + [S5M8767_LDO25] = &ldo_voltage_val1, + [S5M8767_LDO26] = &ldo_voltage_val1, + [S5M8767_LDO27] = &ldo_voltage_val1, + [S5M8767_LDO28] = &ldo_voltage_val1, + [S5M8767_BUCK1] = &buck_voltage_val1, + [S5M8767_BUCK2] = &buck_voltage_val2, + [S5M8767_BUCK3] = &buck_voltage_val2, + [S5M8767_BUCK4] = &buck_voltage_val2, + [S5M8767_BUCK5] = &buck_voltage_val1, + [S5M8767_BUCK6] = &buck_voltage_val1, + [S5M8767_BUCK7] = NULL, + [S5M8767_BUCK8] = NULL, + [S5M8767_BUCK9] = &buck_voltage_val3, +}; + +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 i, 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: + *reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1); + break; + case S5M8767_LDO3 ... S5M8767_LDO28: + *reg = S5M8767_REG_LDO3CTRL + (reg_id - S5M8767_LDO3); + break; + case S5M8767_BUCK1: + *reg = S5M8767_REG_BUCK1CTRL1; + break; + case S5M8767_BUCK2 ... S5M8767_BUCK4: + *reg = S5M8767_REG_BUCK2CTRL + (reg_id - S5M8767_BUCK2) * 9; + break; + case S5M8767_BUCK5: + *reg = S5M8767_REG_BUCK5CTRL1; + break; + case S5M8767_BUCK6 ... S5M8767_BUCK9: + *reg = S5M8767_REG_BUCK6CTRL1 + (reg_id - S5M8767_BUCK6) * 2; + break; + default: + return -EINVAL; + } + + for (i = 0; i < s5m8767->num_regulators; i++) { + if (s5m8767->opmode[i].id == reg_id) { + mode = s5m8767->opmode[i].mode; + break; + } + } + + if (i < s5m8767->num_regulators) + *enable_ctrl = + s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT; + + return 0; +} + +static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int ret, reg; + int mask = 0xc0, enable_ctrl; + unsigned int val; + + ret = s5m8767_get_register(rdev, ®, &enable_ctrl); + if (ret == -EINVAL) + return 1; + else if (ret) + return ret; + + ret = sec_reg_read(s5m8767->iodev, reg, &val); + if (ret) + return ret; + + 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, enable_ctrl; + + ret = s5m8767_get_register(rdev, ®, &enable_ctrl); + if (ret) + return ret; + + return sec_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, enable_ctrl; + + ret = s5m8767_get_register(rdev, ®, &enable_ctrl); + if (ret) + return ret; + + return sec_reg_update(s5m8767->iodev, reg, ~mask, mask); +} + +static int s5m8767_get_vsel_reg(int reg_id, struct s5m8767_info *s5m8767) +{ + int reg; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO2: + reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1); + break; + case S5M8767_LDO3 ... S5M8767_LDO28: + reg = S5M8767_REG_LDO3CTRL + (reg_id - S5M8767_LDO3); + break; + case S5M8767_BUCK1: + reg = S5M8767_REG_BUCK1CTRL2; + break; + case S5M8767_BUCK2: + reg = S5M8767_REG_BUCK2DVS1; + if (s5m8767->buck2_gpiodvs) + reg += s5m8767->buck_gpioindex; + break; + case S5M8767_BUCK3: + reg = S5M8767_REG_BUCK3DVS1; + if (s5m8767->buck3_gpiodvs) + reg += s5m8767->buck_gpioindex; + break; + case S5M8767_BUCK4: + reg = S5M8767_REG_BUCK4DVS1; + if (s5m8767->buck4_gpiodvs) + reg += s5m8767->buck_gpioindex; + break; + case S5M8767_BUCK5: + reg = S5M8767_REG_BUCK5CTRL2; + break; + case S5M8767_BUCK6 ... S5M8767_BUCK9: + reg = S5M8767_REG_BUCK6CTRL2 + (reg_id - S5M8767_BUCK6) * 2; + break; + default: + return -EINVAL; + } + + return reg; +} + +static int s5m8767_convert_voltage_to_sel(const struct sec_voltage_desc *desc, + int min_vol) +{ + int selector = 0; + + if (desc == NULL) + return -EINVAL; + + if (min_vol > desc->max) + return -EINVAL; + + 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 > desc->max) + return -EINVAL; + + return selector; +} + +static inline int 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); + + return 0; +} + +static inline int 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); + + return 0; +} + +static int s5m8767_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int reg_id = rdev_get_id(rdev); + int old_index, index = 0; + u8 *buck234_vol = NULL; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO28: + break; + case S5M8767_BUCK1 ... S5M8767_BUCK6: + 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; + case S5M8767_BUCK9: + break; + default: + return -EINVAL; + } + + /* buck234_vol != NULL means to control buck234 voltage via DVS GPIO */ + if (buck234_vol) { + while (*buck234_vol != selector) { + buck234_vol++; + index++; + } + old_index = s5m8767->buck_gpioindex; + s5m8767->buck_gpioindex = index; + + if (index > old_index) + return s5m8767_set_high(s5m8767); + else + return s5m8767_set_low(s5m8767); + } else { + return regulator_set_voltage_sel_regmap(rdev, selector); + } +} + +static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + const struct sec_voltage_desc *desc; + int reg_id = rdev_get_id(rdev); + + desc = reg_voltage_map[reg_id]; + + if ((old_sel < new_sel) && s5m8767->ramp_delay) + return DIV_ROUND_UP(desc->step * (new_sel - old_sel), + s5m8767->ramp_delay * 1000); + return 0; +} + +static struct regulator_ops s5m8767_ops = { + .list_voltage = regulator_list_voltage_linear, + .is_enabled = s5m8767_reg_is_enabled, + .enable = s5m8767_reg_enable, + .disable = s5m8767_reg_disable, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = s5m8767_set_voltage_sel, + .set_voltage_time_sel = s5m8767_set_voltage_time_sel, +}; + +static struct regulator_ops s5m8767_buck78_ops = { + .is_enabled = s5m8767_reg_is_enabled, + .enable = s5m8767_reg_enable, + .disable = s5m8767_reg_disable, +}; + +#define s5m8767_regulator_desc(_name) { \ + .name = #_name, \ + .id = S5M8767_##_name, \ + .ops = &s5m8767_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + +#define s5m8767_regulator_buck78_desc(_name) { \ + .name = #_name, \ + .id = S5M8767_##_name, \ + .ops = &s5m8767_buck78_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + +static struct regulator_desc regulators[] = { + 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_buck78_desc(BUCK7), + s5m8767_regulator_buck78_desc(BUCK8), + s5m8767_regulator_desc(BUCK9), +}; + +#ifdef CONFIG_OF +static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, + struct sec_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "s5m8767,pmic-buck-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck_gpios[i] = gpio; + } + return 0; +} + +static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev, + struct sec_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "s5m8767,pmic-buck-ds-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck_ds[i] = gpio; + } + return 0; +} + +static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, + struct sec_platform_data *pdata) +{ + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct device_node *pmic_np, *regulators_np, *reg_np; + struct sec_regulator_data *rdata; + struct sec_opmode_data *rmode; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = iodev->dev->of_node; + if (!pmic_np) { + dev_err(iodev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = of_get_child_count(regulators_np); + + rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, + "could not allocate memory for regulator data\n"); + return -ENOMEM; + } + + rmode = devm_kzalloc(&pdev->dev, sizeof(*rmode) * + pdata->num_regulators, GFP_KERNEL); + if (!rmode) { + dev_err(iodev->dev, + "could not allocate memory for regulator mode\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + pdata->opmode = rmode; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(iodev->dev, + "don't know how to configure regulator %s\n", + reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + &pdev->dev, reg_np); + rdata->reg_node = reg_np; + rdata++; + rmode->id = i; + if (of_property_read_u32(reg_np, "op_mode", + &rmode->mode)) { + dev_warn(iodev->dev, + "no op_mode property property at %s\n", + reg_np->full_name); + + rmode->mode = S5M8767_OPMODE_NORMAL_MODE; + } + rmode++; + } + + if (of_get_property(pmic_np, "s5m8767,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "s5m8767,pmic-buck3-uses-gpio-dvs", NULL)) + pdata->buck3_gpiodvs = true; + + if (of_get_property(pmic_np, "s5m8767,pmic-buck4-uses-gpio-dvs", NULL)) + pdata->buck4_gpiodvs = true; + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + ret = s5m8767_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32(pmic_np, + "s5m8767,pmic-buck-default-dvs-idx", + &pdata->buck_default_idx)) { + pdata->buck_default_idx = 0; + } else { + if (pdata->buck_default_idx >= 8) { + pdata->buck_default_idx = 0; + dev_info(iodev->dev, + "invalid value for default dvs index, use 0\n"); + } + } + dvs_voltage_nr = 8; + } + + ret = s5m8767_pmic_dt_parse_ds_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck3-dvs-voltage", + pdata->buck3_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck3 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck4-dvs-voltage", + pdata->buck4_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck4 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, + struct sec_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int s5m8767_pmic_probe(struct platform_device *pdev) +{ + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct sec_platform_data *pdata = iodev->pdata; + struct regulator_config config = { }; + struct regulator_dev **rdev; + struct s5m8767_info *s5m8767; + int i, ret, size, buck_init; + + if (!pdata) { + dev_err(pdev->dev.parent, "Platform data not supplied\n"); + return -ENODEV; + } + + if (iodev->dev->of_node) { + ret = s5m8767_pmic_dt_parse_pdata(pdev, pdata); + if (ret) + return ret; + } + + if (pdata->buck2_gpiodvs) { + if (pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + return -EINVAL; + } + } + + if (pdata->buck3_gpiodvs) { + if (pdata->buck2_gpiodvs || pdata->buck4_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + return -EINVAL; + } + } + + if (pdata->buck4_gpiodvs) { + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + return -EINVAL; + } + } + + s5m8767 = devm_kzalloc(&pdev->dev, sizeof(struct s5m8767_info), + GFP_KERNEL); + if (!s5m8767) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * (S5M8767_REG_MAX - 2); + s5m8767->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!s5m8767->rdev) + return -ENOMEM; + + rdev = s5m8767->rdev; + s5m8767->dev = &pdev->dev; + s5m8767->iodev = iodev; + s5m8767->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, s5m8767); + + s5m8767->buck_gpioindex = pdata->buck_default_idx; + s5m8767->buck2_gpiodvs = pdata->buck2_gpiodvs; + s5m8767->buck3_gpiodvs = pdata->buck3_gpiodvs; + s5m8767->buck4_gpiodvs = pdata->buck4_gpiodvs; + s5m8767->buck_gpios[0] = pdata->buck_gpios[0]; + s5m8767->buck_gpios[1] = pdata->buck_gpios[1]; + s5m8767->buck_gpios[2] = pdata->buck_gpios[2]; + 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->ramp_delay = pdata->buck_ramp_delay; + 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; + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck2_init); + + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck3_init); + + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck4_init); + + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init); + + for (i = 0; i < 8; i++) { + if (s5m8767->buck2_gpiodvs) { + s5m8767->buck2_vol[i] = + s5m8767_convert_voltage_to_sel( + &buck_voltage_val2, + pdata->buck2_voltage[i]); + } + + if (s5m8767->buck3_gpiodvs) { + s5m8767->buck3_vol[i] = + s5m8767_convert_voltage_to_sel( + &buck_voltage_val2, + pdata->buck3_voltage[i]); + } + + if (s5m8767->buck4_gpiodvs) { + s5m8767->buck4_vol[i] = + s5m8767_convert_voltage_to_sel( + &buck_voltage_val2, + pdata->buck4_voltage[i]); + } + } + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + + if (!gpio_is_valid(pdata->buck_gpios[0]) || + !gpio_is_valid(pdata->buck_gpios[1]) || + !gpio_is_valid(pdata->buck_gpios[2])) { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + return -EINVAL; + } + + ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[0], + "S5M8767 SET1"); + if (ret) + return ret; + + ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[1], + "S5M8767 SET2"); + if (ret) + return ret; + + ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[2], + "S5M8767 SET3"); + if (ret) + return ret; + + /* SET1 GPIO */ + gpio_direction_output(pdata->buck_gpios[0], + (s5m8767->buck_gpioindex >> 2) & 0x1); + /* SET2 GPIO */ + gpio_direction_output(pdata->buck_gpios[1], + (s5m8767->buck_gpioindex >> 1) & 0x1); + /* SET3 GPIO */ + gpio_direction_output(pdata->buck_gpios[2], + (s5m8767->buck_gpioindex >> 0) & 0x1); + } + + ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[0], "S5M8767 DS2"); + if (ret) + return ret; + + ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[1], "S5M8767 DS3"); + if (ret) + return ret; + + ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[2], "S5M8767 DS4"); + if (ret) + return ret; + + /* DS2 GPIO */ + gpio_direction_output(pdata->buck_ds[0], 0x0); + /* DS3 GPIO */ + gpio_direction_output(pdata->buck_ds[1], 0x0); + /* DS4 GPIO */ + gpio_direction_output(pdata->buck_ds[2], 0x0); + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, + (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, + (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, + (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + } + + /* Initialize GPIO DVS registers */ + for (i = 0; i < 8; i++) { + if (s5m8767->buck2_gpiodvs) { + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i, + s5m8767->buck2_vol[i]); + } + + if (s5m8767->buck3_gpiodvs) { + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i, + s5m8767->buck3_vol[i]); + } + + if (s5m8767->buck4_gpiodvs) { + sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i, + s5m8767->buck4_vol[i]); + } + } + + if (s5m8767->buck2_ramp) + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08); + + if (s5m8767->buck3_ramp) + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04); + + if (s5m8767->buck4_ramp) + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02); + + if (s5m8767->buck2_ramp || s5m8767->buck3_ramp + || s5m8767->buck4_ramp) { + switch (s5m8767->ramp_delay) { + case 5: + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0x40, 0xf0); + break; + case 10: + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0x90, 0xf0); + break; + case 25: + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xd0, 0xf0); + break; + case 50: + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xe0, 0xf0); + break; + case 100: + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xf0, 0xf0); + break; + default: + sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0x90, 0xf0); + } + } + + for (i = 0; i < pdata->num_regulators; i++) { + const struct sec_voltage_desc *desc; + int id = pdata->regulators[i].id; + + desc = reg_voltage_map[id]; + if (desc) { + regulators[id].n_voltages = + (desc->max - desc->min) / desc->step + 1; + regulators[id].min_uV = desc->min; + regulators[id].uV_step = desc->step; + regulators[id].vsel_reg = + s5m8767_get_vsel_reg(id, s5m8767); + if (id < S5M8767_BUCK1) + regulators[id].vsel_mask = 0x3f; + else + regulators[id].vsel_mask = 0xff; + } + + config.dev = s5m8767->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = s5m8767; + config.regmap = iodev->regmap; + config.of_node = pdata->regulators[i].reg_node; + + 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", + id); + rdev[i] = NULL; + goto err; + } + } + + return 0; +err: + for (i = 0; i < s5m8767->num_regulators; i++) + regulator_unregister(rdev[i]); + + return ret; +} + +static int s5m8767_pmic_remove(struct platform_device *pdev) +{ + struct s5m8767_info *s5m8767 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = s5m8767->rdev; + int i; + + for (i = 0; i < s5m8767->num_regulators; i++) + regulator_unregister(rdev[i]); + + return 0; +} + +static const struct platform_device_id s5m8767_pmic_id[] = { + { "s5m8767-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, s5m8767_pmic_id); + +static struct platform_driver s5m8767_pmic_driver = { + .driver = { + .name = "s5m8767-pmic", + .owner = THIS_MODULE, + }, + .probe = s5m8767_pmic_probe, + .remove = s5m8767_pmic_remove, + .id_table = s5m8767_pmic_id, +}; + +static int __init s5m8767_pmic_init(void) +{ + return platform_driver_register(&s5m8767_pmic_driver); +} +subsys_initcall(s5m8767_pmic_init); + +static void __exit s5m8767_pmic_exit(void) +{ + platform_driver_unregister(&s5m8767_pmic_driver); +} +module_exit(s5m8767_pmic_exit); + +/* Module information */ +MODULE_AUTHOR("Sangbeom Kim "); +MODULE_DESCRIPTION("SAMSUNG S5M8767 Regulator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c new file mode 100644 index 0000000..6e67be7 --- /dev/null +++ b/drivers/regulator/tps51632-regulator.c @@ -0,0 +1,396 @@ +/* + * tps51632-regulator.c -- TI TPS51632 + * + * Regulator driver for TPS51632 3-2-1 Phase D-Cap Step Down Driverless + * Controller with serial VID control and DVFS. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define TPS51632_VOLTAGE_SELECT_REG 0x0 +#define TPS51632_VOLTAGE_BASE_REG 0x1 +#define TPS51632_OFFSET_REG 0x2 +#define TPS51632_IMON_REG 0x3 +#define TPS51632_VMAX_REG 0x4 +#define TPS51632_DVFS_CONTROL_REG 0x5 +#define TPS51632_POWER_STATE_REG 0x6 +#define TPS51632_SLEW_REGS 0x7 +#define TPS51632_FAULT_REG 0x14 + +#define TPS51632_MAX_REG 0x15 + +#define TPS51632_VOUT_MASK 0x7F +#define TPS51632_VOUT_OFFSET_MASK 0x1F +#define TPS51632_VMAX_MASK 0x7F +#define TPS51632_VMAX_LOCK 0x80 + +/* TPS51632_DVFS_CONTROL_REG */ +#define TPS51632_DVFS_PWMEN 0x1 +#define TPS51632_DVFS_STEP_20 0x2 +#define TPS51632_DVFS_VMAX_PG 0x4 +#define TPS51632_DVFS_PWMRST 0x8 +#define TPS51632_DVFS_OCA_EN 0x10 +#define TPS51632_DVFS_FCCM 0x20 + +/* TPS51632_POWER_STATE_REG */ +#define TPS51632_POWER_STATE_MASK 0x03 +#define TPS51632_POWER_STATE_MULTI_PHASE_CCM 0x0 +#define TPS51632_POWER_STATE_SINGLE_PHASE_CCM 0x1 +#define TPS51632_POWER_STATE_SINGLE_PHASE_DCM 0x2 + +#define TPS51632_MIN_VOLATGE 500000 +#define TPS51632_MAX_VOLATGE 1520000 +#define TPS51632_VOLATGE_STEP_10mV 10000 +#define TPS51632_VOLATGE_STEP_20mV 20000 +#define TPS51632_MAX_VSEL 0x7F +#define TPS51632_MIN_VSEL 0x19 +#define TPS51632_DEFAULT_RAMP_DELAY 6000 +#define TPS51632_VOLT_VSEL(uV) \ + (DIV_ROUND_UP(uV - TPS51632_MIN_VOLATGE, \ + TPS51632_VOLATGE_STEP_10mV) + \ + TPS51632_MIN_VSEL) + +/* TPS51632 chip information */ +struct tps51632_chip { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regmap *regmap; +}; + +static int tps51632_dcdc_set_ramp_delay(struct regulator_dev *rdev, + int ramp_delay) +{ + struct tps51632_chip *tps = rdev_get_drvdata(rdev); + int bit = ramp_delay/6000; + int ret; + + if (bit) + bit--; + ret = regmap_write(tps->regmap, TPS51632_SLEW_REGS, BIT(bit)); + if (ret < 0) + dev_err(tps->dev, "SLEW reg write failed, err %d\n", ret); + return ret; +} + +static struct regulator_ops tps51632_dcdc_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = tps51632_dcdc_set_ramp_delay, +}; + +static int tps51632_init_dcdc(struct tps51632_chip *tps, + struct tps51632_regulator_platform_data *pdata) +{ + int ret; + uint8_t control = 0; + int vsel; + + if (!pdata->enable_pwm_dvfs) + goto skip_pwm_config; + + control |= TPS51632_DVFS_PWMEN; + vsel = TPS51632_VOLT_VSEL(pdata->base_voltage_uV); + ret = regmap_write(tps->regmap, TPS51632_VOLTAGE_BASE_REG, vsel); + if (ret < 0) { + dev_err(tps->dev, "BASE reg write failed, err %d\n", ret); + return ret; + } + + if (pdata->dvfs_step_20mV) + control |= TPS51632_DVFS_STEP_20; + + if (pdata->max_voltage_uV) { + unsigned int vmax; + /** + * TPS51632 hw behavior: VMAX register can be write only + * once as it get locked after first write. The lock get + * reset only when device is power-reset. + * Write register only when lock bit is not enabled. + */ + ret = regmap_read(tps->regmap, TPS51632_VMAX_REG, &vmax); + if (ret < 0) { + dev_err(tps->dev, "VMAX read failed, err %d\n", ret); + return ret; + } + if (!(vmax & TPS51632_VMAX_LOCK)) { + vsel = TPS51632_VOLT_VSEL(pdata->max_voltage_uV); + ret = regmap_write(tps->regmap, TPS51632_VMAX_REG, + vsel); + if (ret < 0) { + dev_err(tps->dev, + "VMAX write failed, err %d\n", ret); + return ret; + } + } + } + +skip_pwm_config: + ret = regmap_write(tps->regmap, TPS51632_DVFS_CONTROL_REG, control); + if (ret < 0) + dev_err(tps->dev, "DVFS reg write failed, err %d\n", ret); + return ret; +} + +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS51632_OFFSET_REG: + case TPS51632_FAULT_REG: + case TPS51632_IMON_REG: + return true; + default: + return false; + } +} + +static bool is_read_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x08 ... 0x0F: + return false; + default: + return true; + } +} + +static bool is_write_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS51632_VOLTAGE_SELECT_REG: + case TPS51632_VOLTAGE_BASE_REG: + case TPS51632_VMAX_REG: + case TPS51632_DVFS_CONTROL_REG: + case TPS51632_POWER_STATE_REG: + case TPS51632_SLEW_REGS: + return true; + default: + return false; + } +} + +static const struct regmap_config tps51632_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = is_write_reg, + .readable_reg = is_read_reg, + .volatile_reg = is_volatile_reg, + .max_register = TPS51632_MAX_REG - 1, + .cache_type = REGCACHE_RBTREE, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id tps51632_of_match[] = { + { .compatible = "ti,tps51632",}, + {}, +}; +MODULE_DEVICE_TABLE(of, tps51632_of_match); + +static struct tps51632_regulator_platform_data * + of_get_tps51632_platform_data(struct device *dev) +{ + struct tps51632_regulator_platform_data *pdata; + struct device_node *np = dev->of_node; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "Memory alloc failed for platform data\n"); + return NULL; + } + + pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); + if (!pdata->reg_init_data) { + dev_err(dev, "Not able to get OF regulator init data\n"); + return NULL; + } + + pdata->enable_pwm_dvfs = + of_property_read_bool(np, "ti,enable-pwm-dvfs"); + pdata->dvfs_step_20mV = of_property_read_bool(np, "ti,dvfs-step-20mV"); + + pdata->base_voltage_uV = pdata->reg_init_data->constraints.min_uV ? : + TPS51632_MIN_VOLATGE; + pdata->max_voltage_uV = pdata->reg_init_data->constraints.max_uV ? : + TPS51632_MAX_VOLATGE; + return pdata; +} +#else +static struct tps51632_regulator_platform_data * + of_get_tps51632_platform_data(struct device *dev) +{ + return NULL; +} +#endif + +static int tps51632_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps51632_regulator_platform_data *pdata; + struct regulator_dev *rdev; + struct tps51632_chip *tps; + int ret; + struct regulator_config config = { }; + + if (client->dev.of_node) { + const struct of_device_id *match; + match = of_match_device(of_match_ptr(tps51632_of_match), + &client->dev); + if (!match) { + dev_err(&client->dev, "Error: No device match found\n"); + return -ENODEV; + } + } + + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) + pdata = of_get_tps51632_platform_data(&client->dev); + if (!pdata) { + dev_err(&client->dev, "No Platform data\n"); + return -EINVAL; + } + + if (pdata->enable_pwm_dvfs) { + if ((pdata->base_voltage_uV < TPS51632_MIN_VOLATGE) || + (pdata->base_voltage_uV > TPS51632_MAX_VOLATGE)) { + dev_err(&client->dev, "Invalid base_voltage_uV setting\n"); + return -EINVAL; + } + + if ((pdata->max_voltage_uV) && + ((pdata->max_voltage_uV < TPS51632_MIN_VOLATGE) || + (pdata->max_voltage_uV > TPS51632_MAX_VOLATGE))) { + dev_err(&client->dev, "Invalid max_voltage_uV setting\n"); + return -EINVAL; + } + } + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) { + dev_err(&client->dev, "Memory allocation failed\n"); + return -ENOMEM; + } + + tps->dev = &client->dev; + tps->desc.name = id->name; + tps->desc.id = 0; + tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY; + tps->desc.min_uV = TPS51632_MIN_VOLATGE; + tps->desc.uV_step = TPS51632_VOLATGE_STEP_10mV; + tps->desc.linear_min_sel = TPS51632_MIN_VSEL; + tps->desc.n_voltages = TPS51632_MAX_VSEL + 1; + tps->desc.ops = &tps51632_dcdc_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + + if (pdata->enable_pwm_dvfs) + tps->desc.vsel_reg = TPS51632_VOLTAGE_BASE_REG; + else + tps->desc.vsel_reg = TPS51632_VOLTAGE_SELECT_REG; + tps->desc.vsel_mask = TPS51632_VOUT_MASK; + + tps->regmap = devm_regmap_init_i2c(client, &tps51632_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(&client->dev, "regmap init failed, err %d\n", ret); + return ret; + } + i2c_set_clientdata(client, tps); + + ret = tps51632_init_dcdc(tps, pdata); + if (ret < 0) { + dev_err(tps->dev, "Init failed, err = %d\n", ret); + return ret; + } + + /* Register the regulators */ + config.dev = &client->dev; + config.init_data = pdata->reg_init_data; + config.driver_data = tps; + config.regmap = tps->regmap; + config.of_node = client->dev.of_node; + + rdev = regulator_register(&tps->desc, &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "regulator register failed\n"); + return PTR_ERR(rdev); + } + + tps->rdev = rdev; + return 0; +} + +static int tps51632_remove(struct i2c_client *client) +{ + struct tps51632_chip *tps = i2c_get_clientdata(client); + + regulator_unregister(tps->rdev); + return 0; +} + +static const struct i2c_device_id tps51632_id[] = { + {.name = "tps51632",}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tps51632_id); + +static struct i2c_driver tps51632_i2c_driver = { + .driver = { + .name = "tps51632", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps51632_of_match), + }, + .probe = tps51632_probe, + .remove = tps51632_remove, + .id_table = tps51632_id, +}; + +static int __init tps51632_init(void) +{ + return i2c_add_driver(&tps51632_i2c_driver); +} +subsys_initcall(tps51632_init); + +static void __exit tps51632_cleanup(void) +{ + i2c_del_driver(&tps51632_i2c_driver); +} +module_exit(tps51632_cleanup); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("TPS51632 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c new file mode 100644 index 0000000..ec9453f --- /dev/null +++ b/drivers/regulator/tps6105x-regulator.c @@ -0,0 +1,193 @@ +/* + * Driver for TPS61050/61052 boost converters, typically used for white LEDs + * or audio amplifiers. + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const unsigned int tps6105x_voltages[] = { + 4500000, + 5000000, + 5250000, + 5000000, /* There is an additional 5V */ +}; + +static int tps6105x_regulator_enable(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + int ret; + + /* Activate voltage mode */ + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK, + TPS6105X_REG0_MODE_VOLTAGE << TPS6105X_REG0_MODE_SHIFT); + if (ret) + return ret; + + return 0; +} + +static int tps6105x_regulator_disable(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + int ret; + + /* Set into shutdown mode */ + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK, + TPS6105X_REG0_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); + if (ret) + return ret; + + return 0; +} + +static int tps6105x_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + u8 regval; + int ret; + + ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); + if (ret) + return ret; + regval &= TPS6105X_REG0_MODE_MASK; + regval >>= TPS6105X_REG0_MODE_SHIFT; + + if (regval == TPS6105X_REG0_MODE_VOLTAGE) + return 1; + + return 0; +} + +static int tps6105x_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + u8 regval; + int ret; + + ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); + if (ret) + return ret; + + regval &= TPS6105X_REG0_VOLTAGE_MASK; + regval >>= TPS6105X_REG0_VOLTAGE_SHIFT; + return (int) regval; +} + +static int tps6105x_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + int ret; + + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_VOLTAGE_MASK, + selector << TPS6105X_REG0_VOLTAGE_SHIFT); + if (ret) + return ret; + + return 0; +} + +static struct regulator_ops tps6105x_regulator_ops = { + .enable = tps6105x_regulator_enable, + .disable = tps6105x_regulator_disable, + .is_enabled = tps6105x_regulator_is_enabled, + .get_voltage_sel = tps6105x_regulator_get_voltage_sel, + .set_voltage_sel = tps6105x_regulator_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static const struct regulator_desc tps6105x_regulator_desc = { + .name = "tps6105x-boost", + .ops = &tps6105x_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = 0, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(tps6105x_voltages), + .volt_table = tps6105x_voltages, +}; + +/* + * Registers the chip as a voltage regulator + */ +static int tps6105x_regulator_probe(struct platform_device *pdev) +{ + struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); + struct tps6105x_platform_data *pdata = tps6105x->pdata; + struct regulator_config config = { }; + int ret; + + /* This instance is not set for regulator mode so bail out */ + if (pdata->mode != TPS6105X_MODE_VOLTAGE) { + dev_info(&pdev->dev, + "chip not in voltage mode mode, exit probe \n"); + return 0; + } + + config.dev = &tps6105x->client->dev; + config.init_data = pdata->regulator_data; + config.driver_data = tps6105x; + + /* Register regulator with framework */ + tps6105x->regulator = regulator_register(&tps6105x_regulator_desc, + &config); + if (IS_ERR(tps6105x->regulator)) { + ret = PTR_ERR(tps6105x->regulator); + dev_err(&tps6105x->client->dev, + "failed to register regulator\n"); + return ret; + } + platform_set_drvdata(pdev, tps6105x); + + return 0; +} + +static int tps6105x_regulator_remove(struct platform_device *pdev) +{ + struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); + regulator_unregister(tps6105x->regulator); + return 0; +} + +static struct platform_driver tps6105x_regulator_driver = { + .driver = { + .name = "tps6105x-regulator", + .owner = THIS_MODULE, + }, + .probe = tps6105x_regulator_probe, + .remove = tps6105x_regulator_remove, +}; + +static __init int tps6105x_regulator_init(void) +{ + return platform_driver_register(&tps6105x_regulator_driver); +} +subsys_initcall(tps6105x_regulator_init); + +static __exit void tps6105x_regulator_exit(void) +{ + platform_driver_unregister(&tps6105x_regulator_driver); +} +module_exit(tps6105x_regulator_exit); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("TPS6105x regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6105x-regulator"); diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c new file mode 100644 index 0000000..612919c --- /dev/null +++ b/drivers/regulator/tps62360-regulator.c @@ -0,0 +1,553 @@ +/* + * tps62360.c -- TI tps62360 + * + * Driver for processor core supply tps62360, tps62361B, tps62362 and tps62363. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define REG_VSET0 0 +#define REG_VSET1 1 +#define REG_VSET2 2 +#define REG_VSET3 3 +#define REG_CONTROL 4 +#define REG_TEMP 5 +#define REG_RAMPCTRL 6 +#define REG_CHIPID 8 + +#define FORCE_PWM_ENABLE BIT(7) + +enum chips {TPS62360, TPS62361, TPS62362, TPS62363}; + +#define TPS62360_BASE_VOLTAGE 770000 +#define TPS62360_N_VOLTAGES 64 + +#define TPS62361_BASE_VOLTAGE 500000 +#define TPS62361_N_VOLTAGES 128 + +/* tps 62360 chip information */ +struct tps62360_chip { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regmap *regmap; + int vsel0_gpio; + int vsel1_gpio; + u8 voltage_reg_mask; + bool en_internal_pulldn; + bool en_discharge; + bool valid_gpios; + int lru_index[4]; + int curr_vset_vsel[4]; + int curr_vset_id; +}; + +/* + * find_voltage_set_register: Find new voltage configuration register + * (VSET) id. + * The finding of the new VSET register will be based on the LRU mechanism. + * Each VSET register will have different voltage configured . This + * Function will look if any of the VSET register have requested voltage set + * or not. + * - If it is already there then it will make that register as most + * recently used and return as found so that caller need not to set + * the VSET register but need to set the proper gpios to select this + * VSET register. + * - If requested voltage is not found then it will use the least + * recently mechanism to get new VSET register for new configuration + * and will return not_found so that caller need to set new VSET + * register and then gpios (both). + */ +static bool find_voltage_set_register(struct tps62360_chip *tps, + int req_vsel, int *vset_reg_id) +{ + int i; + bool found = false; + int new_vset_reg = tps->lru_index[3]; + int found_index = 3; + + for (i = 0; i < 4; ++i) { + if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) { + new_vset_reg = tps->lru_index[i]; + found_index = i; + found = true; + goto update_lru_index; + } + } + +update_lru_index: + for (i = found_index; i > 0; i--) + tps->lru_index[i] = tps->lru_index[i - 1]; + + tps->lru_index[0] = new_vset_reg; + *vset_reg_id = new_vset_reg; + return found; +} + +static int tps62360_dcdc_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps62360_chip *tps = rdev_get_drvdata(dev); + int vsel; + unsigned int data; + int ret; + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); + if (ret < 0) { + dev_err(tps->dev, "%s(): register %d read failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); + return ret; + } + vsel = (int)data & tps->voltage_reg_mask; + return vsel; +} + +static int tps62360_dcdc_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps62360_chip *tps = rdev_get_drvdata(dev); + int ret; + bool found = false; + int new_vset_id = tps->curr_vset_id; + + /* + * If gpios are available to select the VSET register then least + * recently used register for new configuration. + */ + if (tps->valid_gpios) + found = find_voltage_set_register(tps, selector, &new_vset_id); + + if (!found) { + ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id, + tps->voltage_reg_mask, selector); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + new_vset_id, ret); + return ret; + } + tps->curr_vset_id = new_vset_id; + tps->curr_vset_vsel[new_vset_id] = selector; + } + + /* Select proper VSET register vio gpios */ + if (tps->valid_gpios) { + gpio_set_value_cansleep(tps->vsel0_gpio, new_vset_id & 0x1); + gpio_set_value_cansleep(tps->vsel1_gpio, + (new_vset_id >> 1) & 0x1); + } + return 0; +} + +static int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct tps62360_chip *tps = rdev_get_drvdata(rdev); + int i; + int val; + int ret; + + /* Enable force PWM mode in FAST mode only. */ + switch (mode) { + case REGULATOR_MODE_FAST: + val = FORCE_PWM_ENABLE; + break; + + case REGULATOR_MODE_NORMAL: + val = 0; + break; + + default: + return -EINVAL; + } + + if (!tps->valid_gpios) { + ret = regmap_update_bits(tps->regmap, + REG_VSET0 + tps->curr_vset_id, FORCE_PWM_ENABLE, val); + if (ret < 0) + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); + return ret; + } + + /* If gpios are valid then all register set need to be control */ + for (i = 0; i < 4; ++i) { + ret = regmap_update_bits(tps->regmap, + REG_VSET0 + i, FORCE_PWM_ENABLE, val); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_VSET0 + i, ret); + return ret; + } + } + return ret; +} + +static unsigned int tps62360_get_mode(struct regulator_dev *rdev) +{ + struct tps62360_chip *tps = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); + if (ret < 0) { + dev_err(tps->dev, "%s(): register %d read failed with err %d\n", + __func__, REG_VSET0 + tps->curr_vset_id, ret); + return ret; + } + return (data & FORCE_PWM_ENABLE) ? + REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops tps62360_dcdc_ops = { + .get_voltage_sel = tps62360_dcdc_get_voltage_sel, + .set_voltage_sel = tps62360_dcdc_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_mode = tps62360_set_mode, + .get_mode = tps62360_get_mode, +}; + +static int tps62360_init_dcdc(struct tps62360_chip *tps, + struct tps62360_regulator_platform_data *pdata) +{ + int ret; + unsigned int ramp_ctrl; + + /* Initialize internal pull up/down control */ + if (tps->en_internal_pulldn) + ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0); + else + ret = regmap_write(tps->regmap, REG_CONTROL, 0x0); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d write failed with err %d\n", + __func__, REG_CONTROL, ret); + return ret; + } + + /* Reset output discharge path to reduce power consumption */ + ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_RAMPCTRL, ret); + return ret; + } + + /* Get ramp value from ramp control register */ + ret = regmap_read(tps->regmap, REG_RAMPCTRL, &ramp_ctrl); + if (ret < 0) { + dev_err(tps->dev, + "%s(): register %d read failed with err %d\n", + __func__, REG_RAMPCTRL, ret); + return ret; + } + ramp_ctrl = (ramp_ctrl >> 5) & 0x7; + + /* ramp mV/us = 32/(2^ramp_ctrl) */ + tps->desc.ramp_delay = DIV_ROUND_UP(32000, BIT(ramp_ctrl)); + return ret; +} + +static const struct regmap_config tps62360_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_CHIPID, + .cache_type = REGCACHE_RBTREE, +}; + +static struct tps62360_regulator_platform_data * + of_get_tps62360_platform_data(struct device *dev) +{ + struct tps62360_regulator_platform_data *pdata; + struct device_node *np = dev->of_node; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "Memory alloc failed for platform data\n"); + return NULL; + } + + pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); + if (!pdata->reg_init_data) { + dev_err(dev, "Not able to get OF regulator init data\n"); + return NULL; + } + + pdata->vsel0_gpio = of_get_named_gpio(np, "vsel0-gpio", 0); + pdata->vsel1_gpio = of_get_named_gpio(np, "vsel1-gpio", 0); + + if (of_find_property(np, "ti,vsel0-state-high", NULL)) + pdata->vsel0_def_state = 1; + + if (of_find_property(np, "ti,vsel1-state-high", NULL)) + pdata->vsel1_def_state = 1; + + if (of_find_property(np, "ti,enable-pull-down", NULL)) + pdata->en_internal_pulldn = true; + + if (of_find_property(np, "ti,enable-vout-discharge", NULL)) + pdata->en_discharge = true; + + return pdata; +} + +#if defined(CONFIG_OF) +static const struct of_device_id tps62360_of_match[] = { + { .compatible = "ti,tps62360", .data = (void *)TPS62360}, + { .compatible = "ti,tps62361", .data = (void *)TPS62361}, + { .compatible = "ti,tps62362", .data = (void *)TPS62362}, + { .compatible = "ti,tps62363", .data = (void *)TPS62363}, + {}, +}; +MODULE_DEVICE_TABLE(of, tps62360_of_match); +#endif + +static int tps62360_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regulator_config config = { }; + struct tps62360_regulator_platform_data *pdata; + struct regulator_dev *rdev; + struct tps62360_chip *tps; + int ret; + int i; + int chip_id; + + pdata = client->dev.platform_data; + chip_id = id->driver_data; + + if (client->dev.of_node) { + const struct of_device_id *match; + match = of_match_device(of_match_ptr(tps62360_of_match), + &client->dev); + if (!match) { + dev_err(&client->dev, "Error: No device match found\n"); + return -ENODEV; + } + chip_id = (int)match->data; + if (!pdata) + pdata = of_get_tps62360_platform_data(&client->dev); + } + + if (!pdata) { + dev_err(&client->dev, "%s(): Platform data not found\n", + __func__); + return -EIO; + } + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) { + dev_err(&client->dev, "%s(): Memory allocation failed\n", + __func__); + return -ENOMEM; + } + + tps->en_discharge = pdata->en_discharge; + tps->en_internal_pulldn = pdata->en_internal_pulldn; + tps->vsel0_gpio = pdata->vsel0_gpio; + tps->vsel1_gpio = pdata->vsel1_gpio; + tps->dev = &client->dev; + + switch (chip_id) { + case TPS62360: + case TPS62362: + tps->desc.min_uV = TPS62360_BASE_VOLTAGE; + tps->voltage_reg_mask = 0x3F; + tps->desc.n_voltages = TPS62360_N_VOLTAGES; + break; + case TPS62361: + case TPS62363: + tps->desc.min_uV = TPS62361_BASE_VOLTAGE; + tps->voltage_reg_mask = 0x7F; + tps->desc.n_voltages = TPS62361_N_VOLTAGES; + break; + default: + return -ENODEV; + } + + tps->desc.name = id->name; + tps->desc.id = 0; + tps->desc.ops = &tps62360_dcdc_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + tps->desc.uV_step = 10000; + + tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(&client->dev, + "%s(): regmap allocation failed with err %d\n", + __func__, ret); + return ret; + } + i2c_set_clientdata(client, tps); + + tps->curr_vset_id = (pdata->vsel1_def_state & 1) * 2 + + (pdata->vsel0_def_state & 1); + tps->lru_index[0] = tps->curr_vset_id; + tps->valid_gpios = false; + + if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) { + int gpio_flags; + gpio_flags = (pdata->vsel0_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = devm_gpio_request_one(&client->dev, tps->vsel0_gpio, + gpio_flags, "tps62360-vsel0"); + if (ret) { + dev_err(&client->dev, + "%s(): Could not obtain vsel0 GPIO %d: %d\n", + __func__, tps->vsel0_gpio, ret); + return ret; + } + + gpio_flags = (pdata->vsel1_def_state) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + ret = devm_gpio_request_one(&client->dev, tps->vsel1_gpio, + gpio_flags, "tps62360-vsel1"); + if (ret) { + dev_err(&client->dev, + "%s(): Could not obtain vsel1 GPIO %d: %d\n", + __func__, tps->vsel1_gpio, ret); + return ret; + } + tps->valid_gpios = true; + + /* + * Initialize the lru index with vset_reg id + * The index 0 will be most recently used and + * set with the tps->curr_vset_id */ + for (i = 0; i < 4; ++i) + tps->lru_index[i] = i; + tps->lru_index[0] = tps->curr_vset_id; + tps->lru_index[tps->curr_vset_id] = 0; + } + + ret = tps62360_init_dcdc(tps, pdata); + if (ret < 0) { + dev_err(tps->dev, "%s(): Init failed with err = %d\n", + __func__, ret); + return ret; + } + + config.dev = &client->dev; + config.init_data = pdata->reg_init_data; + config.driver_data = tps; + config.of_node = client->dev.of_node; + + /* Register the regulators */ + rdev = regulator_register(&tps->desc, &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, + "%s(): regulator register failed with err %s\n", + __func__, id->name); + return PTR_ERR(rdev); + } + + tps->rdev = rdev; + return 0; +} + +/** + * tps62360_remove - tps62360 driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister TPS driver as an i2c client device driver + */ +static int tps62360_remove(struct i2c_client *client) +{ + struct tps62360_chip *tps = i2c_get_clientdata(client); + + regulator_unregister(tps->rdev); + return 0; +} + +static void tps62360_shutdown(struct i2c_client *client) +{ + struct tps62360_chip *tps = i2c_get_clientdata(client); + int st; + + if (!tps->en_discharge) + return; + + /* Configure the output discharge path */ + st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2)); + if (st < 0) + dev_err(tps->dev, + "%s(): register %d update failed with err %d\n", + __func__, REG_RAMPCTRL, st); +} + +static const struct i2c_device_id tps62360_id[] = { + {.name = "tps62360", .driver_data = TPS62360}, + {.name = "tps62361", .driver_data = TPS62361}, + {.name = "tps62362", .driver_data = TPS62362}, + {.name = "tps62363", .driver_data = TPS62363}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tps62360_id); + +static struct i2c_driver tps62360_i2c_driver = { + .driver = { + .name = "tps62360", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps62360_of_match), + }, + .probe = tps62360_probe, + .remove = tps62360_remove, + .shutdown = tps62360_shutdown, + .id_table = tps62360_id, +}; + +static int __init tps62360_init(void) +{ + return i2c_add_driver(&tps62360_i2c_driver); +} +subsys_initcall(tps62360_init); + +static void __exit tps62360_cleanup(void) +{ + i2c_del_driver(&tps62360_i2c_driver); +} +module_exit(tps62360_cleanup); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("TPS6236x voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c new file mode 100644 index 0000000..9d053e2 --- /dev/null +++ b/drivers/regulator/tps65023-regulator.c @@ -0,0 +1,451 @@ +/* + * tps65023-regulator.c + * + * Supports TPS65023 Regulator + * + * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define TPS65023_REG_VERSION 0 +#define TPS65023_REG_PGOODZ 1 +#define TPS65023_REG_MASK 2 +#define TPS65023_REG_REG_CTRL 3 +#define TPS65023_REG_CON_CTRL 4 +#define TPS65023_REG_CON_CTRL2 5 +#define TPS65023_REG_DEF_CORE 6 +#define TPS65023_REG_DEFSLEW 7 +#define TPS65023_REG_LDO_CTRL 8 + +/* PGOODZ bitfields */ +#define TPS65023_PGOODZ_PWRFAILZ BIT(7) +#define TPS65023_PGOODZ_LOWBATTZ BIT(6) +#define TPS65023_PGOODZ_VDCDC1 BIT(5) +#define TPS65023_PGOODZ_VDCDC2 BIT(4) +#define TPS65023_PGOODZ_VDCDC3 BIT(3) +#define TPS65023_PGOODZ_LDO2 BIT(2) +#define TPS65023_PGOODZ_LDO1 BIT(1) + +/* MASK bitfields */ +#define TPS65023_MASK_PWRFAILZ BIT(7) +#define TPS65023_MASK_LOWBATTZ BIT(6) +#define TPS65023_MASK_VDCDC1 BIT(5) +#define TPS65023_MASK_VDCDC2 BIT(4) +#define TPS65023_MASK_VDCDC3 BIT(3) +#define TPS65023_MASK_LDO2 BIT(2) +#define TPS65023_MASK_LDO1 BIT(1) + +/* REG_CTRL bitfields */ +#define TPS65023_REG_CTRL_VDCDC1_EN BIT(5) +#define TPS65023_REG_CTRL_VDCDC2_EN BIT(4) +#define TPS65023_REG_CTRL_VDCDC3_EN BIT(3) +#define TPS65023_REG_CTRL_LDO2_EN BIT(2) +#define TPS65023_REG_CTRL_LDO1_EN BIT(1) + +/* REG_CTRL2 bitfields */ +#define TPS65023_REG_CTRL2_GO BIT(7) +#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6) +#define TPS65023_REG_CTRL2_DCDC2 BIT(2) +#define TPS65023_REG_CTRL2_DCDC1 BIT(1) +#define TPS65023_REG_CTRL2_DCDC3 BIT(0) + +/* Number of step-down converters available */ +#define TPS65023_NUM_DCDC 3 +/* Number of LDO voltage regulators available */ +#define TPS65023_NUM_LDO 2 +/* Number of total regulators available */ +#define TPS65023_NUM_REGULATOR (TPS65023_NUM_DCDC + TPS65023_NUM_LDO) + +/* DCDCs */ +#define TPS65023_DCDC_1 0 +#define TPS65023_DCDC_2 1 +#define TPS65023_DCDC_3 2 +/* LDOs */ +#define TPS65023_LDO_1 3 +#define TPS65023_LDO_2 4 + +#define TPS65023_MAX_REG_ID TPS65023_LDO_2 + +/* Supported voltage values for regulators */ +static const unsigned int VCORE_VSEL_table[] = { + 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1600000, +}; + +static const unsigned int DCDC_FIXED_3300000_VSEL_table[] = { + 3300000, +}; + +static const unsigned int DCDC_FIXED_1800000_VSEL_table[] = { + 1800000, +}; + +/* Supported voltage values for LDO regulators for tps65020 */ +static const unsigned int TPS65020_LDO_VSEL_table[] = { + 1000000, 1050000, 1100000, 1300000, + 1800000, 2500000, 3000000, 3300000, +}; + +/* Supported voltage values for LDO regulators + * for tps65021 and tps65023 */ +static const unsigned int TPS65023_LDO1_VSEL_table[] = { + 1000000, 1100000, 1300000, 1800000, + 2200000, 2600000, 2800000, 3150000, +}; + +static const unsigned int TPS65023_LDO2_VSEL_table[] = { + 1050000, 1200000, 1300000, 1800000, + 2500000, 2800000, 3000000, 3300000, +}; + +/* Regulator specific details */ +struct tps_info { + const char *name; + u8 table_len; + const unsigned int *table; +}; + +/* PMIC details */ +struct tps_pmic { + struct regulator_desc desc[TPS65023_NUM_REGULATOR]; + struct regulator_dev *rdev[TPS65023_NUM_REGULATOR]; + const struct tps_info *info[TPS65023_NUM_REGULATOR]; + struct regmap *regmap; + u8 core_regulator; +}; + +/* Struct passed as driver data */ +struct tps_driver_data { + const struct tps_info *info; + u8 core_regulator; +}; + +static int tps65023_dcdc_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + + if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) + return -EINVAL; + + if (dcdc != tps->core_regulator) + return 0; + + return regulator_get_voltage_sel_regmap(dev); +} + +static int tps65023_dcdc_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + + if (dcdc != tps->core_regulator) + return -EINVAL; + + return regulator_set_voltage_sel_regmap(dev, selector); +} + +/* Operations permitted on VDCDCx */ +static struct regulator_ops tps65023_dcdc_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = tps65023_dcdc_get_voltage_sel, + .set_voltage_sel = tps65023_dcdc_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, +}; + +/* Operations permitted on LDOx */ +static struct regulator_ops tps65023_ldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, +}; + +static struct regmap_config tps65023_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int tps_65023_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct tps_driver_data *drv_data = (void *)id->driver_data; + const struct tps_info *info = drv_data->info; + struct regulator_config config = { }; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + struct tps_pmic *tps; + int i; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + init_data = client->dev.platform_data; + if (!init_data) + return -EIO; + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + tps->regmap = devm_regmap_init_i2c(client, &tps65023_regmap_config); + if (IS_ERR(tps->regmap)) { + error = PTR_ERR(tps->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + /* common for all regulators */ + tps->core_regulator = drv_data->core_regulator; + + for (i = 0; i < TPS65023_NUM_REGULATOR; i++, info++, init_data++) { + /* Store regulator specific information */ + tps->info[i] = info; + + tps->desc[i].name = info->name; + tps->desc[i].id = i; + tps->desc[i].n_voltages = info->table_len; + tps->desc[i].volt_table = info->table; + tps->desc[i].ops = (i > TPS65023_DCDC_3 ? + &tps65023_ldo_ops : &tps65023_dcdc_ops); + tps->desc[i].type = REGULATOR_VOLTAGE; + tps->desc[i].owner = THIS_MODULE; + + tps->desc[i].enable_reg = TPS65023_REG_REG_CTRL; + switch (i) { + case TPS65023_LDO_1: + tps->desc[i].vsel_reg = TPS65023_REG_LDO_CTRL; + tps->desc[i].vsel_mask = 0x07; + tps->desc[i].enable_mask = 1 << 1; + break; + case TPS65023_LDO_2: + tps->desc[i].vsel_reg = TPS65023_REG_LDO_CTRL; + tps->desc[i].vsel_mask = 0x70; + tps->desc[i].enable_mask = 1 << 2; + break; + default: /* DCDCx */ + tps->desc[i].enable_mask = + 1 << (TPS65023_NUM_REGULATOR - i); + tps->desc[i].vsel_reg = TPS65023_REG_DEF_CORE; + tps->desc[i].vsel_mask = info->table_len - 1; + tps->desc[i].apply_reg = TPS65023_REG_CON_CTRL2; + tps->desc[i].apply_bit = TPS65023_REG_CTRL2_GO; + } + + config.dev = &client->dev; + config.init_data = init_data; + config.driver_data = tps; + config.regmap = tps->regmap; + + /* Register the regulators */ + rdev = regulator_register(&tps->desc[i], &config); + if (IS_ERR(rdev)) { + dev_err(&client->dev, "failed to register %s\n", + id->name); + error = PTR_ERR(rdev); + goto fail; + } + + /* Save regulator for cleanup */ + tps->rdev[i] = rdev; + } + + i2c_set_clientdata(client, tps); + + /* Enable setting output voltage by I2C */ + regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_CORE_ADJ, TPS65023_REG_CTRL2_CORE_ADJ); + + return 0; + + fail: + while (--i >= 0) + regulator_unregister(tps->rdev[i]); + return error; +} + +static int tps_65023_remove(struct i2c_client *client) +{ + struct tps_pmic *tps = i2c_get_clientdata(client); + int i; + + for (i = 0; i < TPS65023_NUM_REGULATOR; i++) + regulator_unregister(tps->rdev[i]); + return 0; +} + +static const struct tps_info tps65020_regs[] = { + { + .name = "VDCDC1", + .table_len = ARRAY_SIZE(DCDC_FIXED_3300000_VSEL_table), + .table = DCDC_FIXED_3300000_VSEL_table, + }, + { + .name = "VDCDC2", + .table_len = ARRAY_SIZE(DCDC_FIXED_1800000_VSEL_table), + .table = DCDC_FIXED_1800000_VSEL_table, + }, + { + .name = "VDCDC3", + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + { + .name = "LDO1", + .table_len = ARRAY_SIZE(TPS65020_LDO_VSEL_table), + .table = TPS65020_LDO_VSEL_table, + }, + { + .name = "LDO2", + .table_len = ARRAY_SIZE(TPS65020_LDO_VSEL_table), + .table = TPS65020_LDO_VSEL_table, + }, +}; + +static const struct tps_info tps65021_regs[] = { + { + .name = "VDCDC1", + .table_len = ARRAY_SIZE(DCDC_FIXED_3300000_VSEL_table), + .table = DCDC_FIXED_3300000_VSEL_table, + }, + { + .name = "VDCDC2", + .table_len = ARRAY_SIZE(DCDC_FIXED_1800000_VSEL_table), + .table = DCDC_FIXED_1800000_VSEL_table, + }, + { + .name = "VDCDC3", + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + { + .name = "LDO1", + .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), + .table = TPS65023_LDO1_VSEL_table, + }, + { + .name = "LDO2", + .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), + .table = TPS65023_LDO2_VSEL_table, + }, +}; + +static const struct tps_info tps65023_regs[] = { + { + .name = "VDCDC1", + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + { + .name = "VDCDC2", + .table_len = ARRAY_SIZE(DCDC_FIXED_3300000_VSEL_table), + .table = DCDC_FIXED_3300000_VSEL_table, + }, + { + .name = "VDCDC3", + .table_len = ARRAY_SIZE(DCDC_FIXED_1800000_VSEL_table), + .table = DCDC_FIXED_1800000_VSEL_table, + }, + { + .name = "LDO1", + .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), + .table = TPS65023_LDO1_VSEL_table, + }, + { + .name = "LDO2", + .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), + .table = TPS65023_LDO2_VSEL_table, + }, +}; + +static struct tps_driver_data tps65020_drv_data = { + .info = tps65020_regs, + .core_regulator = TPS65023_DCDC_3, +}; + +static struct tps_driver_data tps65021_drv_data = { + .info = tps65021_regs, + .core_regulator = TPS65023_DCDC_3, +}; + +static struct tps_driver_data tps65023_drv_data = { + .info = tps65023_regs, + .core_regulator = TPS65023_DCDC_1, +}; + +static const struct i2c_device_id tps_65023_id[] = { + {.name = "tps65023", + .driver_data = (unsigned long) &tps65023_drv_data}, + {.name = "tps65021", + .driver_data = (unsigned long) &tps65021_drv_data,}, + {.name = "tps65020", + .driver_data = (unsigned long) &tps65020_drv_data}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, tps_65023_id); + +static struct i2c_driver tps_65023_i2c_driver = { + .driver = { + .name = "tps65023", + .owner = THIS_MODULE, + }, + .probe = tps_65023_probe, + .remove = tps_65023_remove, + .id_table = tps_65023_id, +}; + +static int __init tps_65023_init(void) +{ + return i2c_add_driver(&tps_65023_i2c_driver); +} +subsys_initcall(tps_65023_init); + +static void __exit tps_65023_cleanup(void) +{ + i2c_del_driver(&tps_65023_i2c_driver); +} +module_exit(tps_65023_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TPS65023 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c new file mode 100644 index 0000000..4117ff5 --- /dev/null +++ b/drivers/regulator/tps6507x-regulator.c @@ -0,0 +1,570 @@ +/* + * tps6507x-regulator.c + * + * Regulator driver for TPS65073 PMIC + * + * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DCDC's */ +#define TPS6507X_DCDC_1 0 +#define TPS6507X_DCDC_2 1 +#define TPS6507X_DCDC_3 2 +/* LDOs */ +#define TPS6507X_LDO_1 3 +#define TPS6507X_LDO_2 4 + +#define TPS6507X_MAX_REG_ID TPS6507X_LDO_2 + +/* Number of step-down converters available */ +#define TPS6507X_NUM_DCDC 3 +/* Number of LDO voltage regulators available */ +#define TPS6507X_NUM_LDO 2 +/* Number of total regulators available */ +#define TPS6507X_NUM_REGULATOR (TPS6507X_NUM_DCDC + TPS6507X_NUM_LDO) + +/* Supported voltage values for regulators (in microVolts) */ +static const unsigned int VDCDCx_VSEL_table[] = { + 725000, 750000, 775000, 800000, + 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, + 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, + 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, + 1425000, 1450000, 1475000, 1500000, + 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, + 2150000, 2200000, 2250000, 2300000, + 2350000, 2400000, 2450000, 2500000, + 2550000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, + 3000000, 3100000, 3200000, 3300000, +}; + +static const unsigned int LDO1_VSEL_table[] = { + 1000000, 1100000, 1200000, 1250000, + 1300000, 1350000, 1400000, 1500000, + 1600000, 1800000, 2500000, 2750000, + 2800000, 3000000, 3100000, 3300000, +}; + +/* The voltage mapping table for LDO2 is the same as VDCDCx */ +#define LDO2_VSEL_table VDCDCx_VSEL_table + +struct tps_info { + const char *name; + u8 table_len; + const unsigned int *table; + + /* Does DCDC high or the low register defines output voltage? */ + bool defdcdc_default; +}; + +static struct tps_info tps6507x_pmic_regs[] = { + { + .name = "VDCDC1", + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC2", + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC3", + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "LDO1", + .table_len = ARRAY_SIZE(LDO1_VSEL_table), + .table = LDO1_VSEL_table, + }, + { + .name = "LDO2", + .table_len = ARRAY_SIZE(LDO2_VSEL_table), + .table = LDO2_VSEL_table, + }, +}; + +struct tps6507x_pmic { + struct regulator_desc desc[TPS6507X_NUM_REGULATOR]; + struct tps6507x_dev *mfd; + struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR]; + struct tps_info *info[TPS6507X_NUM_REGULATOR]; + struct mutex io_lock; +}; +static inline int tps6507x_pmic_read(struct tps6507x_pmic *tps, u8 reg) +{ + u8 val; + int err; + + err = tps->mfd->read_dev(tps->mfd, reg, 1, &val); + + if (err) + return err; + + return val; +} + +static inline int tps6507x_pmic_write(struct tps6507x_pmic *tps, u8 reg, u8 val) +{ + return tps->mfd->write_dev(tps->mfd, reg, 1, &val); +} + +static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) +{ + int err, data; + + mutex_lock(&tps->io_lock); + + data = tps6507x_pmic_read(tps, reg); + if (data < 0) { + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data |= mask; + err = tps6507x_pmic_write(tps, reg, data); + if (err) + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) +{ + int err, data; + + mutex_lock(&tps->io_lock); + + data = tps6507x_pmic_read(tps, reg); + if (data < 0) { + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data &= ~mask; + err = tps6507x_pmic_write(tps, reg, data); + if (err) + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps6507x_pmic_reg_read(struct tps6507x_pmic *tps, u8 reg) +{ + int data; + + mutex_lock(&tps->io_lock); + + data = tps6507x_pmic_read(tps, reg); + if (data < 0) + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); + + mutex_unlock(&tps->io_lock); + return data; +} + +static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val) +{ + int err; + + mutex_lock(&tps->io_lock); + + err = tps6507x_pmic_write(tps, reg, val); + if (err < 0) + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps6507x_pmic_is_enabled(struct regulator_dev *dev) +{ + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); + int data, rid = rdev_get_id(dev); + u8 shift; + + if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - rid; + data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1); + + if (data < 0) + return data; + else + return (data & 1< TPS6507X_LDO_2) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - rid; + return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); +} + +static int tps6507x_pmic_disable(struct regulator_dev *dev) +{ + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); + int rid = rdev_get_id(dev); + u8 shift; + + if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - rid; + return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1, + 1 << shift); +} + +static int tps6507x_pmic_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); + int data, rid = rdev_get_id(dev); + u8 reg, mask; + + switch (rid) { + case TPS6507X_DCDC_1: + reg = TPS6507X_REG_DEFDCDC1; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_DCDC_2: + if (tps->info[rid]->defdcdc_default) + reg = TPS6507X_REG_DEFDCDC2_HIGH; + else + reg = TPS6507X_REG_DEFDCDC2_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_DCDC_3: + if (tps->info[rid]->defdcdc_default) + reg = TPS6507X_REG_DEFDCDC3_HIGH; + else + reg = TPS6507X_REG_DEFDCDC3_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_LDO_1: + reg = TPS6507X_REG_LDO_CTRL1; + mask = TPS6507X_REG_LDO_CTRL1_LDO1_MASK; + break; + case TPS6507X_LDO_2: + reg = TPS6507X_REG_DEFLDO2; + mask = TPS6507X_REG_DEFLDO2_LDO2_MASK; + break; + default: + return -EINVAL; + } + + data = tps6507x_pmic_reg_read(tps, reg); + if (data < 0) + return data; + + data &= mask; + return data; +} + +static int tps6507x_pmic_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); + int data, rid = rdev_get_id(dev); + u8 reg, mask; + + switch (rid) { + case TPS6507X_DCDC_1: + reg = TPS6507X_REG_DEFDCDC1; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_DCDC_2: + if (tps->info[rid]->defdcdc_default) + reg = TPS6507X_REG_DEFDCDC2_HIGH; + else + reg = TPS6507X_REG_DEFDCDC2_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_DCDC_3: + if (tps->info[rid]->defdcdc_default) + reg = TPS6507X_REG_DEFDCDC3_HIGH; + else + reg = TPS6507X_REG_DEFDCDC3_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_LDO_1: + reg = TPS6507X_REG_LDO_CTRL1; + mask = TPS6507X_REG_LDO_CTRL1_LDO1_MASK; + break; + case TPS6507X_LDO_2: + reg = TPS6507X_REG_DEFLDO2; + mask = TPS6507X_REG_DEFLDO2_LDO2_MASK; + break; + default: + return -EINVAL; + } + + data = tps6507x_pmic_reg_read(tps, reg); + if (data < 0) + return data; + + data &= ~mask; + data |= selector; + + return tps6507x_pmic_reg_write(tps, reg, data); +} + +static struct regulator_ops tps6507x_pmic_ops = { + .is_enabled = tps6507x_pmic_is_enabled, + .enable = tps6507x_pmic_enable, + .disable = tps6507x_pmic_disable, + .get_voltage_sel = tps6507x_pmic_get_voltage_sel, + .set_voltage_sel = tps6507x_pmic_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, +}; + +#ifdef CONFIG_OF +static struct of_regulator_match tps6507x_matches[] = { + { .name = "VDCDC1"}, + { .name = "VDCDC2"}, + { .name = "VDCDC3"}, + { .name = "LDO1"}, + { .name = "LDO2"}, +}; + +static struct tps6507x_board *tps6507x_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps6507x_reg_matches) +{ + struct tps6507x_board *tps_board; + struct device_node *np = pdev->dev.parent->of_node; + struct device_node *regulators; + struct of_regulator_match *matches; + static struct regulator_init_data *reg_data; + int idx = 0, count, ret; + + tps_board = devm_kzalloc(&pdev->dev, sizeof(*tps_board), + GFP_KERNEL); + if (!tps_board) { + dev_err(&pdev->dev, "Failure to alloc pdata for regulators.\n"); + return NULL; + } + + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_err(&pdev->dev, "regulator node not found\n"); + return NULL; + } + + count = ARRAY_SIZE(tps6507x_matches); + matches = tps6507x_matches; + + ret = of_regulator_match(&pdev->dev, regulators, matches, count); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", + ret); + return NULL; + } + + *tps6507x_reg_matches = matches; + + reg_data = devm_kzalloc(&pdev->dev, (sizeof(struct regulator_init_data) + * TPS6507X_NUM_REGULATOR), GFP_KERNEL); + if (!reg_data) { + dev_err(&pdev->dev, "Failure to alloc init data for regulators.\n"); + return NULL; + } + + tps_board->tps6507x_pmic_init_data = reg_data; + + for (idx = 0; idx < count; idx++) { + if (!matches[idx].init_data || !matches[idx].of_node) + continue; + + memcpy(®_data[idx], matches[idx].init_data, + sizeof(struct regulator_init_data)); + + } + + return tps_board; +} +#else +static inline struct tps6507x_board *tps6507x_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps6507x_reg_matches) +{ + *tps6507x_reg_matches = NULL; + return NULL; +} +#endif +static int tps6507x_pmic_probe(struct platform_device *pdev) +{ + struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); + struct tps_info *info = &tps6507x_pmic_regs[0]; + struct regulator_config config = { }; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + struct tps6507x_pmic *tps; + struct tps6507x_board *tps_board; + struct of_regulator_match *tps6507x_reg_matches = NULL; + int i; + int error; + unsigned int prop; + + /** + * tps_board points to pmic related constants + * coming from the board-evm file. + */ + + tps_board = dev_get_platdata(tps6507x_dev->dev); + if (!tps_board && tps6507x_dev->dev->of_node) + tps_board = tps6507x_parse_dt_reg_data(pdev, + &tps6507x_reg_matches); + if (!tps_board) + return -EINVAL; + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + init_data = tps_board->tps6507x_pmic_init_data; + if (!init_data) + return -EINVAL; + + tps = devm_kzalloc(&pdev->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + mutex_init(&tps->io_lock); + + /* common for all regulators */ + tps->mfd = tps6507x_dev; + + for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) { + /* Register the regulators */ + tps->info[i] = info; + if (init_data->driver_data) { + struct tps6507x_reg_platform_data *data = + init_data->driver_data; + tps->info[i]->defdcdc_default = data->defdcdc_default; + } + + tps->desc[i].name = info->name; + tps->desc[i].id = i; + tps->desc[i].n_voltages = info->table_len; + tps->desc[i].volt_table = info->table; + tps->desc[i].ops = &tps6507x_pmic_ops; + tps->desc[i].type = REGULATOR_VOLTAGE; + tps->desc[i].owner = THIS_MODULE; + + config.dev = tps6507x_dev->dev; + config.init_data = init_data; + config.driver_data = tps; + + if (tps6507x_reg_matches) { + error = of_property_read_u32( + tps6507x_reg_matches[i].of_node, + "ti,defdcdc_default", &prop); + + if (!error) + tps->info[i]->defdcdc_default = prop; + + config.of_node = tps6507x_reg_matches[i].of_node; + } + + rdev = regulator_register(&tps->desc[i], &config); + if (IS_ERR(rdev)) { + dev_err(tps6507x_dev->dev, + "failed to register %s regulator\n", + pdev->name); + error = PTR_ERR(rdev); + goto fail; + } + + /* Save regulator for cleanup */ + tps->rdev[i] = rdev; + } + + tps6507x_dev->pmic = tps; + platform_set_drvdata(pdev, tps6507x_dev); + + return 0; + +fail: + while (--i >= 0) + regulator_unregister(tps->rdev[i]); + return error; +} + +static int tps6507x_pmic_remove(struct platform_device *pdev) +{ + struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); + struct tps6507x_pmic *tps = tps6507x_dev->pmic; + int i; + + for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) + regulator_unregister(tps->rdev[i]); + return 0; +} + +static struct platform_driver tps6507x_pmic_driver = { + .driver = { + .name = "tps6507x-pmic", + .owner = THIS_MODULE, + }, + .probe = tps6507x_pmic_probe, + .remove = tps6507x_pmic_remove, +}; + +static int __init tps6507x_pmic_init(void) +{ + return platform_driver_register(&tps6507x_pmic_driver); +} +subsys_initcall(tps6507x_pmic_init); + +static void __exit tps6507x_pmic_cleanup(void) +{ + platform_driver_unregister(&tps6507x_pmic_driver); +} +module_exit(tps6507x_pmic_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6507x-pmic"); diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c new file mode 100644 index 0000000..c8e7045 --- /dev/null +++ b/drivers/regulator/tps65090-regulator.c @@ -0,0 +1,368 @@ +/* + * Regulator driver for tps65090 power management chip. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tps65090_regulator { + struct device *dev; + struct regulator_desc *desc; + struct regulator_dev *rdev; +}; + +static struct regulator_ops tps65090_ext_control_ops = { +}; + +static struct regulator_ops tps65090_reg_contol_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops tps65090_ldo_ops = { +}; + +#define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \ +{ \ + .name = "TPS65090_RAILS"#_id, \ + .supply_name = _sname, \ + .id = TPS65090_REGULATOR_##_id, \ + .ops = &_ops, \ + .enable_reg = _en_reg, \ + .enable_mask = BIT(0), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + +static struct regulator_desc tps65090_regulator_desc[] = { + tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_reg_contol_ops), + tps65090_REG_DESC(LDO1, "vsys-l1", 0, tps65090_ldo_ops), + tps65090_REG_DESC(LDO2, "vsys-l2", 0, tps65090_ldo_ops), +}; + +static inline bool is_dcdc(int id) +{ + switch (id) { + case TPS65090_REGULATOR_DCDC1: + case TPS65090_REGULATOR_DCDC2: + case TPS65090_REGULATOR_DCDC3: + return true; + default: + return false; + } +} + +static int tps65090_config_ext_control( + struct tps65090_regulator *ri, bool enable) +{ + int ret; + struct device *parent = ri->dev->parent; + unsigned int reg_en_reg = ri->desc->enable_reg; + + if (enable) + ret = tps65090_set_bits(parent, reg_en_reg, 1); + else + ret = tps65090_clr_bits(parent, reg_en_reg, 1); + if (ret < 0) + dev_err(ri->dev, "Error in updating reg 0x%x\n", reg_en_reg); + return ret; +} + +static int tps65090_regulator_disable_ext_control( + struct tps65090_regulator *ri, + struct tps65090_regulator_plat_data *tps_pdata) +{ + int ret = 0; + struct device *parent = ri->dev->parent; + unsigned int reg_en_reg = ri->desc->enable_reg; + + /* + * First enable output for internal control if require. + * And then disable external control. + */ + if (tps_pdata->reg_init_data->constraints.always_on || + tps_pdata->reg_init_data->constraints.boot_on) { + ret = tps65090_set_bits(parent, reg_en_reg, 0); + if (ret < 0) { + dev_err(ri->dev, "Error in set reg 0x%x\n", reg_en_reg); + return ret; + } + } + return tps65090_config_ext_control(ri, false); +} + +static void tps65090_configure_regulator_config( + struct tps65090_regulator_plat_data *tps_pdata, + struct regulator_config *config) +{ + if (gpio_is_valid(tps_pdata->gpio)) { + int gpio_flag = GPIOF_OUT_INIT_LOW; + + if (tps_pdata->reg_init_data->constraints.always_on || + tps_pdata->reg_init_data->constraints.boot_on) + gpio_flag = GPIOF_OUT_INIT_HIGH; + + config->ena_gpio = tps_pdata->gpio; + config->ena_gpio_flags = gpio_flag; + } +} + +#ifdef CONFIG_OF +static struct of_regulator_match tps65090_matches[] = { + { .name = "dcdc1", }, + { .name = "dcdc2", }, + { .name = "dcdc3", }, + { .name = "fet1", }, + { .name = "fet2", }, + { .name = "fet3", }, + { .name = "fet4", }, + { .name = "fet5", }, + { .name = "fet6", }, + { .name = "fet7", }, + { .name = "ldo1", }, + { .name = "ldo2", }, +}; + +static struct tps65090_platform_data *tps65090_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps65090_reg_matches) +{ + struct tps65090_platform_data *tps65090_pdata; + struct device_node *np = pdev->dev.parent->of_node; + struct device_node *regulators; + int idx = 0, ret; + struct tps65090_regulator_plat_data *reg_pdata; + + tps65090_pdata = devm_kzalloc(&pdev->dev, sizeof(*tps65090_pdata), + GFP_KERNEL); + if (!tps65090_pdata) { + dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n"); + return ERR_PTR(-ENOMEM); + } + + reg_pdata = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * + sizeof(*reg_pdata), GFP_KERNEL); + if (!reg_pdata) { + dev_err(&pdev->dev, "Memory alloc for reg_pdata failed\n"); + return ERR_PTR(-ENOMEM); + } + + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_err(&pdev->dev, "regulator node not found\n"); + return ERR_PTR(-ENODEV); + } + + ret = of_regulator_match(&pdev->dev, regulators, tps65090_matches, + ARRAY_SIZE(tps65090_matches)); + if (ret < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", ret); + return ERR_PTR(ret); + } + + *tps65090_reg_matches = tps65090_matches; + for (idx = 0; idx < ARRAY_SIZE(tps65090_matches); idx++) { + struct regulator_init_data *ri_data; + struct tps65090_regulator_plat_data *rpdata; + + rpdata = ®_pdata[idx]; + ri_data = tps65090_matches[idx].init_data; + if (!ri_data || !tps65090_matches[idx].of_node) + continue; + + rpdata->reg_init_data = ri_data; + rpdata->enable_ext_control = of_property_read_bool( + tps65090_matches[idx].of_node, + "ti,enable-ext-control"); + if (rpdata->enable_ext_control) + rpdata->gpio = of_get_named_gpio(np, + "dcdc-ext-control-gpios", 0); + + tps65090_pdata->reg_pdata[idx] = rpdata; + } + return tps65090_pdata; +} +#else +static inline struct tps65090_platform_data *tps65090_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps65090_reg_matches) +{ + *tps65090_reg_matches = NULL; + return NULL; +} +#endif + +static int tps65090_regulator_probe(struct platform_device *pdev) +{ + struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); + struct tps65090_regulator *ri = NULL; + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct tps65090_regulator_plat_data *tps_pdata; + struct tps65090_regulator *pmic; + struct tps65090_platform_data *tps65090_pdata; + struct of_regulator_match *tps65090_reg_matches = NULL; + int num; + int ret; + + dev_dbg(&pdev->dev, "Probing regulator\n"); + + tps65090_pdata = dev_get_platdata(pdev->dev.parent); + if (!tps65090_pdata && tps65090_mfd->dev->of_node) + tps65090_pdata = tps65090_parse_dt_reg_data(pdev, + &tps65090_reg_matches); + if (IS_ERR_OR_NULL(tps65090_pdata)) { + dev_err(&pdev->dev, "Platform data missing\n"); + return tps65090_pdata ? PTR_ERR(tps65090_pdata) : -EINVAL; + } + + pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), + GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + return -ENOMEM; + } + + for (num = 0; num < TPS65090_REGULATOR_MAX; num++) { + tps_pdata = tps65090_pdata->reg_pdata[num]; + + ri = &pmic[num]; + ri->dev = &pdev->dev; + ri->desc = &tps65090_regulator_desc[num]; + + /* + * TPS5090 DCDC support the control from external digital input. + * Configure it as per platform data. + */ + if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) { + if (tps_pdata->enable_ext_control) { + tps65090_configure_regulator_config( + tps_pdata, &config); + ri->desc->ops = &tps65090_ext_control_ops; + } else { + ret = tps65090_regulator_disable_ext_control( + ri, tps_pdata); + if (ret < 0) { + dev_err(&pdev->dev, + "failed disable ext control\n"); + goto scrub; + } + } + } + + config.dev = pdev->dev.parent; + config.driver_data = ri; + config.regmap = tps65090_mfd->rmap; + if (tps_pdata) + config.init_data = tps_pdata->reg_init_data; + else + config.init_data = NULL; + if (tps65090_reg_matches) + config.of_node = tps65090_reg_matches[num].of_node; + else + config.of_node = NULL; + + rdev = regulator_register(ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc->name); + ret = PTR_ERR(rdev); + goto scrub; + } + ri->rdev = rdev; + + /* Enable external control if it is require */ + if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && + tps_pdata->enable_ext_control) { + ret = tps65090_config_ext_control(ri, true); + if (ret < 0) { + /* Increment num to get unregister rdev */ + num++; + goto scrub; + } + } + } + + platform_set_drvdata(pdev, pmic); + return 0; + +scrub: + while (--num >= 0) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return ret; +} + +static int tps65090_regulator_remove(struct platform_device *pdev) +{ + struct tps65090_regulator *pmic = platform_get_drvdata(pdev); + struct tps65090_regulator *ri; + int num; + + for (num = 0; num < TPS65090_REGULATOR_MAX; ++num) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return 0; +} + +static struct platform_driver tps65090_regulator_driver = { + .driver = { + .name = "tps65090-pmic", + .owner = THIS_MODULE, + }, + .probe = tps65090_regulator_probe, + .remove = tps65090_regulator_remove, +}; + +static int __init tps65090_regulator_init(void) +{ + return platform_driver_register(&tps65090_regulator_driver); +} +subsys_initcall(tps65090_regulator_init); + +static void __exit tps65090_regulator_exit(void) +{ + platform_driver_unregister(&tps65090_regulator_driver); +} +module_exit(tps65090_regulator_exit); + +MODULE_DESCRIPTION("tps65090 regulator driver"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65090-pmic"); diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c new file mode 100644 index 0000000..df39518 --- /dev/null +++ b/drivers/regulator/tps65217-regulator.c @@ -0,0 +1,436 @@ +/* + * tps65217-regulator.c + * + * Regulator driver for TPS65217 PMIC + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \ + { \ + .name = _name, \ + .id = _id, \ + .ops = &_ops, \ + .n_voltages = _n, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = TPS65217_REG_ENABLE, \ + .enable_mask = _em, \ + .volt_table = _t, \ + } \ + +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \ + { \ + .name = _nm, \ + .min_uV = _min, \ + .max_uV = _max, \ + .vsel_to_uv = _f1, \ + .uv_to_vsel = _f2, \ + } + +static const unsigned int LDO1_VSEL_table[] = { + 1000000, 1100000, 1200000, 1250000, + 1300000, 1350000, 1400000, 1500000, + 1600000, 1800000, 2500000, 2750000, + 2800000, 3000000, 3100000, 3300000, +}; + +static int tps65217_vsel_to_uv1(unsigned int vsel) +{ + int uV = 0; + + if (vsel > 63) + return -EINVAL; + + if (vsel <= 24) + uV = vsel * 25000 + 900000; + else if (vsel <= 52) + uV = (vsel - 24) * 50000 + 1500000; + else if (vsel < 56) + uV = (vsel - 52) * 100000 + 2900000; + else + uV = 3300000; + + return uV; +} + +static int tps65217_uv_to_vsel1(int uV, unsigned int *vsel) +{ + if (uV < 0 || uV > 3300000) + return -EINVAL; + + if (uV <= 1500000) + *vsel = DIV_ROUND_UP(uV - 900000, 25000); + else if (uV <= 2900000) + *vsel = 24 + DIV_ROUND_UP(uV - 1500000, 50000); + else if (uV < 3300000) + *vsel = 52 + DIV_ROUND_UP(uV - 2900000, 100000); + else + *vsel = 56; + + return 0; +} + +static int tps65217_vsel_to_uv2(unsigned int vsel) +{ + int uV = 0; + + if (vsel > 31) + return -EINVAL; + + if (vsel <= 8) + uV = vsel * 50000 + 1500000; + else if (vsel <= 13) + uV = (vsel - 8) * 100000 + 1900000; + else + uV = (vsel - 13) * 50000 + 2400000; + + return uV; +} + +static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) +{ + if (uV < 0 || uV > 3300000) + return -EINVAL; + + if (uV <= 1900000) + *vsel = DIV_ROUND_UP(uV - 1500000, 50000); + else if (uV <= 2400000) + *vsel = 8 + DIV_ROUND_UP(uV - 1900000, 100000); + else + *vsel = 13 + DIV_ROUND_UP(uV - 2400000, 50000); + + return 0; +} + +static struct tps_info tps65217_pmic_regs[] = { + TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1), + TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1), + TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1), + TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL), + TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1), + TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, + tps65217_uv_to_vsel2), + TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, + tps65217_uv_to_vsel2), +}; + +static int tps65217_pmic_enable(struct regulator_dev *dev) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + /* Enable the regulator and password protection is level 1 */ + return tps65217_set_bits(tps, TPS65217_REG_ENABLE, + dev->desc->enable_mask, dev->desc->enable_mask, + TPS65217_PROTECT_L1); +} + +static int tps65217_pmic_disable(struct regulator_dev *dev) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + /* Disable the regulator and password protection is level 1 */ + return tps65217_clear_bits(tps, TPS65217_REG_ENABLE, + dev->desc->enable_mask, TPS65217_PROTECT_L1); +} + +static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + int ret; + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + /* Set the voltage based on vsel value and write protect level is 2 */ + ret = tps65217_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask, + selector, TPS65217_PROTECT_L2); + + /* Set GO bit for DCDCx to initiate voltage transistion */ + switch (rid) { + case TPS65217_DCDC_1 ... TPS65217_DCDC_3: + ret = tps65217_set_bits(tps, TPS65217_REG_DEFSLEW, + TPS65217_DEFSLEW_GO, TPS65217_DEFSLEW_GO, + TPS65217_PROTECT_L2); + break; + } + + return ret; +} + +static int tps65217_pmic_map_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int sel, rid = rdev_get_id(dev); + int ret; + + /* LDO1 uses regulator_map_voltage_iterate() */ + if (rid == TPS65217_LDO_1) + return -EINVAL; + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + if (min_uV < tps->info[rid]->min_uV) + min_uV = tps->info[rid]->min_uV; + + if (max_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV) + return -EINVAL; + + ret = tps->info[rid]->uv_to_vsel(min_uV, &sel); + if (ret) + return ret; + + return sel; +} + +static int tps65217_pmic_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + if (selector >= dev->desc->n_voltages) + return -EINVAL; + + return tps->info[rid]->vsel_to_uv(selector); +} + +/* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ +static struct regulator_ops tps65217_pmic_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps65217_pmic_enable, + .disable = tps65217_pmic_disable, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = tps65217_pmic_set_voltage_sel, + .list_voltage = tps65217_pmic_list_voltage, + .map_voltage = tps65217_pmic_map_voltage, +}; + +/* Operations permitted on LDO1 */ +static struct regulator_ops tps65217_pmic_ldo1_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps65217_pmic_enable, + .disable = tps65217_pmic_disable, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = tps65217_pmic_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static const struct regulator_desc regulators[] = { + TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC1_EN, NULL), + TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC2_EN, NULL), + TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC3_EN, NULL), + TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, + TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, + TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table), + TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, + TPS65217_ENABLE_LDO2_EN, NULL), + TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, + NULL), + TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, + NULL), +}; + +#ifdef CONFIG_OF +static struct of_regulator_match reg_matches[] = { + { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 }, + { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 }, + { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 }, + { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 }, + { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 }, + { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 }, + { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 }, +}; + +static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) +{ + struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); + struct device_node *node = tps->dev->of_node; + struct tps65217_board *pdata; + struct device_node *regs; + int i, count; + + regs = of_find_node_by_name(node, "regulators"); + if (!regs) + return NULL; + + count = of_regulator_match(&pdev->dev, regs, reg_matches, + TPS65217_NUM_REGULATOR); + of_node_put(regs); + if ((count < 0) || (count > TPS65217_NUM_REGULATOR)) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + for (i = 0; i < count; i++) { + if (!reg_matches[i].init_data || !reg_matches[i].of_node) + continue; + + pdata->tps65217_init_data[i] = reg_matches[i].init_data; + pdata->of_node[i] = reg_matches[i].of_node; + } + + return pdata; +} +#else +static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int tps65217_regulator_probe(struct platform_device *pdev) +{ + struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); + struct tps65217_board *pdata = dev_get_platdata(tps->dev); + struct regulator_init_data *reg_data; + struct regulator_dev *rdev; + struct regulator_config config = { }; + int i, ret; + + if (tps->dev->of_node) + pdata = tps65217_parse_dt(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "Platform data not found\n"); + return -EINVAL; + } + + if (tps65217_chip_id(tps) != TPS65217) { + dev_err(&pdev->dev, "Invalid tps chip version\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, tps); + + for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { + + reg_data = pdata->tps65217_init_data[i]; + + /* + * Regulator API handles empty constraints but not NULL + * constraints + */ + if (!reg_data) + continue; + + /* Register the regulators */ + tps->info[i] = &tps65217_pmic_regs[i]; + + config.dev = tps->dev; + config.init_data = reg_data; + config.driver_data = tps; + config.regmap = tps->regmap; + if (tps->dev->of_node) + config.of_node = pdata->of_node[i]; + + rdev = regulator_register(®ulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "failed to register %s regulator\n", + pdev->name); + ret = PTR_ERR(rdev); + goto err_unregister_regulator; + } + + /* Save regulator for cleanup */ + tps->rdev[i] = rdev; + } + return 0; + +err_unregister_regulator: + while (--i >= 0) + regulator_unregister(tps->rdev[i]); + + return ret; +} + +static int tps65217_regulator_remove(struct platform_device *pdev) +{ + struct tps65217 *tps = platform_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < TPS65217_NUM_REGULATOR; i++) + regulator_unregister(tps->rdev[i]); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver tps65217_regulator_driver = { + .driver = { + .name = "tps65217-pmic", + }, + .probe = tps65217_regulator_probe, + .remove = tps65217_regulator_remove, +}; + +static int __init tps65217_regulator_init(void) +{ + return platform_driver_register(&tps65217_regulator_driver); +} +subsys_initcall(tps65217_regulator_init); + +static void __exit tps65217_regulator_exit(void) +{ + platform_driver_unregister(&tps65217_regulator_driver); +} +module_exit(tps65217_regulator_exit); + +MODULE_AUTHOR("AnilKumar Ch "); +MODULE_DESCRIPTION("TPS65217 voltage regulator driver"); +MODULE_ALIAS("platform:tps65217-pmic"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c new file mode 100644 index 0000000..1094393 --- /dev/null +++ b/drivers/regulator/tps6524x-regulator.c @@ -0,0 +1,664 @@ +/* + * Regulator driver for TPS6524x PMIC + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_LDO_SET 0x0 +#define LDO_ILIM_MASK 1 /* 0 = 400-800, 1 = 900-1500 */ +#define LDO_VSEL_MASK 0x0f +#define LDO2_ILIM_SHIFT 12 +#define LDO2_VSEL_SHIFT 4 +#define LDO1_ILIM_SHIFT 8 +#define LDO1_VSEL_SHIFT 0 + +#define REG_BLOCK_EN 0x1 +#define BLOCK_MASK 1 +#define BLOCK_LDO1_SHIFT 0 +#define BLOCK_LDO2_SHIFT 1 +#define BLOCK_LCD_SHIFT 2 +#define BLOCK_USB_SHIFT 3 + +#define REG_DCDC_SET 0x2 +#define DCDC_VDCDC_MASK 0x1f +#define DCDC_VDCDC1_SHIFT 0 +#define DCDC_VDCDC2_SHIFT 5 +#define DCDC_VDCDC3_SHIFT 10 + +#define REG_DCDC_EN 0x3 +#define DCDCDCDC_EN_MASK 0x1 +#define DCDCDCDC1_EN_SHIFT 0 +#define DCDCDCDC1_PG_MSK BIT(1) +#define DCDCDCDC2_EN_SHIFT 2 +#define DCDCDCDC2_PG_MSK BIT(3) +#define DCDCDCDC3_EN_SHIFT 4 +#define DCDCDCDC3_PG_MSK BIT(5) + +#define REG_USB 0x4 +#define USB_ILIM_SHIFT 0 +#define USB_ILIM_MASK 0x3 +#define USB_TSD_SHIFT 2 +#define USB_TSD_MASK 0x3 +#define USB_TWARN_SHIFT 4 +#define USB_TWARN_MASK 0x3 +#define USB_IWARN_SD BIT(6) +#define USB_FAST_LOOP BIT(7) + +#define REG_ALARM 0x5 +#define ALARM_LDO1 BIT(0) +#define ALARM_DCDC1 BIT(1) +#define ALARM_DCDC2 BIT(2) +#define ALARM_DCDC3 BIT(3) +#define ALARM_LDO2 BIT(4) +#define ALARM_USB_WARN BIT(5) +#define ALARM_USB_ALARM BIT(6) +#define ALARM_LCD BIT(9) +#define ALARM_TEMP_WARM BIT(10) +#define ALARM_TEMP_HOT BIT(11) +#define ALARM_NRST BIT(14) +#define ALARM_POWERUP BIT(15) + +#define REG_INT_ENABLE 0x6 +#define INT_LDO1 BIT(0) +#define INT_DCDC1 BIT(1) +#define INT_DCDC2 BIT(2) +#define INT_DCDC3 BIT(3) +#define INT_LDO2 BIT(4) +#define INT_USB_WARN BIT(5) +#define INT_USB_ALARM BIT(6) +#define INT_LCD BIT(9) +#define INT_TEMP_WARM BIT(10) +#define INT_TEMP_HOT BIT(11) +#define INT_GLOBAL_EN BIT(15) + +#define REG_INT_STATUS 0x7 +#define STATUS_LDO1 BIT(0) +#define STATUS_DCDC1 BIT(1) +#define STATUS_DCDC2 BIT(2) +#define STATUS_DCDC3 BIT(3) +#define STATUS_LDO2 BIT(4) +#define STATUS_USB_WARN BIT(5) +#define STATUS_USB_ALARM BIT(6) +#define STATUS_LCD BIT(9) +#define STATUS_TEMP_WARM BIT(10) +#define STATUS_TEMP_HOT BIT(11) + +#define REG_SOFTWARE_RESET 0xb +#define REG_WRITE_ENABLE 0xd +#define REG_REV_ID 0xf + +#define N_DCDC 3 +#define N_LDO 2 +#define N_SWITCH 2 +#define N_REGULATORS (N_DCDC + N_LDO + N_SWITCH) + +#define CMD_READ(reg) ((reg) << 6) +#define CMD_WRITE(reg) (BIT(5) | (reg) << 6) +#define STAT_CLK BIT(3) +#define STAT_WRITE BIT(2) +#define STAT_INVALID BIT(1) +#define STAT_WP BIT(0) + +struct field { + int reg; + int shift; + int mask; +}; + +struct supply_info { + const char *name; + int n_voltages; + const unsigned int *voltages; + int n_ilimsels; + const unsigned int *ilimsels; + struct field enable, voltage, ilimsel; +}; + +struct tps6524x { + struct device *dev; + struct spi_device *spi; + struct mutex lock; + struct regulator_desc desc[N_REGULATORS]; + struct regulator_dev *rdev[N_REGULATORS]; +}; + +static int __read_reg(struct tps6524x *hw, int reg) +{ + int error = 0; + u16 cmd = CMD_READ(reg), in; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = ∈ + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "read reg %d, data %x, status %x\n", + reg, in, status); + + if (!(status & STAT_CLK) || (status & STAT_WRITE)) + return -EIO; + + if (status & STAT_INVALID) + return -EINVAL; + + return in; +} + +static int read_reg(struct tps6524x *hw, int reg) +{ + int ret; + + mutex_lock(&hw->lock); + ret = __read_reg(hw, reg); + mutex_unlock(&hw->lock); + + return ret; +} + +static int __write_reg(struct tps6524x *hw, int reg, int val) +{ + int error = 0; + u16 cmd = CMD_WRITE(reg), out = val; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &out; + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "wrote reg %d, data %x, status %x\n", + reg, out, status); + + if (!(status & STAT_CLK) || !(status & STAT_WRITE)) + return -EIO; + + if (status & (STAT_INVALID | STAT_WP)) + return -EINVAL; + + return error; +} + +static int __rmw_reg(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + ret = __read_reg(hw, reg); + if (ret < 0) + return ret; + + ret &= ~mask; + ret |= val; + + ret = __write_reg(hw, reg, ret); + + return (ret < 0) ? ret : 0; +} + +static int rmw_protect(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + mutex_lock(&hw->lock); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 1); + if (ret) { + dev_err(hw->dev, "failed to set write enable\n"); + goto error; + } + + ret = __rmw_reg(hw, reg, mask, val); + if (ret) + dev_err(hw->dev, "failed to rmw register %d\n", reg); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 0); + if (ret) { + dev_err(hw->dev, "failed to clear write enable\n"); + goto error; + } + +error: + mutex_unlock(&hw->lock); + + return ret; +} + +static int read_field(struct tps6524x *hw, const struct field *field) +{ + int tmp; + + tmp = read_reg(hw, field->reg); + if (tmp < 0) + return tmp; + + return (tmp >> field->shift) & field->mask; +} + +static int write_field(struct tps6524x *hw, const struct field *field, + int val) +{ + if (val & ~field->mask) + return -EOVERFLOW; + + return rmw_protect(hw, field->reg, + field->mask << field->shift, + val << field->shift); +} + +static const unsigned int dcdc1_voltages[] = { + 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, +}; + +static const unsigned int dcdc2_voltages[] = { + 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, + 1800000, 1850000, 1900000, 1950000, + 2000000, 2050000, 2100000, 2150000, + 2200000, 2250000, 2300000, 2350000, + 2400000, 2450000, 2500000, 2550000, + 2600000, 2650000, 2700000, 2750000, + 2800000, 2850000, 2900000, 2950000, +}; + +static const unsigned int dcdc3_voltages[] = { + 2400000, 2450000, 2500000, 2550000, 2600000, + 2650000, 2700000, 2750000, 2800000, 2850000, + 2900000, 2950000, 3000000, 3050000, 3100000, + 3150000, 3200000, 3250000, 3300000, 3350000, + 3400000, 3450000, 3500000, 3550000, 3600000, +}; + +static const unsigned int ldo1_voltages[] = { + 4300000, 4350000, 4400000, 4450000, + 4500000, 4550000, 4600000, 4650000, + 4700000, 4750000, 4800000, 4850000, + 4900000, 4950000, 5000000, 5050000, +}; + +static const unsigned int ldo2_voltages[] = { + 1100000, 1150000, 1200000, 1250000, + 1300000, 1700000, 1750000, 1800000, + 1850000, 1900000, 3150000, 3200000, + 3250000, 3300000, 3350000, 3400000, +}; + +static const unsigned int fixed_5000000_voltage[] = { + 5000000 +}; + +static const unsigned int ldo_ilimsel[] = { + 400000, 1500000 +}; + +static const unsigned int usb_ilimsel[] = { + 200000, 400000, 800000, 1000000 +}; + +static const unsigned int fixed_2400000_ilimsel[] = { + 2400000 +}; + +static const unsigned int fixed_1200000_ilimsel[] = { + 1200000 +}; + +static const unsigned int fixed_400000_ilimsel[] = { + 400000 +}; + +#define __MK_FIELD(_reg, _mask, _shift) \ + { .reg = (_reg), .mask = (_mask), .shift = (_shift), } + +static const struct supply_info supply_info[N_REGULATORS] = { + { + .name = "DCDC1", + .n_voltages = ARRAY_SIZE(dcdc1_voltages), + .voltages = dcdc1_voltages, + .n_ilimsels = ARRAY_SIZE(fixed_2400000_ilimsel), + .ilimsels = fixed_2400000_ilimsel, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC1_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC1_SHIFT), + }, + { + .name = "DCDC2", + .n_voltages = ARRAY_SIZE(dcdc2_voltages), + .voltages = dcdc2_voltages, + .n_ilimsels = ARRAY_SIZE(fixed_1200000_ilimsel), + .ilimsels = fixed_1200000_ilimsel, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC2_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC2_SHIFT), + }, + { + .name = "DCDC3", + .n_voltages = ARRAY_SIZE(dcdc3_voltages), + .voltages = dcdc3_voltages, + .n_ilimsels = ARRAY_SIZE(fixed_1200000_ilimsel), + .ilimsels = fixed_1200000_ilimsel, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC3_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC3_SHIFT), + }, + { + .name = "LDO1", + .n_voltages = ARRAY_SIZE(ldo1_voltages), + .voltages = ldo1_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO1_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO1_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO1_ILIM_SHIFT), + }, + { + .name = "LDO2", + .n_voltages = ARRAY_SIZE(ldo2_voltages), + .voltages = ldo2_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO2_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO2_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO2_ILIM_SHIFT), + }, + { + .name = "USB", + .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), + .voltages = fixed_5000000_voltage, + .n_ilimsels = ARRAY_SIZE(usb_ilimsel), + .ilimsels = usb_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_USB_SHIFT), + .ilimsel = __MK_FIELD(REG_USB, USB_ILIM_MASK, + USB_ILIM_SHIFT), + }, + { + .name = "LCD", + .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), + .voltages = fixed_5000000_voltage, + .n_ilimsels = ARRAY_SIZE(fixed_400000_ilimsel), + .ilimsels = fixed_400000_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LCD_SHIFT), + }, +}; + +static int set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (rdev->desc->n_voltages == 1) + return -EINVAL; + + return write_field(hw, &info->voltage, selector); +} + +static int get_voltage_sel(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (rdev->desc->n_voltages == 1) + return 0; + + ret = read_field(hw, &info->voltage); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_voltages)) + return -EIO; + + return ret; +} + +static int set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + const struct supply_info *info; + struct tps6524x *hw; + int i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->n_ilimsels == 1) + return -EINVAL; + + for (i = info->n_ilimsels - 1; i >= 0; i--) { + if (min_uA <= info->ilimsels[i] && + max_uA >= info->ilimsels[i]) + return write_field(hw, &info->ilimsel, i); + } + + return -EINVAL; +} + +static int get_current_limit(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->n_ilimsels == 1) + return info->ilimsels[0]; + + ret = read_field(hw, &info->ilimsel); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_ilimsels)) + return -EIO; + + return info->ilimsels[ret]; +} + +static int enable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 1); +} + +static int disable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 0); +} + +static int is_supply_enabled(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return read_field(hw, &info->enable); +} + +static struct regulator_ops regulator_ops = { + .is_enabled = is_supply_enabled, + .enable = enable_supply, + .disable = disable_supply, + .get_voltage_sel = get_voltage_sel, + .set_voltage_sel = set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_current_limit = set_current_limit, + .get_current_limit = get_current_limit, +}; + +static int pmic_remove(struct spi_device *spi) +{ + struct tps6524x *hw = spi_get_drvdata(spi); + int i; + + if (!hw) + return 0; + for (i = 0; i < N_REGULATORS; i++) { + regulator_unregister(hw->rdev[i]); + hw->rdev[i] = NULL; + } + spi_set_drvdata(spi, NULL); + return 0; +} + +static int pmic_probe(struct spi_device *spi) +{ + struct tps6524x *hw; + struct device *dev = &spi->dev; + const struct supply_info *info = supply_info; + struct regulator_init_data *init_data; + struct regulator_config config = { }; + int ret = 0, i; + + init_data = dev->platform_data; + if (!init_data) { + dev_err(dev, "could not find regulator platform data\n"); + return -EINVAL; + } + + hw = devm_kzalloc(&spi->dev, sizeof(struct tps6524x), GFP_KERNEL); + if (!hw) { + dev_err(dev, "cannot allocate regulator private data\n"); + return -ENOMEM; + } + spi_set_drvdata(spi, hw); + + memset(hw, 0, sizeof(struct tps6524x)); + hw->dev = dev; + hw->spi = spi_dev_get(spi); + mutex_init(&hw->lock); + + for (i = 0; i < N_REGULATORS; i++, info++, init_data++) { + hw->desc[i].name = info->name; + hw->desc[i].id = i; + hw->desc[i].n_voltages = info->n_voltages; + hw->desc[i].volt_table = info->voltages; + hw->desc[i].ops = ®ulator_ops; + hw->desc[i].type = REGULATOR_VOLTAGE; + hw->desc[i].owner = THIS_MODULE; + + config.dev = dev; + config.init_data = init_data; + config.driver_data = hw; + + hw->rdev[i] = regulator_register(&hw->desc[i], &config); + if (IS_ERR(hw->rdev[i])) { + ret = PTR_ERR(hw->rdev[i]); + hw->rdev[i] = NULL; + goto fail; + } + } + + return 0; + +fail: + pmic_remove(spi); + return ret; +} + +static struct spi_driver pmic_driver = { + .probe = pmic_probe, + .remove = pmic_remove, + .driver = { + .name = "tps6524x", + .owner = THIS_MODULE, + }, +}; + +module_spi_driver(pmic_driver); + +MODULE_DESCRIPTION("TPS6524X PMIC Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tps6524x"); diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c new file mode 100644 index 0000000..2c9155b --- /dev/null +++ b/drivers/regulator/tps6586x-regulator.c @@ -0,0 +1,464 @@ +/* + * Regulator driver for TI TPS6586x + * + * Copyright (C) 2010 Compulab Ltd. + * Author: Mike Rapoport + * + * Based on da903x + * Copyright (C) 2006-2008 Marvell International Ltd. + * Copyright (C) 2008 Compulab Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* supply control and voltage setting */ +#define TPS6586X_SUPPLYENA 0x10 +#define TPS6586X_SUPPLYENB 0x11 +#define TPS6586X_SUPPLYENC 0x12 +#define TPS6586X_SUPPLYEND 0x13 +#define TPS6586X_SUPPLYENE 0x14 +#define TPS6586X_VCC1 0x20 +#define TPS6586X_VCC2 0x21 +#define TPS6586X_SM1V1 0x23 +#define TPS6586X_SM1V2 0x24 +#define TPS6586X_SM1SL 0x25 +#define TPS6586X_SM0V1 0x26 +#define TPS6586X_SM0V2 0x27 +#define TPS6586X_SM0SL 0x28 +#define TPS6586X_LDO2AV1 0x29 +#define TPS6586X_LDO2AV2 0x2A +#define TPS6586X_LDO2BV1 0x2F +#define TPS6586X_LDO2BV2 0x30 +#define TPS6586X_LDO4V1 0x32 +#define TPS6586X_LDO4V2 0x33 + +/* converter settings */ +#define TPS6586X_SUPPLYV1 0x41 +#define TPS6586X_SUPPLYV2 0x42 +#define TPS6586X_SUPPLYV3 0x43 +#define TPS6586X_SUPPLYV4 0x44 +#define TPS6586X_SUPPLYV5 0x45 +#define TPS6586X_SUPPLYV6 0x46 +#define TPS6586X_SMODE1 0x47 +#define TPS6586X_SMODE2 0x48 + +struct tps6586x_regulator { + struct regulator_desc desc; + + int enable_bit[2]; + int enable_reg[2]; +}; + +static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent; +} + +static struct regulator_ops tps6586x_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +static struct regulator_ops tps6586x_sys_regulator_ops = { +}; + +static const unsigned int tps6586x_ldo0_voltages[] = { + 1200000, 1500000, 1800000, 2500000, 2700000, 2850000, 3100000, 3300000, +}; + +static const unsigned int tps6586x_ldo4_voltages[] = { + 1700000, 1725000, 1750000, 1775000, 1800000, 1825000, 1850000, 1875000, + 1900000, 1925000, 1950000, 1975000, 2000000, 2025000, 2050000, 2075000, + 2100000, 2125000, 2150000, 2175000, 2200000, 2225000, 2250000, 2275000, + 2300000, 2325000, 2350000, 2375000, 2400000, 2425000, 2450000, 2475000, +}; + +static const unsigned int tps6586x_ldo_voltages[] = { + 1250000, 1500000, 1800000, 2500000, 2700000, 2850000, 3100000, 3300000, +}; + +static const unsigned int tps6586x_sm2_voltages[] = { + 3000000, 3050000, 3100000, 3150000, 3200000, 3250000, 3300000, 3350000, + 3400000, 3450000, 3500000, 3550000, 3600000, 3650000, 3700000, 3750000, + 3800000, 3850000, 3900000, 3950000, 4000000, 4050000, 4100000, 4150000, + 4200000, 4250000, 4300000, 4350000, 4400000, 4450000, 4500000, 4550000, +}; + +static const unsigned int tps6586x_dvm_voltages[] = { + 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, +}; + +#define TPS6586X_REGULATOR(_id, _pin_name, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ + .desc = { \ + .supply_name = _pin_name, \ + .name = "REG-" #_id, \ + .ops = &tps6586x_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = TPS6586X_ID_##_id, \ + .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \ + .volt_table = tps6586x_##vdata##_voltages, \ + .owner = THIS_MODULE, \ + .enable_reg = TPS6586X_SUPPLY##ereg0, \ + .enable_mask = 1 << (ebit0), \ + .vsel_reg = TPS6586X_##vreg, \ + .vsel_mask = ((1 << (nbits)) - 1) << (shift), \ + .apply_reg = (goreg), \ + .apply_bit = (gobit), \ + }, \ + .enable_reg[0] = TPS6586X_SUPPLY##ereg0, \ + .enable_bit[0] = (ebit0), \ + .enable_reg[1] = TPS6586X_SUPPLY##ereg1, \ + .enable_bit[1] = (ebit1), + +#define TPS6586X_LDO(_id, _pname, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1) \ +{ \ + TPS6586X_REGULATOR(_id, _pname, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, 0, 0) \ +} + +#define TPS6586X_DVM(_id, _pname, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ +{ \ + TPS6586X_REGULATOR(_id, _pname, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ +} + +#define TPS6586X_SYS_REGULATOR() \ +{ \ + .desc = { \ + .supply_name = "sys", \ + .name = "REG-SYS", \ + .ops = &tps6586x_sys_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = TPS6586X_ID_SYS, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct tps6586x_regulator tps6586x_regulator[] = { + TPS6586X_SYS_REGULATOR(), + TPS6586X_LDO(LDO_0, "vinldo01", ldo0, SUPPLYV1, 5, 3, ENC, 0, END, 0), + TPS6586X_LDO(LDO_3, "vinldo23", ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2), + TPS6586X_LDO(LDO_5, "REG-SYS", ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6), + TPS6586X_LDO(LDO_6, "vinldo678", ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4), + TPS6586X_LDO(LDO_7, "vinldo678", ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5), + TPS6586X_LDO(LDO_8, "vinldo678", ldo, SUPPLYV2, 5, 3, ENC, 6, END, 6), + TPS6586X_LDO(LDO_9, "vinldo9", ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7), + TPS6586X_LDO(LDO_RTC, "REG-SYS", ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7), + TPS6586X_LDO(LDO_1, "vinldo01", dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1), + TPS6586X_LDO(SM_2, "vin-sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7), + + TPS6586X_DVM(LDO_2, "vinldo23", dvm, LDO2BV1, 0, 5, ENA, 3, + ENB, 3, TPS6586X_VCC2, BIT(6)), + TPS6586X_DVM(LDO_4, "vinldo4", ldo4, LDO4V1, 0, 5, ENC, 3, + END, 3, TPS6586X_VCC1, BIT(6)), + TPS6586X_DVM(SM_0, "vin-sm0", dvm, SM0V1, 0, 5, ENA, 1, + ENB, 1, TPS6586X_VCC1, BIT(2)), + TPS6586X_DVM(SM_1, "vin-sm1", dvm, SM1V1, 0, 5, ENA, 0, + ENB, 0, TPS6586X_VCC1, BIT(0)), +}; + +/* + * TPS6586X has 2 enable bits that are OR'ed to determine the actual + * regulator state. Clearing one of this bits allows switching + * regulator on and of with single register write. + */ +static inline int tps6586x_regulator_preinit(struct device *parent, + struct tps6586x_regulator *ri) +{ + uint8_t val1, val2; + int ret; + + if (ri->enable_reg[0] == ri->enable_reg[1] && + ri->enable_bit[0] == ri->enable_bit[1]) + return 0; + + ret = tps6586x_read(parent, ri->enable_reg[0], &val1); + if (ret) + return ret; + + ret = tps6586x_read(parent, ri->enable_reg[1], &val2); + if (ret) + return ret; + + if (!(val2 & (1 << ri->enable_bit[1]))) + return 0; + + /* + * The regulator is on, but it's enabled with the bit we don't + * want to use, so we switch the enable bits + */ + if (!(val1 & (1 << ri->enable_bit[0]))) { + ret = tps6586x_set_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); + if (ret) + return ret; + } + + return tps6586x_clr_bits(parent, ri->enable_reg[1], + 1 << ri->enable_bit[1]); +} + +static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev, + int id, struct regulator_init_data *p) +{ + struct device *parent = pdev->dev.parent; + struct tps6586x_settings *setting = p->driver_data; + uint8_t reg; + + if (setting == NULL) + return 0; + + if (!(setting->slew_rate & TPS6586X_SLEW_RATE_SET)) + return 0; + + /* only SM0 and SM1 can have the slew rate settings */ + switch (id) { + case TPS6586X_ID_SM_0: + reg = TPS6586X_SM0SL; + break; + case TPS6586X_ID_SM_1: + reg = TPS6586X_SM1SL; + break; + default: + dev_err(&pdev->dev, "Only SM0/SM1 can set slew rate\n"); + return -EINVAL; + } + + return tps6586x_write(parent, reg, + setting->slew_rate & TPS6586X_SLEW_RATE_MASK); +} + +static inline struct tps6586x_regulator *find_regulator_info(int id) +{ + struct tps6586x_regulator *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(tps6586x_regulator); i++) { + ri = &tps6586x_regulator[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +#ifdef CONFIG_OF +static struct of_regulator_match tps6586x_matches[] = { + { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS }, + { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 }, + { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 }, + { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 }, + { .name = "ldo0", .driver_data = (void *)TPS6586X_ID_LDO_0 }, + { .name = "ldo1", .driver_data = (void *)TPS6586X_ID_LDO_1 }, + { .name = "ldo2", .driver_data = (void *)TPS6586X_ID_LDO_2 }, + { .name = "ldo3", .driver_data = (void *)TPS6586X_ID_LDO_3 }, + { .name = "ldo4", .driver_data = (void *)TPS6586X_ID_LDO_4 }, + { .name = "ldo5", .driver_data = (void *)TPS6586X_ID_LDO_5 }, + { .name = "ldo6", .driver_data = (void *)TPS6586X_ID_LDO_6 }, + { .name = "ldo7", .driver_data = (void *)TPS6586X_ID_LDO_7 }, + { .name = "ldo8", .driver_data = (void *)TPS6586X_ID_LDO_8 }, + { .name = "ldo9", .driver_data = (void *)TPS6586X_ID_LDO_9 }, + { .name = "ldo_rtc", .driver_data = (void *)TPS6586X_ID_LDO_RTC }, +}; + +static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( + struct platform_device *pdev, + struct of_regulator_match **tps6586x_reg_matches) +{ + const unsigned int num = ARRAY_SIZE(tps6586x_matches); + struct device_node *np = pdev->dev.parent->of_node; + struct device_node *regs; + const char *sys_rail = NULL; + unsigned int i; + struct tps6586x_platform_data *pdata; + int err; + + regs = of_find_node_by_name(np, "regulators"); + if (!regs) { + dev_err(&pdev->dev, "regulator node not found\n"); + return NULL; + } + + err = of_regulator_match(&pdev->dev, regs, tps6586x_matches, num); + of_node_put(regs); + if (err < 0) { + dev_err(&pdev->dev, "Regulator match failed, e %d\n", err); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloction failed\n"); + return NULL; + } + + for (i = 0; i < num; i++) { + int id; + if (!tps6586x_matches[i].init_data) + continue; + + pdata->reg_init_data[i] = tps6586x_matches[i].init_data; + id = (int)tps6586x_matches[i].driver_data; + if (id == TPS6586X_ID_SYS) + sys_rail = pdata->reg_init_data[i]->constraints.name; + + if ((id == TPS6586X_ID_LDO_5) || (id == TPS6586X_ID_LDO_RTC)) + pdata->reg_init_data[i]->supply_regulator = sys_rail; + } + *tps6586x_reg_matches = tps6586x_matches; + return pdata; +} +#else +static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( + struct platform_device *pdev, + struct of_regulator_match **tps6586x_reg_matches) +{ + *tps6586x_reg_matches = NULL; + return NULL; +} +#endif + +static int tps6586x_regulator_probe(struct platform_device *pdev) +{ + struct tps6586x_regulator *ri = NULL; + struct regulator_config config = { }; + struct regulator_dev **rdev; + struct regulator_init_data *reg_data; + struct tps6586x_platform_data *pdata; + struct of_regulator_match *tps6586x_reg_matches = NULL; + int id; + int err; + + dev_dbg(&pdev->dev, "Probing regulator\n"); + + pdata = dev_get_platdata(pdev->dev.parent); + if ((!pdata) && (pdev->dev.parent->of_node)) + pdata = tps6586x_parse_regulator_dt(pdev, + &tps6586x_reg_matches); + + if (!pdata) { + dev_err(&pdev->dev, "Platform data not available, exiting\n"); + return -ENODEV; + } + + rdev = devm_kzalloc(&pdev->dev, TPS6586X_ID_MAX_REGULATOR * + sizeof(*rdev), GFP_KERNEL); + if (!rdev) { + dev_err(&pdev->dev, "Mmemory alloc failed\n"); + return -ENOMEM; + } + + for (id = 0; id < TPS6586X_ID_MAX_REGULATOR; ++id) { + reg_data = pdata->reg_init_data[id]; + + ri = find_regulator_info(id); + if (!ri) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + err = -EINVAL; + goto fail; + } + + err = tps6586x_regulator_preinit(pdev->dev.parent, ri); + if (err) { + dev_err(&pdev->dev, + "regulator %d preinit failed, e %d\n", id, err); + goto fail; + } + + config.dev = pdev->dev.parent; + config.init_data = reg_data; + config.driver_data = ri; + + if (tps6586x_reg_matches) + config.of_node = tps6586x_reg_matches[id].of_node; + + rdev[id] = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev[id])) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + err = PTR_ERR(rdev[id]); + goto fail; + } + + if (reg_data) { + err = tps6586x_regulator_set_slew_rate(pdev, id, + reg_data); + if (err < 0) { + dev_err(&pdev->dev, + "Slew rate config failed, e %d\n", err); + regulator_unregister(rdev[id]); + goto fail; + } + } + } + + platform_set_drvdata(pdev, rdev); + return 0; + +fail: + while (--id >= 0) + regulator_unregister(rdev[id]); + return err; +} + +static int tps6586x_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev **rdev = platform_get_drvdata(pdev); + int id = TPS6586X_ID_MAX_REGULATOR; + + while (--id >= 0) + regulator_unregister(rdev[id]); + + return 0; +} + +static struct platform_driver tps6586x_regulator_driver = { + .driver = { + .name = "tps6586x-regulator", + .owner = THIS_MODULE, + }, + .probe = tps6586x_regulator_probe, + .remove = tps6586x_regulator_remove, +}; + +static int __init tps6586x_regulator_init(void) +{ + return platform_driver_register(&tps6586x_regulator_driver); +} +subsys_initcall(tps6586x_regulator_init); + +static void __exit tps6586x_regulator_exit(void) +{ + platform_driver_unregister(&tps6586x_regulator_driver); +} +module_exit(tps6586x_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport "); +MODULE_DESCRIPTION("Regulator Driver for TI TPS6586X PMIC"); +MODULE_ALIAS("platform:tps6586x-regulator"); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c new file mode 100644 index 0000000..45c1644 --- /dev/null +++ b/drivers/regulator/tps65910-regulator.c @@ -0,0 +1,1266 @@ +/* + * tps65910.c -- TI tps65910 + * + * Copyright 2010 Texas Instruments Inc. + * + * Author: Graeme Gregory + * Author: Jorge Eduardo Candelaria + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TPS65910_SUPPLY_STATE_ENABLED 0x1 +#define EXT_SLEEP_CONTROL (TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1 | \ + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2 | \ + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3 | \ + TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) + +/* supported VIO voltages in microvolts */ +static const unsigned int VIO_VSEL_table[] = { + 1500000, 1800000, 2500000, 3300000, +}; + +/* VSEL tables for TPS65910 specific LDOs and dcdc's */ + +/* supported VRTC voltages in microvolts */ +static const unsigned int VRTC_VSEL_table[] = { + 1800000, +}; + +/* supported VDD3 voltages in microvolts */ +static const unsigned int VDD3_VSEL_table[] = { + 5000000, +}; + +/* supported VDIG1 voltages in microvolts */ +static const unsigned int VDIG1_VSEL_table[] = { + 1200000, 1500000, 1800000, 2700000, +}; + +/* supported VDIG2 voltages in microvolts */ +static const unsigned int VDIG2_VSEL_table[] = { + 1000000, 1100000, 1200000, 1800000, +}; + +/* supported VPLL voltages in microvolts */ +static const unsigned int VPLL_VSEL_table[] = { + 1000000, 1100000, 1800000, 2500000, +}; + +/* supported VDAC voltages in microvolts */ +static const unsigned int VDAC_VSEL_table[] = { + 1800000, 2600000, 2800000, 2850000, +}; + +/* supported VAUX1 voltages in microvolts */ +static const unsigned int VAUX1_VSEL_table[] = { + 1800000, 2500000, 2800000, 2850000, +}; + +/* supported VAUX2 voltages in microvolts */ +static const unsigned int VAUX2_VSEL_table[] = { + 1800000, 2800000, 2900000, 3300000, +}; + +/* supported VAUX33 voltages in microvolts */ +static const unsigned int VAUX33_VSEL_table[] = { + 1800000, 2000000, 2800000, 3300000, +}; + +/* supported VMMC voltages in microvolts */ +static const unsigned int VMMC_VSEL_table[] = { + 1800000, 2800000, 3000000, 3300000, +}; + +struct tps_info { + const char *name; + const char *vin_name; + u8 n_voltages; + const unsigned int *voltage_table; + int enable_time_us; +}; + +static struct tps_info tps65910_regs[] = { + { + .name = "vrtc", + .vin_name = "vcc7", + .n_voltages = ARRAY_SIZE(VRTC_VSEL_table), + .voltage_table = VRTC_VSEL_table, + .enable_time_us = 2200, + }, + { + .name = "vio", + .vin_name = "vccio", + .n_voltages = ARRAY_SIZE(VIO_VSEL_table), + .voltage_table = VIO_VSEL_table, + .enable_time_us = 350, + }, + { + .name = "vdd1", + .vin_name = "vcc1", + .enable_time_us = 350, + }, + { + .name = "vdd2", + .vin_name = "vcc2", + .enable_time_us = 350, + }, + { + .name = "vdd3", + .n_voltages = ARRAY_SIZE(VDD3_VSEL_table), + .voltage_table = VDD3_VSEL_table, + .enable_time_us = 200, + }, + { + .name = "vdig1", + .vin_name = "vcc6", + .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table), + .voltage_table = VDIG1_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vdig2", + .vin_name = "vcc6", + .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table), + .voltage_table = VDIG2_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vpll", + .vin_name = "vcc5", + .n_voltages = ARRAY_SIZE(VPLL_VSEL_table), + .voltage_table = VPLL_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vdac", + .vin_name = "vcc5", + .n_voltages = ARRAY_SIZE(VDAC_VSEL_table), + .voltage_table = VDAC_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vaux1", + .vin_name = "vcc4", + .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table), + .voltage_table = VAUX1_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vaux2", + .vin_name = "vcc4", + .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table), + .voltage_table = VAUX2_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vaux33", + .vin_name = "vcc3", + .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table), + .voltage_table = VAUX33_VSEL_table, + .enable_time_us = 100, + }, + { + .name = "vmmc", + .vin_name = "vcc3", + .n_voltages = ARRAY_SIZE(VMMC_VSEL_table), + .voltage_table = VMMC_VSEL_table, + .enable_time_us = 100, + }, +}; + +static struct tps_info tps65911_regs[] = { + { + .name = "vrtc", + .vin_name = "vcc7", + .enable_time_us = 2200, + }, + { + .name = "vio", + .vin_name = "vccio", + .n_voltages = ARRAY_SIZE(VIO_VSEL_table), + .voltage_table = VIO_VSEL_table, + .enable_time_us = 350, + }, + { + .name = "vdd1", + .vin_name = "vcc1", + .n_voltages = 0x4C, + .enable_time_us = 350, + }, + { + .name = "vdd2", + .vin_name = "vcc2", + .n_voltages = 0x4C, + .enable_time_us = 350, + }, + { + .name = "vddctrl", + .n_voltages = 0x44, + .enable_time_us = 900, + }, + { + .name = "ldo1", + .vin_name = "vcc6", + .n_voltages = 0x33, + .enable_time_us = 420, + }, + { + .name = "ldo2", + .vin_name = "vcc6", + .n_voltages = 0x33, + .enable_time_us = 420, + }, + { + .name = "ldo3", + .vin_name = "vcc5", + .n_voltages = 0x1A, + .enable_time_us = 230, + }, + { + .name = "ldo4", + .vin_name = "vcc5", + .n_voltages = 0x33, + .enable_time_us = 230, + }, + { + .name = "ldo5", + .vin_name = "vcc4", + .n_voltages = 0x1A, + .enable_time_us = 230, + }, + { + .name = "ldo6", + .vin_name = "vcc3", + .n_voltages = 0x1A, + .enable_time_us = 230, + }, + { + .name = "ldo7", + .vin_name = "vcc3", + .n_voltages = 0x1A, + .enable_time_us = 230, + }, + { + .name = "ldo8", + .vin_name = "vcc3", + .n_voltages = 0x1A, + .enable_time_us = 230, + }, +}; + +#define EXT_CONTROL_REG_BITS(id, regs_offs, bits) (((regs_offs) << 8) | (bits)) +static unsigned int tps65910_ext_sleep_control[] = { + 0, + EXT_CONTROL_REG_BITS(VIO, 1, 0), + EXT_CONTROL_REG_BITS(VDD1, 1, 1), + EXT_CONTROL_REG_BITS(VDD2, 1, 2), + EXT_CONTROL_REG_BITS(VDD3, 1, 3), + EXT_CONTROL_REG_BITS(VDIG1, 0, 1), + EXT_CONTROL_REG_BITS(VDIG2, 0, 2), + EXT_CONTROL_REG_BITS(VPLL, 0, 6), + EXT_CONTROL_REG_BITS(VDAC, 0, 7), + EXT_CONTROL_REG_BITS(VAUX1, 0, 3), + EXT_CONTROL_REG_BITS(VAUX2, 0, 4), + EXT_CONTROL_REG_BITS(VAUX33, 0, 5), + EXT_CONTROL_REG_BITS(VMMC, 0, 0), +}; + +static unsigned int tps65911_ext_sleep_control[] = { + 0, + EXT_CONTROL_REG_BITS(VIO, 1, 0), + EXT_CONTROL_REG_BITS(VDD1, 1, 1), + EXT_CONTROL_REG_BITS(VDD2, 1, 2), + EXT_CONTROL_REG_BITS(VDDCTRL, 1, 3), + EXT_CONTROL_REG_BITS(LDO1, 0, 1), + EXT_CONTROL_REG_BITS(LDO2, 0, 2), + EXT_CONTROL_REG_BITS(LDO3, 0, 7), + EXT_CONTROL_REG_BITS(LDO4, 0, 6), + EXT_CONTROL_REG_BITS(LDO5, 0, 3), + EXT_CONTROL_REG_BITS(LDO6, 0, 0), + EXT_CONTROL_REG_BITS(LDO7, 0, 5), + EXT_CONTROL_REG_BITS(LDO8, 0, 4), +}; + +struct tps65910_reg { + struct regulator_desc *desc; + struct tps65910 *mfd; + struct regulator_dev **rdev; + struct tps_info **info; + int num_regulators; + int mode; + int (*get_ctrl_reg)(int); + unsigned int *ext_sleep_control; + unsigned int board_ext_control[TPS65910_NUM_REGS]; +}; + +static int tps65910_get_ctrl_register(int id) +{ + switch (id) { + case TPS65910_REG_VRTC: + return TPS65910_VRTC; + case TPS65910_REG_VIO: + return TPS65910_VIO; + case TPS65910_REG_VDD1: + return TPS65910_VDD1; + case TPS65910_REG_VDD2: + return TPS65910_VDD2; + case TPS65910_REG_VDD3: + return TPS65910_VDD3; + case TPS65910_REG_VDIG1: + return TPS65910_VDIG1; + case TPS65910_REG_VDIG2: + return TPS65910_VDIG2; + case TPS65910_REG_VPLL: + return TPS65910_VPLL; + case TPS65910_REG_VDAC: + return TPS65910_VDAC; + case TPS65910_REG_VAUX1: + return TPS65910_VAUX1; + case TPS65910_REG_VAUX2: + return TPS65910_VAUX2; + case TPS65910_REG_VAUX33: + return TPS65910_VAUX33; + case TPS65910_REG_VMMC: + return TPS65910_VMMC; + default: + return -EINVAL; + } +} + +static int tps65911_get_ctrl_register(int id) +{ + switch (id) { + case TPS65910_REG_VRTC: + return TPS65910_VRTC; + case TPS65910_REG_VIO: + return TPS65910_VIO; + case TPS65910_REG_VDD1: + return TPS65910_VDD1; + case TPS65910_REG_VDD2: + return TPS65910_VDD2; + case TPS65911_REG_VDDCTRL: + return TPS65911_VDDCTRL; + case TPS65911_REG_LDO1: + return TPS65911_LDO1; + case TPS65911_REG_LDO2: + return TPS65911_LDO2; + case TPS65911_REG_LDO3: + return TPS65911_LDO3; + case TPS65911_REG_LDO4: + return TPS65911_LDO4; + case TPS65911_REG_LDO5: + return TPS65911_LDO5; + case TPS65911_REG_LDO6: + return TPS65911_LDO6; + case TPS65911_REG_LDO7: + return TPS65911_LDO7; + case TPS65911_REG_LDO8: + return TPS65911_LDO8; + default: + return -EINVAL; + } +} + +static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + struct tps65910 *mfd = pmic->mfd; + int reg, value, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + return tps65910_reg_update_bits(pmic->mfd, reg, + LDO_ST_MODE_BIT | LDO_ST_ON_BIT, + LDO_ST_ON_BIT); + case REGULATOR_MODE_IDLE: + value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT; + return tps65910_reg_set_bits(mfd, reg, value); + case REGULATOR_MODE_STANDBY: + return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT); + } + + return -EINVAL; +} + +static unsigned int tps65910_get_mode(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int ret, reg, value, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + ret = tps65910_reg_read(pmic->mfd, reg, &value); + if (ret < 0) + return ret; + + if (!(value & LDO_ST_ON_BIT)) + return REGULATOR_MODE_STANDBY; + else if (value & LDO_ST_MODE_BIT) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int ret, id = rdev_get_id(dev); + int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0; + + switch (id) { + case TPS65910_REG_VDD1: + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1_OP, &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1, &mult); + if (ret < 0) + return ret; + mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT; + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD1_SR, &srvsel); + if (ret < 0) + return ret; + sr = opvsel & VDD1_OP_CMD_MASK; + opvsel &= VDD1_OP_SEL_MASK; + srvsel &= VDD1_SR_SEL_MASK; + vselmax = 75; + break; + case TPS65910_REG_VDD2: + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2_OP, &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2, &mult); + if (ret < 0) + return ret; + mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT; + ret = tps65910_reg_read(pmic->mfd, TPS65910_VDD2_SR, &srvsel); + if (ret < 0) + return ret; + sr = opvsel & VDD2_OP_CMD_MASK; + opvsel &= VDD2_OP_SEL_MASK; + srvsel &= VDD2_SR_SEL_MASK; + vselmax = 75; + break; + case TPS65911_REG_VDDCTRL: + ret = tps65910_reg_read(pmic->mfd, TPS65911_VDDCTRL_OP, + &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, TPS65911_VDDCTRL_SR, + &srvsel); + if (ret < 0) + return ret; + sr = opvsel & VDDCTRL_OP_CMD_MASK; + opvsel &= VDDCTRL_OP_SEL_MASK; + srvsel &= VDDCTRL_SR_SEL_MASK; + vselmax = 64; + break; + } + + /* multiplier 0 == 1 but 2,3 normal */ + if (!mult) + mult=1; + + if (sr) { + /* normalise to valid range */ + if (srvsel < 3) + srvsel = 3; + if (srvsel > vselmax) + srvsel = vselmax; + return srvsel - 3; + } else { + + /* normalise to valid range*/ + if (opvsel < 3) + opvsel = 3; + if (opvsel > vselmax) + opvsel = vselmax; + return opvsel - 3; + } + return -EINVAL; +} + +static int tps65910_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int ret, reg, value, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + ret = tps65910_reg_read(pmic->mfd, reg, &value); + if (ret < 0) + return ret; + + switch (id) { + case TPS65910_REG_VIO: + case TPS65910_REG_VDIG1: + case TPS65910_REG_VDIG2: + case TPS65910_REG_VPLL: + case TPS65910_REG_VDAC: + case TPS65910_REG_VAUX1: + case TPS65910_REG_VAUX2: + case TPS65910_REG_VAUX33: + case TPS65910_REG_VMMC: + value &= LDO_SEL_MASK; + value >>= LDO_SEL_SHIFT; + break; + default: + return -EINVAL; + } + + return value; +} + +static int tps65910_get_voltage_vdd3(struct regulator_dev *dev) +{ + return dev->desc->volt_table[0]; +} + +static int tps65911_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int ret, id = rdev_get_id(dev); + unsigned int value, reg; + + reg = pmic->get_ctrl_reg(id); + + ret = tps65910_reg_read(pmic->mfd, reg, &value); + if (ret < 0) + return ret; + + switch (id) { + case TPS65911_REG_LDO1: + case TPS65911_REG_LDO2: + case TPS65911_REG_LDO4: + value &= LDO1_SEL_MASK; + value >>= LDO_SEL_SHIFT; + break; + case TPS65911_REG_LDO3: + case TPS65911_REG_LDO5: + case TPS65911_REG_LDO6: + case TPS65911_REG_LDO7: + case TPS65911_REG_LDO8: + value &= LDO3_SEL_MASK; + value >>= LDO_SEL_SHIFT; + break; + case TPS65910_REG_VIO: + value &= LDO_SEL_MASK; + value >>= LDO_SEL_SHIFT; + break; + default: + return -EINVAL; + } + + return value; +} + +static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev), vsel; + int dcdc_mult = 0; + + switch (id) { + case TPS65910_REG_VDD1: + dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; + if (dcdc_mult == 1) + dcdc_mult--; + vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3; + + tps65910_reg_update_bits(pmic->mfd, TPS65910_VDD1, + VDD1_VGAIN_SEL_MASK, + dcdc_mult << VDD1_VGAIN_SEL_SHIFT); + tps65910_reg_write(pmic->mfd, TPS65910_VDD1_OP, vsel); + break; + case TPS65910_REG_VDD2: + dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; + if (dcdc_mult == 1) + dcdc_mult--; + vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3; + + tps65910_reg_update_bits(pmic->mfd, TPS65910_VDD2, + VDD1_VGAIN_SEL_MASK, + dcdc_mult << VDD2_VGAIN_SEL_SHIFT); + tps65910_reg_write(pmic->mfd, TPS65910_VDD2_OP, vsel); + break; + case TPS65911_REG_VDDCTRL: + vsel = selector + 3; + tps65910_reg_write(pmic->mfd, TPS65911_VDDCTRL_OP, vsel); + } + + return 0; +} + +static int tps65910_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + switch (id) { + case TPS65910_REG_VIO: + case TPS65910_REG_VDIG1: + case TPS65910_REG_VDIG2: + case TPS65910_REG_VPLL: + case TPS65910_REG_VDAC: + case TPS65910_REG_VAUX1: + case TPS65910_REG_VAUX2: + case TPS65910_REG_VAUX33: + case TPS65910_REG_VMMC: + return tps65910_reg_update_bits(pmic->mfd, reg, LDO_SEL_MASK, + selector << LDO_SEL_SHIFT); + } + + return -EINVAL; +} + +static int tps65911_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int reg, id = rdev_get_id(dev); + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + switch (id) { + case TPS65911_REG_LDO1: + case TPS65911_REG_LDO2: + case TPS65911_REG_LDO4: + return tps65910_reg_update_bits(pmic->mfd, reg, LDO1_SEL_MASK, + selector << LDO_SEL_SHIFT); + case TPS65911_REG_LDO3: + case TPS65911_REG_LDO5: + case TPS65911_REG_LDO6: + case TPS65911_REG_LDO7: + case TPS65911_REG_LDO8: + return tps65910_reg_update_bits(pmic->mfd, reg, LDO3_SEL_MASK, + selector << LDO_SEL_SHIFT); + case TPS65910_REG_VIO: + return tps65910_reg_update_bits(pmic->mfd, reg, LDO_SEL_MASK, + selector << LDO_SEL_SHIFT); + } + + return -EINVAL; +} + + +static int tps65910_list_voltage_dcdc(struct regulator_dev *dev, + unsigned selector) +{ + int volt, mult = 1, id = rdev_get_id(dev); + + switch (id) { + case TPS65910_REG_VDD1: + case TPS65910_REG_VDD2: + mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1; + volt = VDD1_2_MIN_VOLT + + (selector % VDD1_2_NUM_VOLT_FINE) * VDD1_2_OFFSET; + break; + case TPS65911_REG_VDDCTRL: + volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET); + break; + default: + BUG(); + return -EINVAL; + } + + return volt * 100 * mult; +} + +static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int step_mv = 0, id = rdev_get_id(dev); + + switch(id) { + case TPS65911_REG_LDO1: + case TPS65911_REG_LDO2: + case TPS65911_REG_LDO4: + /* The first 5 values of the selector correspond to 1V */ + if (selector < 5) + selector = 0; + else + selector -= 4; + + step_mv = 50; + break; + case TPS65911_REG_LDO3: + case TPS65911_REG_LDO5: + case TPS65911_REG_LDO6: + case TPS65911_REG_LDO7: + case TPS65911_REG_LDO8: + /* The first 3 values of the selector correspond to 1V */ + if (selector < 3) + selector = 0; + else + selector -= 2; + + step_mv = 100; + break; + case TPS65910_REG_VIO: + return pmic->info[id]->voltage_table[selector]; + default: + return -EINVAL; + } + + return (LDO_MIN_VOLT + selector * step_mv) * 1000; +} + +/* Regulator ops (except VRTC) */ +static struct regulator_ops tps65910_ops_dcdc = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage_sel = tps65910_get_voltage_dcdc_sel, + .set_voltage_sel = tps65910_set_voltage_dcdc_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .list_voltage = tps65910_list_voltage_dcdc, + .map_voltage = regulator_map_voltage_ascend, +}; + +static struct regulator_ops tps65910_ops_vdd3 = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage = tps65910_get_voltage_vdd3, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, +}; + +static struct regulator_ops tps65910_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage_sel = tps65910_get_voltage_sel, + .set_voltage_sel = tps65910_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, +}; + +static struct regulator_ops tps65911_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_mode = tps65910_set_mode, + .get_mode = tps65910_get_mode, + .get_voltage_sel = tps65911_get_voltage_sel, + .set_voltage_sel = tps65911_set_voltage_sel, + .list_voltage = tps65911_list_voltage, + .map_voltage = regulator_map_voltage_ascend, +}; + +static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, + int id, int ext_sleep_config) +{ + struct tps65910 *mfd = pmic->mfd; + u8 regoffs = (pmic->ext_sleep_control[id] >> 8) & 0xFF; + u8 bit_pos = (1 << pmic->ext_sleep_control[id] & 0xFF); + int ret; + + /* + * Regulator can not be control from multiple external input EN1, EN2 + * and EN3 together. + */ + if (ext_sleep_config & EXT_SLEEP_CONTROL) { + int en_count; + en_count = ((ext_sleep_config & + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) != 0); + en_count += ((ext_sleep_config & + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) != 0); + en_count += ((ext_sleep_config & + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) != 0); + en_count += ((ext_sleep_config & + TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) != 0); + if (en_count > 1) { + dev_err(mfd->dev, + "External sleep control flag is not proper\n"); + return -EINVAL; + } + } + + pmic->board_ext_control[id] = ext_sleep_config; + + /* External EN1 control */ + if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) + ret = tps65910_reg_set_bits(mfd, + TPS65910_EN1_LDO_ASS + regoffs, bit_pos); + else + ret = tps65910_reg_clear_bits(mfd, + TPS65910_EN1_LDO_ASS + regoffs, bit_pos); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring external control EN1\n"); + return ret; + } + + /* External EN2 control */ + if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) + ret = tps65910_reg_set_bits(mfd, + TPS65910_EN2_LDO_ASS + regoffs, bit_pos); + else + ret = tps65910_reg_clear_bits(mfd, + TPS65910_EN2_LDO_ASS + regoffs, bit_pos); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring external control EN2\n"); + return ret; + } + + /* External EN3 control for TPS65910 LDO only */ + if ((tps65910_chip_id(mfd) == TPS65910) && + (id >= TPS65910_REG_VDIG1)) { + if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) + ret = tps65910_reg_set_bits(mfd, + TPS65910_EN3_LDO_ASS + regoffs, bit_pos); + else + ret = tps65910_reg_clear_bits(mfd, + TPS65910_EN3_LDO_ASS + regoffs, bit_pos); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring external control EN3\n"); + return ret; + } + } + + /* Return if no external control is selected */ + if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) { + /* Clear all sleep controls */ + ret = tps65910_reg_clear_bits(mfd, + TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); + if (!ret) + ret = tps65910_reg_clear_bits(mfd, + TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); + if (ret < 0) + dev_err(mfd->dev, + "Error in configuring SLEEP register\n"); + return ret; + } + + /* + * For regulator that has separate operational and sleep register make + * sure that operational is used and clear sleep register to turn + * regulator off when external control is inactive + */ + if ((id == TPS65910_REG_VDD1) || + (id == TPS65910_REG_VDD2) || + ((id == TPS65911_REG_VDDCTRL) && + (tps65910_chip_id(mfd) == TPS65911))) { + int op_reg_add = pmic->get_ctrl_reg(id) + 1; + int sr_reg_add = pmic->get_ctrl_reg(id) + 2; + int opvsel, srvsel; + + ret = tps65910_reg_read(pmic->mfd, op_reg_add, &opvsel); + if (ret < 0) + return ret; + ret = tps65910_reg_read(pmic->mfd, sr_reg_add, &srvsel); + if (ret < 0) + return ret; + + if (opvsel & VDD1_OP_CMD_MASK) { + u8 reg_val = srvsel & VDD1_OP_SEL_MASK; + + ret = tps65910_reg_write(pmic->mfd, op_reg_add, + reg_val); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring op register\n"); + return ret; + } + } + ret = tps65910_reg_write(pmic->mfd, sr_reg_add, 0); + if (ret < 0) { + dev_err(mfd->dev, "Error in settting sr register\n"); + return ret; + } + } + + ret = tps65910_reg_clear_bits(mfd, + TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); + if (!ret) { + if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) + ret = tps65910_reg_set_bits(mfd, + TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); + else + ret = tps65910_reg_clear_bits(mfd, + TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); + } + if (ret < 0) + dev_err(mfd->dev, + "Error in configuring SLEEP register\n"); + + return ret; +} + +#ifdef CONFIG_OF + +static struct of_regulator_match tps65910_matches[] = { + { .name = "vrtc", .driver_data = (void *) &tps65910_regs[0] }, + { .name = "vio", .driver_data = (void *) &tps65910_regs[1] }, + { .name = "vdd1", .driver_data = (void *) &tps65910_regs[2] }, + { .name = "vdd2", .driver_data = (void *) &tps65910_regs[3] }, + { .name = "vdd3", .driver_data = (void *) &tps65910_regs[4] }, + { .name = "vdig1", .driver_data = (void *) &tps65910_regs[5] }, + { .name = "vdig2", .driver_data = (void *) &tps65910_regs[6] }, + { .name = "vpll", .driver_data = (void *) &tps65910_regs[7] }, + { .name = "vdac", .driver_data = (void *) &tps65910_regs[8] }, + { .name = "vaux1", .driver_data = (void *) &tps65910_regs[9] }, + { .name = "vaux2", .driver_data = (void *) &tps65910_regs[10] }, + { .name = "vaux33", .driver_data = (void *) &tps65910_regs[11] }, + { .name = "vmmc", .driver_data = (void *) &tps65910_regs[12] }, +}; + +static struct of_regulator_match tps65911_matches[] = { + { .name = "vrtc", .driver_data = (void *) &tps65911_regs[0] }, + { .name = "vio", .driver_data = (void *) &tps65911_regs[1] }, + { .name = "vdd1", .driver_data = (void *) &tps65911_regs[2] }, + { .name = "vdd2", .driver_data = (void *) &tps65911_regs[3] }, + { .name = "vddctrl", .driver_data = (void *) &tps65911_regs[4] }, + { .name = "ldo1", .driver_data = (void *) &tps65911_regs[5] }, + { .name = "ldo2", .driver_data = (void *) &tps65911_regs[6] }, + { .name = "ldo3", .driver_data = (void *) &tps65911_regs[7] }, + { .name = "ldo4", .driver_data = (void *) &tps65911_regs[8] }, + { .name = "ldo5", .driver_data = (void *) &tps65911_regs[9] }, + { .name = "ldo6", .driver_data = (void *) &tps65911_regs[10] }, + { .name = "ldo7", .driver_data = (void *) &tps65911_regs[11] }, + { .name = "ldo8", .driver_data = (void *) &tps65911_regs[12] }, +}; + +static struct tps65910_board *tps65910_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps65910_reg_matches) +{ + struct tps65910_board *pmic_plat_data; + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct device_node *np, *regulators; + struct of_regulator_match *matches; + unsigned int prop; + int idx = 0, ret, count; + + pmic_plat_data = devm_kzalloc(&pdev->dev, sizeof(*pmic_plat_data), + GFP_KERNEL); + + if (!pmic_plat_data) { + dev_err(&pdev->dev, "Failure to alloc pdata for regulators.\n"); + return NULL; + } + + np = of_node_get(pdev->dev.parent->of_node); + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_err(&pdev->dev, "regulator node not found\n"); + return NULL; + } + + switch (tps65910_chip_id(tps65910)) { + case TPS65910: + count = ARRAY_SIZE(tps65910_matches); + matches = tps65910_matches; + break; + case TPS65911: + count = ARRAY_SIZE(tps65911_matches); + matches = tps65911_matches; + break; + default: + of_node_put(regulators); + dev_err(&pdev->dev, "Invalid tps chip version\n"); + return NULL; + } + + ret = of_regulator_match(&pdev->dev, regulators, matches, count); + of_node_put(regulators); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", + ret); + return NULL; + } + + *tps65910_reg_matches = matches; + + for (idx = 0; idx < count; idx++) { + if (!matches[idx].init_data || !matches[idx].of_node) + continue; + + pmic_plat_data->tps65910_pmic_init_data[idx] = + matches[idx].init_data; + + ret = of_property_read_u32(matches[idx].of_node, + "ti,regulator-ext-sleep-control", &prop); + if (!ret) + pmic_plat_data->regulator_ext_sleep_control[idx] = prop; + + } + + return pmic_plat_data; +} +#else +static inline struct tps65910_board *tps65910_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps65910_reg_matches) +{ + *tps65910_reg_matches = NULL; + return NULL; +} +#endif + +static int tps65910_probe(struct platform_device *pdev) +{ + struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct tps_info *info; + struct regulator_init_data *reg_data; + struct regulator_dev *rdev; + struct tps65910_reg *pmic; + struct tps65910_board *pmic_plat_data; + struct of_regulator_match *tps65910_reg_matches = NULL; + int i, err; + + pmic_plat_data = dev_get_platdata(tps65910->dev); + if (!pmic_plat_data && tps65910->dev->of_node) + pmic_plat_data = tps65910_parse_dt_reg_data(pdev, + &tps65910_reg_matches); + + if (!pmic_plat_data) { + dev_err(&pdev->dev, "Platform data not found\n"); + return -EINVAL; + } + + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "Memory allocation failed for pmic\n"); + return -ENOMEM; + } + + pmic->mfd = tps65910; + platform_set_drvdata(pdev, pmic); + + /* Give control of all register to control port */ + tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, + DEVCTRL_SR_CTL_I2C_SEL_MASK); + + switch(tps65910_chip_id(tps65910)) { + case TPS65910: + pmic->get_ctrl_reg = &tps65910_get_ctrl_register; + pmic->num_regulators = ARRAY_SIZE(tps65910_regs); + pmic->ext_sleep_control = tps65910_ext_sleep_control; + info = tps65910_regs; + break; + case TPS65911: + pmic->get_ctrl_reg = &tps65911_get_ctrl_register; + pmic->num_regulators = ARRAY_SIZE(tps65911_regs); + pmic->ext_sleep_control = tps65911_ext_sleep_control; + info = tps65911_regs; + break; + default: + dev_err(&pdev->dev, "Invalid tps chip version\n"); + return -ENODEV; + } + + pmic->desc = devm_kzalloc(&pdev->dev, pmic->num_regulators * + sizeof(struct regulator_desc), GFP_KERNEL); + if (!pmic->desc) { + dev_err(&pdev->dev, "Memory alloc fails for desc\n"); + return -ENOMEM; + } + + pmic->info = devm_kzalloc(&pdev->dev, pmic->num_regulators * + sizeof(struct tps_info *), GFP_KERNEL); + if (!pmic->info) { + dev_err(&pdev->dev, "Memory alloc fails for info\n"); + return -ENOMEM; + } + + pmic->rdev = devm_kzalloc(&pdev->dev, pmic->num_regulators * + sizeof(struct regulator_dev *), GFP_KERNEL); + if (!pmic->rdev) { + dev_err(&pdev->dev, "Memory alloc fails for rdev\n"); + return -ENOMEM; + } + + for (i = 0; i < pmic->num_regulators && i < TPS65910_NUM_REGS; + i++, info++) { + + reg_data = pmic_plat_data->tps65910_pmic_init_data[i]; + + /* Regulator API handles empty constraints but not NULL + * constraints */ + if (!reg_data) + continue; + + /* Register the regulators */ + pmic->info[i] = info; + + pmic->desc[i].name = info->name; + pmic->desc[i].supply_name = info->vin_name; + pmic->desc[i].id = i; + pmic->desc[i].n_voltages = info->n_voltages; + pmic->desc[i].enable_time = info->enable_time_us; + + if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) { + pmic->desc[i].ops = &tps65910_ops_dcdc; + pmic->desc[i].n_voltages = VDD1_2_NUM_VOLT_FINE * + VDD1_2_NUM_VOLT_COARSE; + pmic->desc[i].ramp_delay = 12500; + } else if (i == TPS65910_REG_VDD3) { + if (tps65910_chip_id(tps65910) == TPS65910) { + pmic->desc[i].ops = &tps65910_ops_vdd3; + pmic->desc[i].volt_table = info->voltage_table; + } else { + pmic->desc[i].ops = &tps65910_ops_dcdc; + pmic->desc[i].ramp_delay = 5000; + } + } else { + if (tps65910_chip_id(tps65910) == TPS65910) { + pmic->desc[i].ops = &tps65910_ops; + pmic->desc[i].volt_table = info->voltage_table; + } else { + pmic->desc[i].ops = &tps65911_ops; + } + } + + err = tps65910_set_ext_sleep_config(pmic, i, + pmic_plat_data->regulator_ext_sleep_control[i]); + /* + * Failing on regulator for configuring externally control + * is not a serious issue, just throw warning. + */ + if (err < 0) + dev_warn(tps65910->dev, + "Failed to initialise ext control config\n"); + + pmic->desc[i].type = REGULATOR_VOLTAGE; + pmic->desc[i].owner = THIS_MODULE; + pmic->desc[i].enable_reg = pmic->get_ctrl_reg(i); + pmic->desc[i].enable_mask = TPS65910_SUPPLY_STATE_ENABLED; + + config.dev = tps65910->dev; + config.init_data = reg_data; + config.driver_data = pmic; + config.regmap = tps65910->regmap; + + if (tps65910_reg_matches) + config.of_node = tps65910_reg_matches[i].of_node; + + rdev = regulator_register(&pmic->desc[i], &config); + if (IS_ERR(rdev)) { + dev_err(tps65910->dev, + "failed to register %s regulator\n", + pdev->name); + err = PTR_ERR(rdev); + goto err_unregister_regulator; + } + + /* Save regulator for cleanup */ + pmic->rdev[i] = rdev; + } + return 0; + +err_unregister_regulator: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + return err; +} + +static int tps65910_remove(struct platform_device *pdev) +{ + struct tps65910_reg *pmic = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < pmic->num_regulators; i++) + regulator_unregister(pmic->rdev[i]); + + return 0; +} + +static void tps65910_shutdown(struct platform_device *pdev) +{ + struct tps65910_reg *pmic = platform_get_drvdata(pdev); + int i; + + /* + * Before bootloader jumps to kernel, it makes sure that required + * external control signals are in desired state so that given rails + * can be configure accordingly. + * If rails are configured to be controlled from external control + * then before shutting down/rebooting the system, the external + * control configuration need to be remove from the rails so that + * its output will be available as per register programming even + * if external controls are removed. This is require when the POR + * value of the control signals are not in active state and before + * bootloader initializes it, the system requires the rail output + * to be active for booting. + */ + for (i = 0; i < pmic->num_regulators; i++) { + int err; + if (!pmic->rdev[i]) + continue; + + err = tps65910_set_ext_sleep_config(pmic, i, 0); + if (err < 0) + dev_err(&pdev->dev, + "Error in clearing external control\n"); + } +} + +static struct platform_driver tps65910_driver = { + .driver = { + .name = "tps65910-pmic", + .owner = THIS_MODULE, + }, + .probe = tps65910_probe, + .remove = tps65910_remove, + .shutdown = tps65910_shutdown, +}; + +static int __init tps65910_init(void) +{ + return platform_driver_register(&tps65910_driver); +} +subsys_initcall(tps65910_init); + +static void __exit tps65910_cleanup(void) +{ + platform_driver_unregister(&tps65910_driver); +} +module_exit(tps65910_cleanup); + +MODULE_AUTHOR("Graeme Gregory "); +MODULE_DESCRIPTION("TPS65910/TPS65911 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65910-pmic"); diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c new file mode 100644 index 0000000..17e994e --- /dev/null +++ b/drivers/regulator/tps65912-regulator.c @@ -0,0 +1,562 @@ +/* + * tps65912.c -- TI tps65912 + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya Cabrera + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This driver is based on wm8350 implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DCDC's */ +#define TPS65912_REG_DCDC1 0 +#define TPS65912_REG_DCDC2 1 +#define TPS65912_REG_DCDC3 2 +#define TPS65912_REG_DCDC4 3 + +/* LDOs */ +#define TPS65912_REG_LDO1 4 +#define TPS65912_REG_LDO2 5 +#define TPS65912_REG_LDO3 6 +#define TPS65912_REG_LDO4 7 +#define TPS65912_REG_LDO5 8 +#define TPS65912_REG_LDO6 9 +#define TPS65912_REG_LDO7 10 +#define TPS65912_REG_LDO8 11 +#define TPS65912_REG_LDO9 12 +#define TPS65912_REG_LDO10 13 + +/* Number of step-down converters available */ +#define TPS65912_NUM_DCDC 4 + +/* Number of LDO voltage regulators available */ +#define TPS65912_NUM_LDO 10 + +/* Number of total regulators available */ +#define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO) + +#define TPS65912_REG_ENABLED 0x80 +#define OP_SELREG_MASK 0x40 +#define OP_SELREG_SHIFT 6 + +struct tps_info { + const char *name; +}; + +static struct tps_info tps65912_regs[] = { + { + .name = "DCDC1", + }, + { + .name = "DCDC2", + }, + { + .name = "DCDC3", + }, + { + .name = "DCDC4", + }, + { + .name = "LDO1", + }, + { + .name = "LDO2", + }, + { + .name = "LDO3", + }, + { + .name = "LDO4", + }, + { + .name = "LDO5", + }, + { + .name = "LDO6", + }, + { + .name = "LDO7", + }, + { + .name = "LDO8", + }, + { + .name = "LDO9", + }, + { + .name = "LDO10", + }, +}; + +struct tps65912_reg { + struct regulator_desc desc[TPS65912_NUM_REGULATOR]; + struct tps65912 *mfd; + struct regulator_dev *rdev[TPS65912_NUM_REGULATOR]; + struct tps_info *info[TPS65912_NUM_REGULATOR]; + /* for read/write access */ + struct mutex io_lock; + int mode; + int (*get_ctrl_reg)(int); + int dcdc_range[TPS65912_NUM_DCDC]; + int pwm_mode_reg; + int eco_reg; +}; + +static int tps65912_get_range(struct tps65912_reg *pmic, int id) +{ + struct tps65912 *mfd = pmic->mfd; + int range; + + switch (id) { + case TPS65912_REG_DCDC1: + range = tps65912_reg_read(mfd, TPS65912_DCDC1_LIMIT); + break; + case TPS65912_REG_DCDC2: + range = tps65912_reg_read(mfd, TPS65912_DCDC2_LIMIT); + break; + case TPS65912_REG_DCDC3: + range = tps65912_reg_read(mfd, TPS65912_DCDC3_LIMIT); + break; + case TPS65912_REG_DCDC4: + range = tps65912_reg_read(mfd, TPS65912_DCDC4_LIMIT); + break; + default: + return 0; + } + + if (range >= 0) + range = (range & DCDC_LIMIT_RANGE_MASK) + >> DCDC_LIMIT_RANGE_SHIFT; + + pmic->dcdc_range[id] = range; + return range; +} + +static unsigned long tps65912_vsel_to_uv_range0(u8 vsel) +{ + unsigned long uv; + + uv = ((vsel * 12500) + 500000); + return uv; +} + +static unsigned long tps65912_vsel_to_uv_range1(u8 vsel) +{ + unsigned long uv; + + uv = ((vsel * 12500) + 700000); + return uv; +} + +static unsigned long tps65912_vsel_to_uv_range2(u8 vsel) +{ + unsigned long uv; + + uv = ((vsel * 25000) + 500000); + return uv; +} + +static unsigned long tps65912_vsel_to_uv_range3(u8 vsel) +{ + unsigned long uv; + + if (vsel == 0x3f) + uv = 3800000; + else + uv = ((vsel * 50000) + 500000); + + return uv; +} + +static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel) +{ + unsigned long uv = 0; + + if (vsel <= 32) + uv = ((vsel * 25000) + 800000); + else if (vsel > 32 && vsel <= 60) + uv = (((vsel - 32) * 50000) + 1600000); + else if (vsel > 60) + uv = (((vsel - 60) * 100000) + 3000000); + + return uv; +} + +static int tps65912_get_ctrl_register(int id) +{ + if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) + return id * 3 + TPS65912_DCDC1_AVS; + else if (id >= TPS65912_REG_LDO5 && id <= TPS65912_REG_LDO10) + return id - TPS65912_REG_LDO5 + TPS65912_LDO5; + else + return -EINVAL; +} + +static int tps65912_get_sel_register(struct tps65912_reg *pmic, int id) +{ + struct tps65912 *mfd = pmic->mfd; + int opvsel; + u8 reg = 0; + + if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) { + opvsel = tps65912_reg_read(mfd, id * 3 + TPS65912_DCDC1_OP); + if (opvsel & OP_SELREG_MASK) + reg = id * 3 + TPS65912_DCDC1_AVS; + else + reg = id * 3 + TPS65912_DCDC1_OP; + } else if (id >= TPS65912_REG_LDO5 && id <= TPS65912_REG_LDO10) { + reg = id - TPS65912_REG_LDO5 + TPS65912_LDO5; + } else { + return -EINVAL; + } + + return reg; +} + +static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id) +{ + switch (id) { + case TPS65912_REG_DCDC1: + pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL; + pmic->eco_reg = TPS65912_DCDC1_AVS; + break; + case TPS65912_REG_DCDC2: + pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL; + pmic->eco_reg = TPS65912_DCDC2_AVS; + break; + case TPS65912_REG_DCDC3: + pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL; + pmic->eco_reg = TPS65912_DCDC3_AVS; + break; + case TPS65912_REG_DCDC4: + pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL; + pmic->eco_reg = TPS65912_DCDC4_AVS; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tps65912_reg_is_enabled(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int reg, value, id = rdev_get_id(dev); + + if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) + return -EINVAL; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + value = tps65912_reg_read(mfd, reg); + if (value < 0) + return value; + + return value & TPS65912_REG_ENABLED; +} + +static int tps65912_reg_enable(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int reg; + + if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) + return -EINVAL; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED); +} + +static int tps65912_reg_disable(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev), reg; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED); +} + +static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int pwm_mode, eco, id = rdev_get_id(dev); + + tps65912_get_mode_regiters(pmic, id); + + pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); + eco = tps65912_reg_read(mfd, pmic->eco_reg); + + pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; + eco &= DCDC_AVS_ECO_MASK; + + switch (mode) { + case REGULATOR_MODE_FAST: + /* Verify if mode alredy set */ + if (pwm_mode && !eco) + break; + tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); + tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); + break; + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_IDLE: + if (!pwm_mode && !eco) + break; + tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); + tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); + break; + case REGULATOR_MODE_STANDBY: + if (!pwm_mode && eco) + break; + tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); + tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int tps65912_get_mode(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int pwm_mode, eco, mode = 0, id = rdev_get_id(dev); + + tps65912_get_mode_regiters(pmic, id); + + pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); + eco = tps65912_reg_read(mfd, pmic->eco_reg); + + pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; + eco &= DCDC_AVS_ECO_MASK; + + if (pwm_mode && !eco) + mode = REGULATOR_MODE_FAST; + else if (!pwm_mode && !eco) + mode = REGULATOR_MODE_NORMAL; + else if (!pwm_mode && eco) + mode = REGULATOR_MODE_STANDBY; + + return mode; +} + +static int tps65912_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + int range, voltage = 0, id = rdev_get_id(dev); + + if (id >= TPS65912_REG_LDO1 && id <= TPS65912_REG_LDO10) + return tps65912_vsel_to_uv_ldo(selector); + + if (id > TPS65912_REG_DCDC4) + return -EINVAL; + + range = pmic->dcdc_range[id]; + + switch (range) { + case 0: + /* 0.5 - 1.2875V in 12.5mV steps */ + voltage = tps65912_vsel_to_uv_range0(selector); + break; + case 1: + /* 0.7 - 1.4875V in 12.5mV steps */ + voltage = tps65912_vsel_to_uv_range1(selector); + break; + case 2: + /* 0.5 - 2.075V in 25mV steps */ + voltage = tps65912_vsel_to_uv_range2(selector); + break; + case 3: + /* 0.5 - 3.8V in 50mV steps */ + voltage = tps65912_vsel_to_uv_range3(selector); + break; + } + return voltage; +} + +static int tps65912_get_voltage_sel(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int reg, vsel; + + reg = tps65912_get_sel_register(pmic, id); + if (reg < 0) + return reg; + + vsel = tps65912_reg_read(mfd, reg); + vsel &= 0x3F; + + return vsel; +} + +static int tps65912_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int value; + u8 reg; + + reg = tps65912_get_sel_register(pmic, id); + value = tps65912_reg_read(mfd, reg); + value &= 0xC0; + return tps65912_reg_write(mfd, reg, selector | value); +} + +/* Operations permitted on DCDCx */ +static struct regulator_ops tps65912_ops_dcdc = { + .is_enabled = tps65912_reg_is_enabled, + .enable = tps65912_reg_enable, + .disable = tps65912_reg_disable, + .set_mode = tps65912_set_mode, + .get_mode = tps65912_get_mode, + .get_voltage_sel = tps65912_get_voltage_sel, + .set_voltage_sel = tps65912_set_voltage_sel, + .list_voltage = tps65912_list_voltage, +}; + +/* Operations permitted on LDOx */ +static struct regulator_ops tps65912_ops_ldo = { + .is_enabled = tps65912_reg_is_enabled, + .enable = tps65912_reg_enable, + .disable = tps65912_reg_disable, + .get_voltage_sel = tps65912_get_voltage_sel, + .set_voltage_sel = tps65912_set_voltage_sel, + .list_voltage = tps65912_list_voltage, +}; + +static int tps65912_probe(struct platform_device *pdev) +{ + struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct tps_info *info; + struct regulator_init_data *reg_data; + struct regulator_dev *rdev; + struct tps65912_reg *pmic; + struct tps65912_board *pmic_plat_data; + int i, err; + + pmic_plat_data = dev_get_platdata(tps65912->dev); + if (!pmic_plat_data) + return -EINVAL; + + reg_data = pmic_plat_data->tps65912_pmic_init_data; + + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + mutex_init(&pmic->io_lock); + pmic->mfd = tps65912; + platform_set_drvdata(pdev, pmic); + + pmic->get_ctrl_reg = &tps65912_get_ctrl_register; + info = tps65912_regs; + + for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) { + int range = 0; + /* Register the regulators */ + pmic->info[i] = info; + + pmic->desc[i].name = info->name; + pmic->desc[i].id = i; + pmic->desc[i].n_voltages = 64; + pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ? + &tps65912_ops_ldo : &tps65912_ops_dcdc); + pmic->desc[i].type = REGULATOR_VOLTAGE; + pmic->desc[i].owner = THIS_MODULE; + range = tps65912_get_range(pmic, i); + + config.dev = tps65912->dev; + config.init_data = reg_data; + config.driver_data = pmic; + + rdev = regulator_register(&pmic->desc[i], &config); + if (IS_ERR(rdev)) { + dev_err(tps65912->dev, + "failed to register %s regulator\n", + pdev->name); + err = PTR_ERR(rdev); + goto err; + } + + /* Save regulator for cleanup */ + pmic->rdev[i] = rdev; + } + return 0; + +err: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + return err; +} + +static int tps65912_remove(struct platform_device *pdev) +{ + struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < TPS65912_NUM_REGULATOR; i++) + regulator_unregister(tps65912_reg->rdev[i]); + return 0; +} + +static struct platform_driver tps65912_driver = { + .driver = { + .name = "tps65912-pmic", + .owner = THIS_MODULE, + }, + .probe = tps65912_probe, + .remove = tps65912_remove, +}; + +static int __init tps65912_init(void) +{ + return platform_driver_register(&tps65912_driver); +} +subsys_initcall(tps65912_init); + +static void __exit tps65912_cleanup(void) +{ + platform_driver_unregister(&tps65912_driver); +} +module_exit(tps65912_cleanup); + +MODULE_AUTHOR("Margarita Olaya Cabrera "); +MODULE_DESCRIPTION("TPS65912 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65912-pmic"); diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c new file mode 100644 index 0000000..6511d0b --- /dev/null +++ b/drivers/regulator/tps80031-regulator.c @@ -0,0 +1,791 @@ +/* + * tps80031-regulator.c -- TI TPS80031 regulator driver. + * + * Regulator driver for TI TPS80031/TPS80032 Fully Integrated Power + * Management with Power Path and Battery Charger. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Flags for DCDC Voltage reading */ +#define DCDC_OFFSET_EN BIT(0) +#define DCDC_EXTENDED_EN BIT(1) +#define TRACK_MODE_ENABLE BIT(2) + +#define SMPS_MULTOFFSET_VIO BIT(1) +#define SMPS_MULTOFFSET_SMPS1 BIT(3) +#define SMPS_MULTOFFSET_SMPS2 BIT(4) +#define SMPS_MULTOFFSET_SMPS3 BIT(6) +#define SMPS_MULTOFFSET_SMPS4 BIT(0) + +#define SMPS_CMD_MASK 0xC0 +#define SMPS_VSEL_MASK 0x3F +#define LDO_VSEL_MASK 0x1F +#define LDO_TRACK_VSEL_MASK 0x3F + +#define MISC2_LDOUSB_IN_VSYS BIT(4) +#define MISC2_LDOUSB_IN_PMID BIT(3) +#define MISC2_LDOUSB_IN_MASK 0x18 + +#define MISC2_LDO3_SEL_VIB_VAL BIT(0) +#define MISC2_LDO3_SEL_VIB_MASK 0x1 + +#define BOOST_HW_PWR_EN BIT(5) +#define BOOST_HW_PWR_EN_MASK BIT(5) + +#define OPA_MODE_EN BIT(6) +#define OPA_MODE_EN_MASK BIT(6) + +#define USB_VBUS_CTRL_SET 0x04 +#define USB_VBUS_CTRL_CLR 0x05 +#define VBUS_DISCHRG 0x20 + +struct tps80031_regulator_info { + /* Regulator register address.*/ + u8 trans_reg; + u8 state_reg; + u8 force_reg; + u8 volt_reg; + u8 volt_id; + + /*Power request bits */ + int preq_bit; + + /* used by regulator core */ + struct regulator_desc desc; + +}; + +struct tps80031_regulator { + struct device *dev; + struct regulator_dev *rdev; + struct tps80031_regulator_info *rinfo; + + u8 device_flags; + unsigned int config_flags; + unsigned int ext_ctrl_flag; +}; + +static inline struct device *to_tps80031_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static int tps80031_reg_is_enabled(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + u8 reg_val; + int ret; + + if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ) + return true; + + ret = tps80031_read(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg, + ®_val); + if (ret < 0) { + dev_err(&rdev->dev, "Reg 0x%02x read failed, err = %d\n", + ri->rinfo->state_reg, ret); + return ret; + } + return ((reg_val & TPS80031_STATE_MASK) == TPS80031_STATE_ON); +} + +static int tps80031_reg_enable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ) + return 0; + + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg, + TPS80031_STATE_ON, TPS80031_STATE_MASK); + if (ret < 0) { + dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n", + ri->rinfo->state_reg, ret); + return ret; + } + return ret; +} + +static int tps80031_reg_disable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ) + return 0; + + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg, + TPS80031_STATE_OFF, TPS80031_STATE_MASK); + if (ret < 0) + dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n", + ri->rinfo->state_reg, ret); + return ret; +} + +/* DCDC voltages for the selector of 58 to 63 */ +static int tps80031_dcdc_voltages[4][5] = { + { 1350, 1500, 1800, 1900, 2100}, + { 1350, 1500, 1800, 1900, 2100}, + { 2084, 2315, 2778, 2932, 3241}, + { 4167, 2315, 2778, 2932, 3241}, +}; + +static int tps80031_dcdc_list_voltage(struct regulator_dev *rdev, unsigned sel) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + int volt_index = ri->device_flags & 0x3; + + if (sel == 0) + return 0; + else if (sel < 58) + return regulator_list_voltage_linear(rdev, sel - 1); + else + return tps80031_dcdc_voltages[volt_index][sel - 58] * 1000; +} + +static int tps80031_dcdc_set_voltage_sel(struct regulator_dev *rdev, + unsigned vsel) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + u8 reg_val; + + if (ri->rinfo->force_reg) { + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->force_reg, ®_val); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + ri->rinfo->force_reg, ret); + return ret; + } + if (!(reg_val & SMPS_CMD_MASK)) { + ret = tps80031_update(parent, ri->rinfo->volt_id, + ri->rinfo->force_reg, vsel, SMPS_VSEL_MASK); + if (ret < 0) + dev_err(ri->dev, + "reg 0x%02x update failed, e = %d\n", + ri->rinfo->force_reg, ret); + return ret; + } + } + ret = tps80031_update(parent, ri->rinfo->volt_id, + ri->rinfo->volt_reg, vsel, SMPS_VSEL_MASK); + if (ret < 0) + dev_err(ri->dev, "reg 0x%02x update failed, e = %d\n", + ri->rinfo->volt_reg, ret); + return ret; +} + +static int tps80031_dcdc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + uint8_t vsel = 0; + int ret; + + if (ri->rinfo->force_reg) { + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->force_reg, &vsel); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + ri->rinfo->force_reg, ret); + return ret; + } + + if (!(vsel & SMPS_CMD_MASK)) + return vsel & SMPS_VSEL_MASK; + } + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->volt_reg, &vsel); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + ri->rinfo->volt_reg, ret); + return ret; + } + return vsel & SMPS_VSEL_MASK; +} + +static int tps80031_ldo_list_voltage(struct regulator_dev *rdev, + unsigned int sel) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + + /* Check for valid setting for TPS80031 or TPS80032-ES1.0 */ + if ((ri->rinfo->desc.id == TPS80031_REGULATOR_LDO2) && + (ri->device_flags & TRACK_MODE_ENABLE)) { + unsigned nvsel = (sel) & 0x1F; + if (((tps80031_get_chip_info(parent) == TPS80031) || + ((tps80031_get_chip_info(parent) == TPS80032) && + (tps80031_get_pmu_version(parent) == 0x0))) && + ((nvsel == 0x0) || (nvsel >= 0x19 && nvsel <= 0x1F))) { + dev_err(ri->dev, + "Invalid sel %d in track mode LDO2\n", + nvsel); + return -EINVAL; + } + } + + return regulator_list_voltage_linear(rdev, sel); +} + +static int tps80031_ldo_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + + /* Check for valid setting for TPS80031 or TPS80032-ES1.0 */ + if ((ri->rinfo->desc.id == TPS80031_REGULATOR_LDO2) && + (ri->device_flags & TRACK_MODE_ENABLE)) { + if (((tps80031_get_chip_info(parent) == TPS80031) || + ((tps80031_get_chip_info(parent) == TPS80032) && + (tps80031_get_pmu_version(parent) == 0x0)))) { + return regulator_map_voltage_iterate(rdev, min_uV, + max_uV); + } + } + + return regulator_map_voltage_linear(rdev, min_uV, max_uV); +} + +static int tps80031_vbus_is_enabled(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret = -EIO; + uint8_t ctrl1 = 0; + uint8_t ctrl3 = 0; + + ret = tps80031_read(parent, TPS80031_SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL1, &ctrl1); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + ret = tps80031_read(parent, TPS80031_SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL3, &ctrl3); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL3, ret); + return ret; + } + if ((ctrl1 & OPA_MODE_EN) && (ctrl3 & BOOST_HW_PWR_EN)) + return 1; + return ret; +} + +static int tps80031_vbus_enable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + ret = tps80031_set_bits(parent, TPS80031_SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + + ret = tps80031_set_bits(parent, TPS80031_SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL3, ret); + return ret; + } + return ret; +} + +static int tps80031_vbus_disable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret = 0; + + if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) { + ret = tps80031_write(parent, TPS80031_SLAVE_ID2, + USB_VBUS_CTRL_SET, VBUS_DISCHRG); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n", + USB_VBUS_CTRL_SET, ret); + return ret; + } + } + + ret = tps80031_clr_bits(parent, TPS80031_SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + + ret = tps80031_clr_bits(parent, TPS80031_SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL3, ret); + return ret; + } + + mdelay(DIV_ROUND_UP(ri->rinfo->desc.enable_time, 1000)); + if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) { + ret = tps80031_write(parent, TPS80031_SLAVE_ID2, + USB_VBUS_CTRL_CLR, VBUS_DISCHRG); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n", + USB_VBUS_CTRL_CLR, ret); + return ret; + } + } + return ret; +} + +static struct regulator_ops tps80031_dcdc_ops = { + .list_voltage = tps80031_dcdc_list_voltage, + .set_voltage_sel = tps80031_dcdc_set_voltage_sel, + .get_voltage_sel = tps80031_dcdc_get_voltage_sel, + .enable = tps80031_reg_enable, + .disable = tps80031_reg_disable, + .is_enabled = tps80031_reg_is_enabled, +}; + +static struct regulator_ops tps80031_ldo_ops = { + .list_voltage = tps80031_ldo_list_voltage, + .map_voltage = tps80031_ldo_map_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = tps80031_reg_enable, + .disable = tps80031_reg_disable, + .is_enabled = tps80031_reg_is_enabled, +}; + +static struct regulator_ops tps80031_vbus_sw_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = tps80031_vbus_enable, + .disable = tps80031_vbus_disable, + .is_enabled = tps80031_vbus_is_enabled, +}; + +static struct regulator_ops tps80031_vbus_hw_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops tps80031_ext_reg_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = tps80031_reg_enable, + .disable = tps80031_reg_disable, + .is_enabled = tps80031_reg_is_enabled, +}; + +/* Non-exiting default definition for some register */ +#define TPS80031_SMPS3_CFG_FORCE 0 +#define TPS80031_SMPS4_CFG_FORCE 0 + +#define TPS80031_VBUS_CFG_TRANS 0 +#define TPS80031_VBUS_CFG_STATE 0 + +#define TPS80031_REG_SMPS(_id, _volt_id, _pbit) \ +{ \ + .trans_reg = TPS80031_##_id##_CFG_TRANS, \ + .state_reg = TPS80031_##_id##_CFG_STATE, \ + .force_reg = TPS80031_##_id##_CFG_FORCE, \ + .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \ + .volt_id = TPS80031_SLAVE_##_volt_id, \ + .preq_bit = _pbit, \ + .desc = { \ + .name = "tps80031_"#_id, \ + .id = TPS80031_REGULATOR_##_id, \ + .n_voltages = 63, \ + .ops = &tps80031_dcdc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_time = 500, \ + }, \ +} + +#define TPS80031_REG_LDO(_id, _preq_bit) \ +{ \ + .trans_reg = TPS80031_##_id##_CFG_TRANS, \ + .state_reg = TPS80031_##_id##_CFG_STATE, \ + .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \ + .volt_id = TPS80031_SLAVE_ID1, \ + .preq_bit = _preq_bit, \ + .desc = { \ + .owner = THIS_MODULE, \ + .name = "tps80031_"#_id, \ + .id = TPS80031_REGULATOR_##_id, \ + .ops = &tps80031_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 1000000, \ + .uV_step = 100000, \ + .linear_min_sel = 1, \ + .n_voltages = 25, \ + .vsel_reg = TPS80031_##_id##_CFG_VOLTAGE, \ + .vsel_mask = LDO_VSEL_MASK, \ + .enable_time = 500, \ + }, \ +} + +#define TPS80031_REG_FIXED(_id, max_mV, _ops, _delay, _pbit) \ +{ \ + .trans_reg = TPS80031_##_id##_CFG_TRANS, \ + .state_reg = TPS80031_##_id##_CFG_STATE, \ + .volt_id = TPS80031_SLAVE_ID1, \ + .preq_bit = _pbit, \ + .desc = { \ + .name = "tps80031_"#_id, \ + .id = TPS80031_REGULATOR_##_id, \ + .min_uV = max_mV * 1000, \ + .n_voltages = 1, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_time = _delay, \ + }, \ +} + +static struct tps80031_regulator_info tps80031_rinfo[TPS80031_REGULATOR_MAX] = { + TPS80031_REG_SMPS(VIO, ID0, 4), + TPS80031_REG_SMPS(SMPS1, ID0, 0), + TPS80031_REG_SMPS(SMPS2, ID0, 1), + TPS80031_REG_SMPS(SMPS3, ID1, 2), + TPS80031_REG_SMPS(SMPS4, ID1, 3), + TPS80031_REG_LDO(VANA, -1), + TPS80031_REG_LDO(LDO1, 8), + TPS80031_REG_LDO(LDO2, 9), + TPS80031_REG_LDO(LDO3, 10), + TPS80031_REG_LDO(LDO4, 11), + TPS80031_REG_LDO(LDO5, 12), + TPS80031_REG_LDO(LDO6, 13), + TPS80031_REG_LDO(LDO7, 14), + TPS80031_REG_LDO(LDOLN, 15), + TPS80031_REG_LDO(LDOUSB, 5), + TPS80031_REG_FIXED(VBUS, 5000, tps80031_vbus_hw_ops, 100000, -1), + TPS80031_REG_FIXED(REGEN1, 3300, tps80031_ext_reg_ops, 0, 16), + TPS80031_REG_FIXED(REGEN2, 3300, tps80031_ext_reg_ops, 0, 17), + TPS80031_REG_FIXED(SYSEN, 3300, tps80031_ext_reg_ops, 0, 18), +}; + +static int tps80031_power_req_config(struct device *parent, + struct tps80031_regulator *ri, + struct tps80031_regulator_platform_data *tps80031_pdata) +{ + int ret = 0; + + if (ri->rinfo->preq_bit < 0) + goto skip_pwr_req_config; + + ret = tps80031_ext_power_req_config(parent, ri->ext_ctrl_flag, + ri->rinfo->preq_bit, ri->rinfo->state_reg, + ri->rinfo->trans_reg); + if (ret < 0) { + dev_err(ri->dev, "ext powerreq config failed, err = %d\n", ret); + return ret; + } + +skip_pwr_req_config: + if (tps80031_pdata->ext_ctrl_flag & TPS80031_PWR_ON_ON_SLEEP) { + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, + ri->rinfo->trans_reg, TPS80031_TRANS_SLEEP_ON, + TPS80031_TRANS_SLEEP_MASK); + if (ret < 0) { + dev_err(ri->dev, "Reg 0x%02x update failed, e %d\n", + ri->rinfo->trans_reg, ret); + return ret; + } + } + return ret; +} + +static int tps80031_regulator_config(struct device *parent, + struct tps80031_regulator *ri, + struct tps80031_regulator_platform_data *tps80031_pdata) +{ + int ret = 0; + + switch (ri->rinfo->desc.id) { + case TPS80031_REGULATOR_LDOUSB: + if (ri->config_flags & (TPS80031_USBLDO_INPUT_VSYS | + TPS80031_USBLDO_INPUT_PMID)) { + unsigned val = 0; + if (ri->config_flags & TPS80031_USBLDO_INPUT_VSYS) + val = MISC2_LDOUSB_IN_VSYS; + else + val = MISC2_LDOUSB_IN_PMID; + + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, + TPS80031_MISC2, val, + MISC2_LDOUSB_IN_MASK); + if (ret < 0) { + dev_err(ri->dev, + "LDOUSB config failed, e= %d\n", ret); + return ret; + } + } + break; + + case TPS80031_REGULATOR_LDO3: + if (ri->config_flags & TPS80031_LDO3_OUTPUT_VIB) { + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, + TPS80031_MISC2, MISC2_LDO3_SEL_VIB_VAL, + MISC2_LDO3_SEL_VIB_MASK); + if (ret < 0) { + dev_err(ri->dev, + "LDO3 config failed, e = %d\n", ret); + return ret; + } + } + break; + + case TPS80031_REGULATOR_VBUS: + /* Provide SW control Ops if VBUS is SW control */ + if (!(ri->config_flags & TPS80031_VBUS_SW_ONLY)) + ri->rinfo->desc.ops = &tps80031_vbus_sw_ops; + break; + default: + break; + } + + /* Configure Active state to ON, SLEEP to OFF and OFF_state to OFF */ + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->trans_reg, + TPS80031_TRANS_ACTIVE_ON | TPS80031_TRANS_SLEEP_OFF | + TPS80031_TRANS_OFF_OFF, TPS80031_TRANS_ACTIVE_MASK | + TPS80031_TRANS_SLEEP_MASK | TPS80031_TRANS_OFF_MASK); + if (ret < 0) { + dev_err(ri->dev, "trans reg update failed, e %d\n", ret); + return ret; + } + + return ret; +} + +static int check_smps_mode_mult(struct device *parent, + struct tps80031_regulator *ri) +{ + int mult_offset; + int ret; + u8 smps_offset; + u8 smps_mult; + + ret = tps80031_read(parent, TPS80031_SLAVE_ID1, + TPS80031_SMPS_OFFSET, &smps_offset); + if (ret < 0) { + dev_err(parent, "Error in reading smps offset register\n"); + return ret; + } + + ret = tps80031_read(parent, TPS80031_SLAVE_ID1, + TPS80031_SMPS_MULT, &smps_mult); + if (ret < 0) { + dev_err(parent, "Error in reading smps mult register\n"); + return ret; + } + + switch (ri->rinfo->desc.id) { + case TPS80031_REGULATOR_VIO: + mult_offset = SMPS_MULTOFFSET_VIO; + break; + case TPS80031_REGULATOR_SMPS1: + mult_offset = SMPS_MULTOFFSET_SMPS1; + break; + case TPS80031_REGULATOR_SMPS2: + mult_offset = SMPS_MULTOFFSET_SMPS2; + break; + case TPS80031_REGULATOR_SMPS3: + mult_offset = SMPS_MULTOFFSET_SMPS3; + break; + case TPS80031_REGULATOR_SMPS4: + mult_offset = SMPS_MULTOFFSET_SMPS4; + break; + case TPS80031_REGULATOR_LDO2: + ri->device_flags = smps_mult & BIT(5) ? TRACK_MODE_ENABLE : 0; + /* TRACK mode the ldo2 varies from 600mV to 1300mV */ + if (ri->device_flags & TRACK_MODE_ENABLE) { + ri->rinfo->desc.min_uV = 600000; + ri->rinfo->desc.uV_step = 12500; + ri->rinfo->desc.n_voltages = 57; + ri->rinfo->desc.vsel_mask = LDO_TRACK_VSEL_MASK; + } + return 0; + default: + return 0; + } + + ri->device_flags = (smps_offset & mult_offset) ? DCDC_OFFSET_EN : 0; + ri->device_flags |= (smps_mult & mult_offset) ? DCDC_EXTENDED_EN : 0; + switch (ri->device_flags) { + case 0: + ri->rinfo->desc.min_uV = 607700; + ri->rinfo->desc.uV_step = 12660; + break; + case DCDC_OFFSET_EN: + ri->rinfo->desc.min_uV = 700000; + ri->rinfo->desc.uV_step = 12500; + break; + case DCDC_EXTENDED_EN: + ri->rinfo->desc.min_uV = 1852000; + ri->rinfo->desc.uV_step = 38600; + break; + case DCDC_OFFSET_EN | DCDC_EXTENDED_EN: + ri->rinfo->desc.min_uV = 2161000; + ri->rinfo->desc.uV_step = 38600; + break; + } + return 0; +} + +static int tps80031_regulator_probe(struct platform_device *pdev) +{ + struct tps80031_platform_data *pdata; + struct tps80031_regulator_platform_data *tps_pdata; + struct tps80031_regulator *ri; + struct tps80031_regulator *pmic; + struct regulator_dev *rdev; + struct regulator_config config = { }; + struct tps80031 *tps80031_mfd = dev_get_drvdata(pdev->dev.parent); + int ret; + int num; + + pdata = dev_get_platdata(pdev->dev.parent); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + pmic = devm_kzalloc(&pdev->dev, + TPS80031_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + return -ENOMEM; + } + + for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) { + tps_pdata = pdata->regulator_pdata[num]; + ri = &pmic[num]; + ri->rinfo = &tps80031_rinfo[num]; + ri->dev = &pdev->dev; + + check_smps_mode_mult(pdev->dev.parent, ri); + config.dev = &pdev->dev; + config.init_data = NULL; + config.driver_data = ri; + config.regmap = tps80031_mfd->regmap[ri->rinfo->volt_id]; + + if (tps_pdata) { + config.init_data = tps_pdata->reg_init_data; + ri->config_flags = tps_pdata->config_flags; + ri->ext_ctrl_flag = tps_pdata->ext_ctrl_flag; + ret = tps80031_regulator_config(pdev->dev.parent, + ri, tps_pdata); + if (ret < 0) { + dev_err(&pdev->dev, + "regulator config failed, e %d\n", ret); + goto fail; + } + + ret = tps80031_power_req_config(pdev->dev.parent, + ri, tps_pdata); + if (ret < 0) { + dev_err(&pdev->dev, + "pwr_req config failed, err %d\n", ret); + goto fail; + } + } + rdev = regulator_register(&ri->rinfo->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "register regulator failed %s\n", + ri->rinfo->desc.name); + ret = PTR_ERR(rdev); + goto fail; + } + ri->rdev = rdev; + } + + platform_set_drvdata(pdev, pmic); + return 0; +fail: + while (--num >= 0) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return ret; +} + +static int tps80031_regulator_remove(struct platform_device *pdev) +{ + struct tps80031_regulator *pmic = platform_get_drvdata(pdev); + struct tps80031_regulator *ri = NULL; + int num; + + for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return 0; +} + +static struct platform_driver tps80031_regulator_driver = { + .driver = { + .name = "tps80031-pmic", + .owner = THIS_MODULE, + }, + .probe = tps80031_regulator_probe, + .remove = tps80031_regulator_remove, +}; + +static int __init tps80031_regulator_init(void) +{ + return platform_driver_register(&tps80031_regulator_driver); +} +subsys_initcall(tps80031_regulator_init); + +static void __exit tps80031_regulator_exit(void) +{ + platform_driver_unregister(&tps80031_regulator_driver); +} +module_exit(tps80031_regulator_exit); + +MODULE_ALIAS("platform:tps80031-regulator"); +MODULE_DESCRIPTION("Regulator Driver for TI TPS80031/TPS80032 PMIC"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c new file mode 100644 index 0000000..fb6e67d --- /dev/null +++ b/drivers/regulator/twl-regulator.c @@ -0,0 +1,1253 @@ +/* + * twl-regulator.c -- support regulators in twl4030/twl6030 family chips + * + * Copyright (C) 2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * The TWL4030/TW5030/TPS659x0/TWL6030 family chips include power management, a + * USB OTG transceiver, an RTC, ADC, PWM, and lots more. Some versions + * include an audio codec, battery charger, and more voltage regulators. + * These chips are often used in OMAP-based systems. + * + * This driver implements software-based resource control for various + * voltage regulators. This is usually augmented with state machine + * based control. + */ + +struct twlreg_info { + /* start of regulator's PM_RECEIVER control register bank */ + u8 base; + + /* twl resource ID, for resource control state machine */ + u8 id; + + /* voltage in mV = table[VSEL]; table_len must be a power-of-two */ + u8 table_len; + const u16 *table; + + /* State REMAP default configuration */ + u8 remap; + + /* chip constraints on regulator behavior */ + u16 min_mV; + u16 max_mV; + + u8 flags; + + /* used by regulator core */ + struct regulator_desc desc; + + /* chip specific features */ + unsigned long features; + + /* + * optional override functions for voltage set/get + * these are currently only used for SMPS regulators + */ + int (*get_voltage)(void *data); + int (*set_voltage)(void *data, int target_uV); + + /* data passed from board for external get/set voltage */ + void *data; +}; + + +/* LDO control registers ... offset is from the base of its register bank. + * The first three registers of all power resource banks help hardware to + * manage the various resource groups. + */ +/* Common offset in TWL4030/6030 */ +#define VREG_GRP 0 +/* TWL4030 register offsets */ +#define VREG_TYPE 1 +#define VREG_REMAP 2 +#define VREG_DEDICATED 3 /* LDO control */ +#define VREG_VOLTAGE_SMPS_4030 9 +/* TWL6030 register offsets */ +#define VREG_TRANS 1 +#define VREG_STATE 2 +#define VREG_VOLTAGE 3 +#define VREG_VOLTAGE_SMPS 4 +/* TWL6030 Misc register offsets */ +#define VREG_BC_ALL 1 +#define VREG_BC_REF 2 +#define VREG_BC_PROC 3 +#define VREG_BC_CLK_RST 4 + +/* TWL6030 LDO register values for CFG_STATE */ +#define TWL6030_CFG_STATE_OFF 0x00 +#define TWL6030_CFG_STATE_ON 0x01 +#define TWL6030_CFG_STATE_OFF2 0x02 +#define TWL6030_CFG_STATE_SLEEP 0x03 +#define TWL6030_CFG_STATE_GRP_SHIFT 5 +#define TWL6030_CFG_STATE_APP_SHIFT 2 +#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT) +#define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ + TWL6030_CFG_STATE_APP_SHIFT) + +/* Flags for SMPS Voltage reading */ +#define SMPS_OFFSET_EN BIT(0) +#define SMPS_EXTENDED_EN BIT(1) + +/* twl6025 SMPS EPROM values */ +#define TWL6030_SMPS_OFFSET 0xB0 +#define TWL6030_SMPS_MULT 0xB3 +#define SMPS_MULTOFFSET_SMPS4 BIT(0) +#define SMPS_MULTOFFSET_VIO BIT(1) +#define SMPS_MULTOFFSET_SMPS3 BIT(6) + +static inline int +twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset) +{ + u8 value; + int status; + + status = twl_i2c_read_u8(slave_subgp, + &value, info->base + offset); + return (status < 0) ? status : value; +} + +static inline int +twlreg_write(struct twlreg_info *info, unsigned slave_subgp, unsigned offset, + u8 value) +{ + return twl_i2c_write_u8(slave_subgp, + value, info->base + offset); +} + +/*----------------------------------------------------------------------*/ + +/* generic power resource operations, which work on all regulators */ + +static int twlreg_grp(struct regulator_dev *rdev) +{ + return twlreg_read(rdev_get_drvdata(rdev), TWL_MODULE_PM_RECEIVER, + VREG_GRP); +} + +/* + * Enable/disable regulators by joining/leaving the P1 (processor) group. + * We assume nobody else is updating the DEV_GRP registers. + */ +/* definition for 4030 family */ +#define P3_GRP_4030 BIT(7) /* "peripherals" */ +#define P2_GRP_4030 BIT(6) /* secondary processor, modem, etc */ +#define P1_GRP_4030 BIT(5) /* CPU/Linux */ +/* definition for 6030 family */ +#define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */ +#define P2_GRP_6030 BIT(1) /* "peripherals" */ +#define P1_GRP_6030 BIT(0) /* CPU/Linux */ + +static int twl4030reg_is_enabled(struct regulator_dev *rdev) +{ + int state = twlreg_grp(rdev); + + if (state < 0) + return state; + + return state & P1_GRP_4030; +} + +static int twl6030reg_is_enabled(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0, val; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) { + grp = twlreg_grp(rdev); + if (grp < 0) + return grp; + grp &= P1_GRP_6030; + } else { + grp = 1; + } + + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + val = TWL6030_CFG_STATE_APP(val); + + return grp && (val == TWL6030_CFG_STATE_ON); +} + +static int twl4030reg_enable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp; + int ret; + + grp = twlreg_grp(rdev); + if (grp < 0) + return grp; + + grp |= P1_GRP_4030; + + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); + + return ret; +} + +static int twl6030reg_enable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int ret; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = twlreg_grp(rdev); + if (grp < 0) + return grp; + + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, + grp << TWL6030_CFG_STATE_GRP_SHIFT | + TWL6030_CFG_STATE_ON); + return ret; +} + +static int twl4030reg_disable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp; + int ret; + + grp = twlreg_grp(rdev); + if (grp < 0) + return grp; + + grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030); + + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); + + return ret; +} + +static int twl6030reg_disable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int ret; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030; + + /* For 6030, set the off state for all grps enabled */ + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, + (grp) << TWL6030_CFG_STATE_GRP_SHIFT | + TWL6030_CFG_STATE_OFF); + + return ret; +} + +static int twl4030reg_get_status(struct regulator_dev *rdev) +{ + int state = twlreg_grp(rdev); + + if (state < 0) + return state; + state &= 0x0f; + + /* assume state != WARM_RESET; we'd not be running... */ + if (!state) + return REGULATOR_STATUS_OFF; + return (state & BIT(3)) + ? REGULATOR_STATUS_NORMAL + : REGULATOR_STATUS_STANDBY; +} + +static int twl6030reg_get_status(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int val; + + val = twlreg_grp(rdev); + if (val < 0) + return val; + + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + + switch (TWL6030_CFG_STATE_APP(val)) { + case TWL6030_CFG_STATE_ON: + return REGULATOR_STATUS_NORMAL; + + case TWL6030_CFG_STATE_SLEEP: + return REGULATOR_STATUS_STANDBY; + + case TWL6030_CFG_STATE_OFF: + case TWL6030_CFG_STATE_OFF2: + default: + break; + } + + return REGULATOR_STATUS_OFF; +} + +static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + unsigned message; + int status; + + /* We can only set the mode through state machine commands... */ + switch (mode) { + case REGULATOR_MODE_NORMAL: + message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_ACTIVE); + break; + case REGULATOR_MODE_STANDBY: + message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_SLEEP); + break; + default: + return -EINVAL; + } + + /* Ensure the resource is associated with some group */ + status = twlreg_grp(rdev); + if (status < 0) + return status; + if (!(status & (P3_GRP_4030 | P2_GRP_4030 | P1_GRP_4030))) + return -EACCES; + + status = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, + message >> 8, TWL4030_PM_MASTER_PB_WORD_MSB); + if (status < 0) + return status; + + return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, + message & 0xff, TWL4030_PM_MASTER_PB_WORD_LSB); +} + +static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int val; + + if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) + grp = twlreg_grp(rdev); + + if (grp < 0) + return grp; + + /* Compose the state register settings */ + val = grp << TWL6030_CFG_STATE_GRP_SHIFT; + /* We can only set the mode through state machine commands... */ + switch (mode) { + case REGULATOR_MODE_NORMAL: + val |= TWL6030_CFG_STATE_ON; + break; + case REGULATOR_MODE_STANDBY: + val |= TWL6030_CFG_STATE_SLEEP; + break; + + default: + return -EINVAL; + } + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val); +} + +/*----------------------------------------------------------------------*/ + +/* + * Support for adjustable-voltage LDOs uses a four bit (or less) voltage + * select field in its control register. We use tables indexed by VSEL + * to record voltages in milliVolts. (Accuracy is about three percent.) + * + * Note that VSEL values for VAUX2 changed in twl5030 and newer silicon; + * currently handled by listing two slightly different VAUX2 regulators, + * only one of which will be configured. + * + * VSEL values documented as "TI cannot support these values" are flagged + * in these tables as UNSUP() values; we normally won't assign them. + * + * VAUX3 at 3V is incorrectly listed in some TI manuals as unsupported. + * TI are revising the twl5030/tps659x0 specs to support that 3.0V setting. + */ +#define UNSUP_MASK 0x8000 + +#define UNSUP(x) (UNSUP_MASK | (x)) +#define IS_UNSUP(info, x) \ + ((UNSUP_MASK & (x)) && \ + !((info)->features & TWL4030_ALLOW_UNSUPPORTED)) +#define LDO_MV(x) (~UNSUP_MASK & (x)) + + +static const u16 VAUX1_VSEL_table[] = { + UNSUP(1500), UNSUP(1800), 2500, 2800, + 3000, 3000, 3000, 3000, +}; +static const u16 VAUX2_4030_VSEL_table[] = { + UNSUP(1000), UNSUP(1000), UNSUP(1200), 1300, + 1500, 1800, UNSUP(1850), 2500, + UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000), + UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), +}; +static const u16 VAUX2_VSEL_table[] = { + 1700, 1700, 1900, 1300, + 1500, 1800, 2000, 2500, + 2100, 2800, 2200, 2300, + 2400, 2400, 2400, 2400, +}; +static const u16 VAUX3_VSEL_table[] = { + 1500, 1800, 2500, 2800, + 3000, 3000, 3000, 3000, +}; +static const u16 VAUX4_VSEL_table[] = { + 700, 1000, 1200, UNSUP(1300), + 1500, 1800, UNSUP(1850), 2500, + UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000), + UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), +}; +static const u16 VMMC1_VSEL_table[] = { + 1850, 2850, 3000, 3150, +}; +static const u16 VMMC2_VSEL_table[] = { + UNSUP(1000), UNSUP(1000), UNSUP(1200), UNSUP(1300), + UNSUP(1500), UNSUP(1800), 1850, UNSUP(2500), + 2600, 2800, 2850, 3000, + 3150, 3150, 3150, 3150, +}; +static const u16 VPLL1_VSEL_table[] = { + 1000, 1200, 1300, 1800, + UNSUP(2800), UNSUP(3000), UNSUP(3000), UNSUP(3000), +}; +static const u16 VPLL2_VSEL_table[] = { + 700, 1000, 1200, 1300, + UNSUP(1500), 1800, UNSUP(1850), UNSUP(2500), + UNSUP(2600), UNSUP(2800), UNSUP(2850), UNSUP(3000), + UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), +}; +static const u16 VSIM_VSEL_table[] = { + UNSUP(1000), UNSUP(1200), UNSUP(1300), 1800, + 2800, 3000, 3000, 3000, +}; +static const u16 VDAC_VSEL_table[] = { + 1200, 1300, 1800, 1800, +}; +static const u16 VIO_VSEL_table[] = { + 1800, 1850, +}; +static const u16 VINTANA2_VSEL_table[] = { + 2500, 2750, +}; + +static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int mV = info->table[index]; + + return IS_UNSUP(info, mV) ? 0 : (LDO_MV(mV) * 1000); +} + +static int +twl4030ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, + selector); +} + +static int twl4030ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE); + + if (vsel < 0) + return vsel; + + vsel &= info->table_len - 1; + return vsel; +} + +static struct regulator_ops twl4030ldo_ops = { + .list_voltage = twl4030ldo_list_voltage, + + .set_voltage_sel = twl4030ldo_set_voltage_sel, + .get_voltage_sel = twl4030ldo_get_voltage_sel, + + .enable = twl4030reg_enable, + .disable = twl4030reg_disable, + .is_enabled = twl4030reg_is_enabled, + + .set_mode = twl4030reg_set_mode, + + .get_status = twl4030reg_get_status, +}; + +static int +twl4030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, + unsigned *selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = DIV_ROUND_UP(min_uV - 600000, 12500); + + if (info->set_voltage) { + return info->set_voltage(info->data, min_uV); + } else { + twlreg_write(info, TWL_MODULE_PM_RECEIVER, + VREG_VOLTAGE_SMPS_4030, vsel); + } + + return 0; +} + +static int twl4030smps_get_voltage(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel; + + if (info->get_voltage) + return info->get_voltage(info->data); + + vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, + VREG_VOLTAGE_SMPS_4030); + + return vsel * 12500 + 600000; +} + +static struct regulator_ops twl4030smps_ops = { + .set_voltage = twl4030smps_set_voltage, + .get_voltage = twl4030smps_get_voltage, +}; + +static int twl6030coresmps_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + if (info->set_voltage) + return info->set_voltage(info->data, min_uV); + + return -ENODEV; +} + +static int twl6030coresmps_get_voltage(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + if (info->get_voltage) + return info->get_voltage(info->data); + + return -ENODEV; +} + +static struct regulator_ops twl6030coresmps_ops = { + .set_voltage = twl6030coresmps_set_voltage, + .get_voltage = twl6030coresmps_get_voltage, +}; + +static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned sel) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + switch (sel) { + case 0: + return 0; + case 1 ... 24: + /* Linear mapping from 00000001 to 00011000: + * Absolute voltage value = 1.0 V + 0.1 V × (sel – 00000001) + */ + return (info->min_mV + 100 * (sel - 1)) * 1000; + case 25 ... 30: + return -EINVAL; + case 31: + return 2750000; + default: + return -EINVAL; + } +} + +static int +twl6030ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, + selector); +} + +static int twl6030ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE); + + return vsel; +} + +static struct regulator_ops twl6030ldo_ops = { + .list_voltage = twl6030ldo_list_voltage, + + .set_voltage_sel = twl6030ldo_set_voltage_sel, + .get_voltage_sel = twl6030ldo_get_voltage_sel, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, +}; + +/*----------------------------------------------------------------------*/ + +static struct regulator_ops twl4030fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + + .enable = twl4030reg_enable, + .disable = twl4030reg_disable, + .is_enabled = twl4030reg_is_enabled, + + .set_mode = twl4030reg_set_mode, + + .get_status = twl4030reg_get_status, +}; + +static struct regulator_ops twl6030fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, +}; + +/* + * SMPS status and control + */ + +static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + int voltage = 0; + + switch (info->flags) { + case SMPS_OFFSET_EN: + voltage = 100000; + /* fall through */ + case 0: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 1350 * 1000; + break; + case 59: + voltage = 1500 * 1000; + break; + case 60: + voltage = 1800 * 1000; + break; + case 61: + voltage = 1900 * 1000; + break; + case 62: + voltage = 2100 * 1000; + break; + default: + voltage += (600000 + (12500 * (index - 1))); + } + break; + case SMPS_EXTENDED_EN: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 2084 * 1000; + break; + case 59: + voltage = 2315 * 1000; + break; + case 60: + voltage = 2778 * 1000; + break; + case 61: + voltage = 2932 * 1000; + break; + case 62: + voltage = 3241 * 1000; + break; + default: + voltage = (1852000 + (38600 * (index - 1))); + } + break; + case SMPS_OFFSET_EN | SMPS_EXTENDED_EN: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 4167 * 1000; + break; + case 59: + voltage = 2315 * 1000; + break; + case 60: + voltage = 2778 * 1000; + break; + case 61: + voltage = 2932 * 1000; + break; + case 62: + voltage = 3241 * 1000; + break; + default: + voltage = (2161000 + (38600 * (index - 1))); + } + break; + } + + return voltage; +} + +static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = 0; + + switch (info->flags) { + case 0: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 600000) && (min_uV <= 1300000)) { + vsel = DIV_ROUND_UP(min_uV - 600000, 12500); + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (min_uV <= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (min_uV <= 1900000)) + vsel = 61; + else if ((min_uV > 1500000) && (min_uV <= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (min_uV <= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (min_uV <= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_OFFSET_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 700000) && (min_uV <= 1420000)) { + vsel = DIV_ROUND_UP(min_uV - 700000, 12500); + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (min_uV <= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (min_uV <= 1900000)) + vsel = 61; + else if ((min_uV > 1350000) && (min_uV <= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (min_uV <= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (min_uV <= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_EXTENDED_EN: + if (min_uV == 0) { + vsel = 0; + } else if ((min_uV >= 1852000) && (max_uV <= 4013600)) { + vsel = DIV_ROUND_UP(min_uV - 1852000, 38600); + vsel++; + } + break; + case SMPS_OFFSET_EN|SMPS_EXTENDED_EN: + if (min_uV == 0) { + vsel = 0; + } else if ((min_uV >= 2161000) && (min_uV <= 4321000)) { + vsel = DIV_ROUND_UP(min_uV - 2161000, 38600); + vsel++; + } + break; + } + + return vsel; +} + +static int twl6030smps_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS, + selector); +} + +static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS); +} + +static struct regulator_ops twlsmps_ops = { + .list_voltage = twl6030smps_list_voltage, + .map_voltage = twl6030smps_map_voltage, + + .set_voltage_sel = twl6030smps_set_voltage_sel, + .get_voltage_sel = twl6030smps_get_voltage_sel, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, +}; + +/*----------------------------------------------------------------------*/ + +#define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ + remap_conf) \ + TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ + remap_conf, TWL4030, twl4030fixed_ops) +#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ + TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \ + 0x0, TWL6030, twl6030fixed_ops) + +#define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \ +static const struct twlreg_info TWL4030_INFO_##label = { \ + .base = offset, \ + .id = num, \ + .table_len = ARRAY_SIZE(label##_VSEL_table), \ + .table = label##_VSEL_table, \ + .remap = remap_conf, \ + .desc = { \ + .name = #label, \ + .id = TWL4030_REG_##label, \ + .n_voltages = ARRAY_SIZE(label##_VSEL_table), \ + .ops = &twl4030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_time = turnon_delay, \ + }, \ + } + +#define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf) \ +static const struct twlreg_info TWL4030_INFO_##label = { \ + .base = offset, \ + .id = num, \ + .remap = remap_conf, \ + .desc = { \ + .name = #label, \ + .id = TWL4030_REG_##label, \ + .ops = &twl4030smps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_time = turnon_delay, \ + }, \ + } + +#define TWL6030_ADJUSTABLE_SMPS(label) \ +static const struct twlreg_info TWL6030_INFO_##label = { \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .ops = &twl6030coresmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ +static const struct twlreg_info TWL6030_INFO_##label = { \ + .base = offset, \ + .min_mV = min_mVolts, \ + .max_mV = max_mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .n_voltages = 32, \ + .ops = &twl6030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ +static const struct twlreg_info TWL6025_INFO_##label = { \ + .base = offset, \ + .min_mV = min_mVolts, \ + .max_mV = max_mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL6025_REG_##label, \ + .n_voltages = 32, \ + .ops = &twl6030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \ + family, operations) \ +static const struct twlreg_info TWLFIXED_INFO_##label = { \ + .base = offset, \ + .id = num, \ + .min_mV = mVolts, \ + .remap = remap_conf, \ + .desc = { \ + .name = #label, \ + .id = family##_REG_##label, \ + .n_voltages = 1, \ + .ops = &operations, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = mVolts * 1000, \ + .enable_time = turnon_delay, \ + }, \ + } + +#define TWL6025_ADJUSTABLE_SMPS(label, offset) \ +static const struct twlreg_info TWLSMPS_INFO_##label = { \ + .base = offset, \ + .min_mV = 600, \ + .max_mV = 2100, \ + .desc = { \ + .name = #label, \ + .id = TWL6025_REG_##label, \ + .n_voltages = 63, \ + .ops = &twlsmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +/* + * We list regulators here if systems need some level of + * software control over them after boot. + */ +TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7, 100, 0x00); +TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9, 100, 0x00); +TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12, 100, 0x08); +TWL4030_ADJUSTABLE_LDO(VIO, 0x4b, 14, 1000, 0x08); +TWL4030_ADJUSTABLE_SMPS(VDD1, 0x55, 15, 1000, 0x08); +TWL4030_ADJUSTABLE_SMPS(VDD2, 0x63, 16, 1000, 0x08); +/* VUSBCP is managed *only* by the USB subchip */ +/* 6030 REG with base as PMC Slave Misc : 0x0030 */ +/* Turnon-delay and remap configuration values for 6030 are not + verified since the specification is not public */ +TWL6030_ADJUSTABLE_SMPS(VDD1); +TWL6030_ADJUSTABLE_SMPS(VDD2); +TWL6030_ADJUSTABLE_SMPS(VDD3); +TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300); +/* 6025 are renamed compared to 6030 versions */ +TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300); +TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300); +TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08); +TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08); +TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08); +TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08); +TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19, 150, 0x08); +TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0); +TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0); +TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0); +TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0); +TWL6030_FIXED_LDO(V1V8, 0x16, 1800, 0); +TWL6030_FIXED_LDO(V2V1, 0x1c, 2100, 0); +TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34); +TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10); +TWL6025_ADJUSTABLE_SMPS(VIO, 0x16); + +static u8 twl_get_smps_offset(void) +{ + u8 value; + + twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, + TWL6030_SMPS_OFFSET); + return value; +} + +static u8 twl_get_smps_mult(void) +{ + u8 value; + + twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, + TWL6030_SMPS_MULT); + return value; +} + +#define TWL_OF_MATCH(comp, family, label) \ + { \ + .compatible = comp, \ + .data = &family##_INFO_##label, \ + } + +#define TWL4030_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL4030, label) +#define TWL6030_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6030, label) +#define TWL6025_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6025, label) +#define TWLFIXED_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLFIXED, label) +#define TWLSMPS_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLSMPS, label) + +static const struct of_device_id twl_of_match[] = { + TWL4030_OF_MATCH("ti,twl4030-vaux1", VAUX1), + TWL4030_OF_MATCH("ti,twl4030-vaux2", VAUX2_4030), + TWL4030_OF_MATCH("ti,twl5030-vaux2", VAUX2), + TWL4030_OF_MATCH("ti,twl4030-vaux3", VAUX3), + TWL4030_OF_MATCH("ti,twl4030-vaux4", VAUX4), + TWL4030_OF_MATCH("ti,twl4030-vmmc1", VMMC1), + TWL4030_OF_MATCH("ti,twl4030-vmmc2", VMMC2), + TWL4030_OF_MATCH("ti,twl4030-vpll1", VPLL1), + TWL4030_OF_MATCH("ti,twl4030-vpll2", VPLL2), + TWL4030_OF_MATCH("ti,twl4030-vsim", VSIM), + TWL4030_OF_MATCH("ti,twl4030-vdac", VDAC), + TWL4030_OF_MATCH("ti,twl4030-vintana2", VINTANA2), + TWL4030_OF_MATCH("ti,twl4030-vio", VIO), + TWL4030_OF_MATCH("ti,twl4030-vdd1", VDD1), + TWL4030_OF_MATCH("ti,twl4030-vdd2", VDD2), + TWL6030_OF_MATCH("ti,twl6030-vdd1", VDD1), + TWL6030_OF_MATCH("ti,twl6030-vdd2", VDD2), + TWL6030_OF_MATCH("ti,twl6030-vdd3", VDD3), + TWL6030_OF_MATCH("ti,twl6030-vaux1", VAUX1_6030), + TWL6030_OF_MATCH("ti,twl6030-vaux2", VAUX2_6030), + TWL6030_OF_MATCH("ti,twl6030-vaux3", VAUX3_6030), + TWL6030_OF_MATCH("ti,twl6030-vmmc", VMMC), + TWL6030_OF_MATCH("ti,twl6030-vpp", VPP), + TWL6030_OF_MATCH("ti,twl6030-vusim", VUSIM), + TWL6025_OF_MATCH("ti,twl6025-ldo2", LDO2), + TWL6025_OF_MATCH("ti,twl6025-ldo4", LDO4), + TWL6025_OF_MATCH("ti,twl6025-ldo3", LDO3), + TWL6025_OF_MATCH("ti,twl6025-ldo5", LDO5), + TWL6025_OF_MATCH("ti,twl6025-ldo1", LDO1), + TWL6025_OF_MATCH("ti,twl6025-ldo7", LDO7), + TWL6025_OF_MATCH("ti,twl6025-ldo6", LDO6), + TWL6025_OF_MATCH("ti,twl6025-ldoln", LDOLN), + TWL6025_OF_MATCH("ti,twl6025-ldousb", LDOUSB), + TWLFIXED_OF_MATCH("ti,twl4030-vintana1", VINTANA1), + TWLFIXED_OF_MATCH("ti,twl4030-vintdig", VINTDIG), + TWLFIXED_OF_MATCH("ti,twl4030-vusb1v5", VUSB1V5), + TWLFIXED_OF_MATCH("ti,twl4030-vusb1v8", VUSB1V8), + TWLFIXED_OF_MATCH("ti,twl4030-vusb3v1", VUSB3V1), + TWLFIXED_OF_MATCH("ti,twl6030-vana", VANA), + TWLFIXED_OF_MATCH("ti,twl6030-vcxio", VCXIO), + TWLFIXED_OF_MATCH("ti,twl6030-vdac", VDAC), + TWLFIXED_OF_MATCH("ti,twl6030-vusb", VUSB), + TWLFIXED_OF_MATCH("ti,twl6030-v1v8", V1V8), + TWLFIXED_OF_MATCH("ti,twl6030-v2v1", V2V1), + TWLSMPS_OF_MATCH("ti,twl6025-smps3", SMPS3), + TWLSMPS_OF_MATCH("ti,twl6025-smps4", SMPS4), + TWLSMPS_OF_MATCH("ti,twl6025-vio", VIO), + {}, +}; +MODULE_DEVICE_TABLE(of, twl_of_match); + +static int twlreg_probe(struct platform_device *pdev) +{ + int i, id; + struct twlreg_info *info; + const struct twlreg_info *template; + struct regulator_init_data *initdata; + struct regulation_constraints *c; + struct regulator_dev *rdev; + struct twl_regulator_driver_data *drvdata; + const struct of_device_id *match; + struct regulator_config config = { }; + + match = of_match_device(twl_of_match, &pdev->dev); + if (match) { + template = match->data; + id = template->desc.id; + initdata = of_get_regulator_init_data(&pdev->dev, + pdev->dev.of_node); + drvdata = NULL; + } else { + id = pdev->id; + initdata = pdev->dev.platform_data; + for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) { + template = twl_of_match[i].data; + if (template && template->desc.id == id) + break; + } + if (i == ARRAY_SIZE(twl_of_match)) + return -ENODEV; + + drvdata = initdata->driver_data; + if (!drvdata) + return -EINVAL; + } + + if (!template) + return -ENODEV; + + if (!initdata) + return -EINVAL; + + info = kmemdup(template, sizeof (*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (drvdata) { + /* copy the driver data into regulator data */ + info->features = drvdata->features; + info->data = drvdata->data; + info->set_voltage = drvdata->set_voltage; + info->get_voltage = drvdata->get_voltage; + } + + /* Constrain board-specific capabilities according to what + * this driver and the chip itself can actually do. + */ + c = &initdata->constraints; + c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY; + c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS; + switch (id) { + case TWL4030_REG_VIO: + case TWL4030_REG_VDD1: + case TWL4030_REG_VDD2: + case TWL4030_REG_VPLL1: + case TWL4030_REG_VINTANA1: + case TWL4030_REG_VINTANA2: + case TWL4030_REG_VINTDIG: + c->always_on = true; + break; + default: + break; + } + + switch (id) { + case TWL6025_REG_SMPS3: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3) + info->flags |= SMPS_OFFSET_EN; + break; + case TWL6025_REG_SMPS4: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4) + info->flags |= SMPS_OFFSET_EN; + break; + case TWL6025_REG_VIO: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO) + info->flags |= SMPS_OFFSET_EN; + break; + } + + config.dev = &pdev->dev; + config.init_data = initdata; + config.driver_data = info; + config.of_node = pdev->dev.of_node; + + rdev = regulator_register(&info->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "can't register %s, %ld\n", + info->desc.name, PTR_ERR(rdev)); + kfree(info); + return PTR_ERR(rdev); + } + platform_set_drvdata(pdev, rdev); + + if (twl_class_is_4030()) + twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP, + info->remap); + + /* NOTE: many regulators support short-circuit IRQs (presentable + * as REGULATOR_OVER_CURRENT notifications?) configured via: + * - SC_CONFIG + * - SC_DETECT1 (vintana2, vmmc1/2, vaux1/2/3/4) + * - SC_DETECT2 (vusb, vdac, vio, vdd1/2, vpll2) + * - IT_CONFIG + */ + + return 0; +} + +static int twlreg_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct twlreg_info *info = rdev->reg_data; + + regulator_unregister(rdev); + kfree(info); + return 0; +} + +MODULE_ALIAS("platform:twl_reg"); + +static struct platform_driver twlreg_driver = { + .probe = twlreg_probe, + .remove = twlreg_remove, + /* NOTE: short name, to work around driver model truncation of + * "twl_regulator.12" (and friends) to "twl_regulator.1". + */ + .driver = { + .name = "twl_reg", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl_of_match), + }, +}; + +static int __init twlreg_init(void) +{ + return platform_driver_register(&twlreg_driver); +} +subsys_initcall(twlreg_init); + +static void __exit twlreg_exit(void) +{ + platform_driver_unregister(&twlreg_driver); +} +module_exit(twlreg_exit) + +MODULE_DESCRIPTION("TWL regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c new file mode 100644 index 0000000..a7c8deb --- /dev/null +++ b/drivers/regulator/userspace-consumer.c @@ -0,0 +1,186 @@ +/* + * userspace-consumer.c + * + * Copyright 2009 CompuLab, Ltd. + * + * Author: Mike Rapoport + * + * Based of virtual consumer driver: + * Copyright 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct userspace_consumer_data { + const char *name; + + struct mutex lock; + bool enabled; + + int num_supplies; + struct regulator_bulk_data *supplies; +}; + +static ssize_t reg_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct userspace_consumer_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t reg_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct userspace_consumer_data *data = dev_get_drvdata(dev); + + if (data->enabled) + return sprintf(buf, "enabled\n"); + + return sprintf(buf, "disabled\n"); +} + +static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct userspace_consumer_data *data = dev_get_drvdata(dev); + bool enabled; + int ret; + + /* + * sysfs_streq() doesn't need the \n's, but we add them so the strings + * will be shared with show_state(), above. + */ + if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) + enabled = true; + else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) + enabled = false; + else { + dev_err(dev, "Configuring invalid mode\n"); + return count; + } + + mutex_lock(&data->lock); + if (enabled != data->enabled) { + if (enabled) + ret = regulator_bulk_enable(data->num_supplies, + data->supplies); + else + ret = regulator_bulk_disable(data->num_supplies, + data->supplies); + + if (ret == 0) + data->enabled = enabled; + else + dev_err(dev, "Failed to configure state: %d\n", ret); + } + mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(name, 0444, reg_show_name, NULL); +static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state); + +static struct attribute *attributes[] = { + &dev_attr_name.attr, + &dev_attr_state.attr, + NULL, +}; + +static const struct attribute_group attr_group = { + .attrs = attributes, +}; + +static int regulator_userspace_consumer_probe(struct platform_device *pdev) +{ + struct regulator_userspace_consumer_data *pdata; + struct userspace_consumer_data *drvdata; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + + drvdata = devm_kzalloc(&pdev->dev, + sizeof(struct userspace_consumer_data), + GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + + drvdata->name = pdata->name; + drvdata->num_supplies = pdata->num_supplies; + drvdata->supplies = pdata->supplies; + + mutex_init(&drvdata->lock); + + ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies, + drvdata->supplies); + if (ret) { + dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); + if (ret != 0) + return ret; + + if (pdata->init_on) { + ret = regulator_bulk_enable(drvdata->num_supplies, + drvdata->supplies); + if (ret) { + dev_err(&pdev->dev, + "Failed to set initial state: %d\n", ret); + goto err_enable; + } + } + + drvdata->enabled = pdata->init_on; + platform_set_drvdata(pdev, drvdata); + + return 0; + +err_enable: + sysfs_remove_group(&pdev->dev.kobj, &attr_group); + + return ret; +} + +static int regulator_userspace_consumer_remove(struct platform_device *pdev) +{ + struct userspace_consumer_data *data = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &attr_group); + + if (data->enabled) + regulator_bulk_disable(data->num_supplies, data->supplies); + + return 0; +} + +static struct platform_driver regulator_userspace_consumer_driver = { + .probe = regulator_userspace_consumer_probe, + .remove = regulator_userspace_consumer_remove, + .driver = { + .name = "reg-userspace-consumer", + }, +}; + +module_platform_driver(regulator_userspace_consumer_driver); + +MODULE_AUTHOR("Mike Rapoport "); +MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c new file mode 100644 index 0000000..4668c7f --- /dev/null +++ b/drivers/regulator/vexpress.c @@ -0,0 +1,147 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-regulator" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vexpress_regulator { + struct regulator_desc desc; + struct regulator_dev *regdev; + struct vexpress_config_func *func; +}; + +static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + u32 uV; + int err = vexpress_config_read(reg->func, 0, &uV); + + return err ? err : uV; +} + +static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + + return vexpress_config_write(reg->func, 0, min_uV); +} + +static struct regulator_ops vexpress_regulator_ops_ro = { + .get_voltage = vexpress_regulator_get_voltage, +}; + +static struct regulator_ops vexpress_regulator_ops = { + .get_voltage = vexpress_regulator_get_voltage, + .set_voltage = vexpress_regulator_set_voltage, +}; + +static int vexpress_regulator_probe(struct platform_device *pdev) +{ + int err; + struct vexpress_regulator *reg; + struct regulator_init_data *init_data; + struct regulator_config config = { }; + + reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); + if (!reg) { + err = -ENOMEM; + goto error_kzalloc; + } + + reg->func = vexpress_config_func_get_by_dev(&pdev->dev); + if (!reg->func) { + err = -ENXIO; + goto error_get_func; + } + + reg->desc.name = dev_name(&pdev->dev); + reg->desc.type = REGULATOR_VOLTAGE; + reg->desc.owner = THIS_MODULE; + reg->desc.continuous_voltage_range = true; + + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + if (!init_data) { + err = -EINVAL; + goto error_get_regulator_init_data; + } + + init_data->constraints.apply_uV = 0; + if (init_data->constraints.min_uV && init_data->constraints.max_uV) + reg->desc.ops = &vexpress_regulator_ops; + else + reg->desc.ops = &vexpress_regulator_ops_ro; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = reg; + config.of_node = pdev->dev.of_node; + + reg->regdev = regulator_register(®->desc, &config); + if (IS_ERR(reg->regdev)) { + err = PTR_ERR(reg->regdev); + goto error_regulator_register; + } + + platform_set_drvdata(pdev, reg); + + return 0; + +error_regulator_register: +error_get_regulator_init_data: + vexpress_config_func_put(reg->func); +error_get_func: +error_kzalloc: + return err; +} + +static int vexpress_regulator_remove(struct platform_device *pdev) +{ + struct vexpress_regulator *reg = platform_get_drvdata(pdev); + + vexpress_config_func_put(reg->func); + regulator_unregister(reg->regdev); + + return 0; +} + +static struct of_device_id vexpress_regulator_of_match[] = { + { .compatible = "arm,vexpress-volt", }, + { } +}; + +static struct platform_driver vexpress_regulator_driver = { + .probe = vexpress_regulator_probe, + .remove = vexpress_regulator_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_regulator_of_match, + }, +}; + +module_platform_driver(vexpress_regulator_driver); + +MODULE_AUTHOR("Pawel Moll "); +MODULE_DESCRIPTION("Versatile Express regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-regulator"); diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c new file mode 100644 index 0000000..01c66e9 --- /dev/null +++ b/drivers/regulator/virtual.c @@ -0,0 +1,352 @@ +/* + * reg-virtual-consumer.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +struct virtual_consumer_data { + struct mutex lock; + struct regulator *regulator; + bool enabled; + int min_uV; + int max_uV; + int min_uA; + int max_uA; + unsigned int mode; +}; + +static void update_voltage_constraints(struct device *dev, + struct virtual_consumer_data *data) +{ + int ret; + + if (data->min_uV && data->max_uV + && data->min_uV <= data->max_uV) { + dev_dbg(dev, "Requesting %d-%duV\n", + data->min_uV, data->max_uV); + ret = regulator_set_voltage(data->regulator, + data->min_uV, data->max_uV); + if (ret != 0) { + dev_err(dev, + "regulator_set_voltage() failed: %d\n", ret); + return; + } + } + + if (data->min_uV && data->max_uV && !data->enabled) { + dev_dbg(dev, "Enabling regulator\n"); + ret = regulator_enable(data->regulator); + if (ret == 0) + data->enabled = true; + else + dev_err(dev, "regulator_enable() failed: %d\n", + ret); + } + + if (!(data->min_uV && data->max_uV) && data->enabled) { + dev_dbg(dev, "Disabling regulator\n"); + ret = regulator_disable(data->regulator); + if (ret == 0) + data->enabled = false; + else + dev_err(dev, "regulator_disable() failed: %d\n", + ret); + } +} + +static void update_current_limit_constraints(struct device *dev, + struct virtual_consumer_data *data) +{ + int ret; + + if (data->max_uA + && data->min_uA <= data->max_uA) { + dev_dbg(dev, "Requesting %d-%duA\n", + data->min_uA, data->max_uA); + ret = regulator_set_current_limit(data->regulator, + data->min_uA, data->max_uA); + if (ret != 0) { + dev_err(dev, + "regulator_set_current_limit() failed: %d\n", + ret); + return; + } + } + + if (data->max_uA && !data->enabled) { + dev_dbg(dev, "Enabling regulator\n"); + ret = regulator_enable(data->regulator); + if (ret == 0) + data->enabled = true; + else + dev_err(dev, "regulator_enable() failed: %d\n", + ret); + } + + if (!(data->min_uA && data->max_uA) && data->enabled) { + dev_dbg(dev, "Disabling regulator\n"); + ret = regulator_disable(data->regulator); + if (ret == 0) + data->enabled = false; + else + dev_err(dev, "regulator_disable() failed: %d\n", + ret); + } +} + +static ssize_t show_min_uV(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->min_uV); +} + +static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->min_uV = val; + update_voltage_constraints(dev, data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_max_uV(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->max_uV); +} + +static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->max_uV = val; + update_voltage_constraints(dev, data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_min_uA(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->min_uA); +} + +static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->min_uA = val; + update_current_limit_constraints(dev, data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_max_uA(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->max_uA); +} + +static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->max_uA = val; + update_current_limit_constraints(dev, data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + + switch (data->mode) { + case REGULATOR_MODE_FAST: + return sprintf(buf, "fast\n"); + case REGULATOR_MODE_NORMAL: + return sprintf(buf, "normal\n"); + case REGULATOR_MODE_IDLE: + return sprintf(buf, "idle\n"); + case REGULATOR_MODE_STANDBY: + return sprintf(buf, "standby\n"); + default: + return sprintf(buf, "unknown\n"); + } +} + +static ssize_t set_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + unsigned int mode; + int ret; + + /* + * sysfs_streq() doesn't need the \n's, but we add them so the strings + * will be shared with show_mode(), above. + */ + if (sysfs_streq(buf, "fast\n")) + mode = REGULATOR_MODE_FAST; + else if (sysfs_streq(buf, "normal\n")) + mode = REGULATOR_MODE_NORMAL; + else if (sysfs_streq(buf, "idle\n")) + mode = REGULATOR_MODE_IDLE; + else if (sysfs_streq(buf, "standby\n")) + mode = REGULATOR_MODE_STANDBY; + else { + dev_err(dev, "Configuring invalid mode\n"); + return count; + } + + mutex_lock(&data->lock); + ret = regulator_set_mode(data->regulator, mode); + if (ret == 0) + data->mode = mode; + else + dev_err(dev, "Failed to configure mode: %d\n", ret); + mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(min_microvolts, 0666, show_min_uV, set_min_uV); +static DEVICE_ATTR(max_microvolts, 0666, show_max_uV, set_max_uV); +static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA); +static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA); +static DEVICE_ATTR(mode, 0666, show_mode, set_mode); + +static struct attribute *regulator_virtual_attributes[] = { + &dev_attr_min_microvolts.attr, + &dev_attr_max_microvolts.attr, + &dev_attr_min_microamps.attr, + &dev_attr_max_microamps.attr, + &dev_attr_mode.attr, + NULL +}; + +static const struct attribute_group regulator_virtual_attr_group = { + .attrs = regulator_virtual_attributes, +}; + +static int regulator_virtual_probe(struct platform_device *pdev) +{ + char *reg_id = pdev->dev.platform_data; + struct virtual_consumer_data *drvdata; + int ret; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data), + GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + + mutex_init(&drvdata->lock); + + drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id); + if (IS_ERR(drvdata->regulator)) { + ret = PTR_ERR(drvdata->regulator); + dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n", + reg_id, ret); + return ret; + } + + ret = sysfs_create_group(&pdev->dev.kobj, + ®ulator_virtual_attr_group); + if (ret != 0) { + dev_err(&pdev->dev, + "Failed to create attribute group: %d\n", ret); + return ret; + } + + drvdata->mode = regulator_get_mode(drvdata->regulator); + + platform_set_drvdata(pdev, drvdata); + + return 0; +} + +static int regulator_virtual_remove(struct platform_device *pdev) +{ + struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, ®ulator_virtual_attr_group); + + if (drvdata->enabled) + regulator_disable(drvdata->regulator); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver regulator_virtual_consumer_driver = { + .probe = regulator_virtual_probe, + .remove = regulator_virtual_remove, + .driver = { + .name = "reg-virt-consumer", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(regulator_virtual_consumer_driver); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Virtual regulator consumer"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:reg-virt-consumer"); diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c new file mode 100644 index 0000000..0af6898 --- /dev/null +++ b/drivers/regulator/wm831x-dcdc.c @@ -0,0 +1,997 @@ +/* + * wm831x-dcdc.c -- DC-DC buck convertor driver for the WM831x series + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WM831X_BUCKV_MAX_SELECTOR 0x68 +#define WM831X_BUCKP_MAX_SELECTOR 0x66 + +#define WM831X_DCDC_MODE_FAST 0 +#define WM831X_DCDC_MODE_NORMAL 1 +#define WM831X_DCDC_MODE_IDLE 2 +#define WM831X_DCDC_MODE_STANDBY 3 + +#define WM831X_DCDC_MAX_NAME 9 + +/* Register offsets in control block */ +#define WM831X_DCDC_CONTROL_1 0 +#define WM831X_DCDC_CONTROL_2 1 +#define WM831X_DCDC_ON_CONFIG 2 +#define WM831X_DCDC_SLEEP_CONTROL 3 +#define WM831X_DCDC_DVS_CONTROL 4 + +/* + * Shared + */ + +struct wm831x_dcdc { + char name[WM831X_DCDC_MAX_NAME]; + char supply_name[WM831X_DCDC_MAX_NAME]; + struct regulator_desc desc; + int base; + struct wm831x *wm831x; + struct regulator_dev *regulator; + int dvs_gpio; + int dvs_gpio_state; + int on_vsel; + int dvs_vsel; +}; + +static unsigned int wm831x_dcdc_get_mode(struct regulator_dev *rdev) + +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; + int val; + + val = wm831x_reg_read(wm831x, reg); + if (val < 0) + return val; + + val = (val & WM831X_DC1_ON_MODE_MASK) >> WM831X_DC1_ON_MODE_SHIFT; + + switch (val) { + case WM831X_DCDC_MODE_FAST: + return REGULATOR_MODE_FAST; + case WM831X_DCDC_MODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case WM831X_DCDC_MODE_STANDBY: + return REGULATOR_MODE_STANDBY; + case WM831X_DCDC_MODE_IDLE: + return REGULATOR_MODE_IDLE; + default: + BUG(); + return -EINVAL; + } +} + +static int wm831x_dcdc_set_mode_int(struct wm831x *wm831x, int reg, + unsigned int mode) +{ + int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = WM831X_DCDC_MODE_FAST; + break; + case REGULATOR_MODE_NORMAL: + val = WM831X_DCDC_MODE_NORMAL; + break; + case REGULATOR_MODE_STANDBY: + val = WM831X_DCDC_MODE_STANDBY; + break; + case REGULATOR_MODE_IDLE: + val = WM831X_DCDC_MODE_IDLE; + break; + default: + return -EINVAL; + } + + return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_MODE_MASK, + val << WM831X_DC1_ON_MODE_SHIFT); +} + +static int wm831x_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; + + return wm831x_dcdc_set_mode_int(wm831x, reg, mode); +} + +static int wm831x_dcdc_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; + + return wm831x_dcdc_set_mode_int(wm831x, reg, mode); +} + +static int wm831x_dcdc_get_status(struct regulator_dev *rdev) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + int ret; + + /* First, check for errors */ + ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS); + if (ret < 0) + return ret; + + if (ret & (1 << rdev_get_id(rdev))) { + dev_dbg(wm831x->dev, "DCDC%d under voltage\n", + rdev_get_id(rdev) + 1); + return REGULATOR_STATUS_ERROR; + } + + /* DCDC1 and DCDC2 can additionally detect high voltage/current */ + if (rdev_get_id(rdev) < 2) { + if (ret & (WM831X_DC1_OV_STS << rdev_get_id(rdev))) { + dev_dbg(wm831x->dev, "DCDC%d over voltage\n", + rdev_get_id(rdev) + 1); + return REGULATOR_STATUS_ERROR; + } + + if (ret & (WM831X_DC1_HC_STS << rdev_get_id(rdev))) { + dev_dbg(wm831x->dev, "DCDC%d over current\n", + rdev_get_id(rdev) + 1); + return REGULATOR_STATUS_ERROR; + } + } + + /* Is the regulator on? */ + ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS); + if (ret < 0) + return ret; + if (!(ret & (1 << rdev_get_id(rdev)))) + return REGULATOR_STATUS_OFF; + + /* TODO: When we handle hardware control modes so we can report the + * current mode. */ + return REGULATOR_STATUS_ON; +} + +static irqreturn_t wm831x_dcdc_uv_irq(int irq, void *data) +{ + struct wm831x_dcdc *dcdc = data; + + regulator_notifier_call_chain(dcdc->regulator, + REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + + return IRQ_HANDLED; +} + +static irqreturn_t wm831x_dcdc_oc_irq(int irq, void *data) +{ + struct wm831x_dcdc *dcdc = data; + + regulator_notifier_call_chain(dcdc->regulator, + REGULATOR_EVENT_OVER_CURRENT, + NULL); + + return IRQ_HANDLED; +} + +/* + * BUCKV specifics + */ + +static int wm831x_buckv_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector <= 0x8) + return 600000; + if (selector <= WM831X_BUCKV_MAX_SELECTOR) + return 600000 + ((selector - 0x8) * 12500); + return -EINVAL; +} + +static int wm831x_buckv_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + u16 vsel; + + if (min_uV < 600000) + vsel = 0; + else if (min_uV <= 1800000) + vsel = DIV_ROUND_UP(min_uV - 600000, 12500) + 8; + else + return -EINVAL; + + if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV) + return -EINVAL; + + return vsel; +} + +static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + + if (state == dcdc->dvs_gpio_state) + return 0; + + dcdc->dvs_gpio_state = state; + gpio_set_value(dcdc->dvs_gpio, state); + + /* Should wait for DVS state change to be asserted if we have + * a GPIO for it, for now assume the device is configured + * for the fastest possible transition. + */ + + return 0; +} + +static int wm831x_buckv_set_voltage_sel(struct regulator_dev *rdev, + unsigned vsel) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG; + int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL; + int ret; + + /* If this value is already set then do a GPIO update if we can */ + if (dcdc->dvs_gpio && dcdc->on_vsel == vsel) + return wm831x_buckv_set_dvs(rdev, 0); + + if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel) + return wm831x_buckv_set_dvs(rdev, 1); + + /* Always set the ON status to the minimum voltage */ + ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel); + if (ret < 0) + return ret; + dcdc->on_vsel = vsel; + + if (!dcdc->dvs_gpio) + return ret; + + /* Kick the voltage transition now */ + ret = wm831x_buckv_set_dvs(rdev, 0); + if (ret < 0) + return ret; + + /* + * If this VSEL is higher than the last one we've seen then + * remember it as the DVS VSEL. This is optimised for CPUfreq + * usage where we want to get to the highest voltage very + * quickly. + */ + if (vsel > dcdc->dvs_vsel) { + ret = wm831x_set_bits(wm831x, dvs_reg, + WM831X_DC1_DVS_VSEL_MASK, + vsel); + if (ret == 0) + dcdc->dvs_vsel = vsel; + else + dev_warn(wm831x->dev, + "Failed to set DCDC DVS VSEL: %d\n", ret); + } + + return 0; +} + +static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev, + int uV) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; + int vsel; + + vsel = wm831x_buckv_map_voltage(rdev, uV, uV); + if (vsel < 0) + return vsel; + + return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel); +} + +static int wm831x_buckv_get_voltage_sel(struct regulator_dev *rdev) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + + if (dcdc->dvs_gpio && dcdc->dvs_gpio_state) + return dcdc->dvs_vsel; + else + return dcdc->on_vsel; +} + +/* Current limit options */ +static u16 wm831x_dcdc_ilim[] = { + 125, 250, 375, 500, 625, 750, 875, 1000 +}; + +static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2; + int i; + + for (i = ARRAY_SIZE(wm831x_dcdc_ilim) - 1; i >= 0; i--) { + if ((min_uA <= wm831x_dcdc_ilim[i]) && + (wm831x_dcdc_ilim[i] <= max_uA)) + return wm831x_set_bits(wm831x, reg, + WM831X_DC1_HC_THR_MASK, + i << WM831X_DC1_HC_THR_SHIFT); + } + + return -EINVAL; +} + +static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2; + int val; + + val = wm831x_reg_read(wm831x, reg); + if (val < 0) + return val; + + val = (val & WM831X_DC1_HC_THR_MASK) >> WM831X_DC1_HC_THR_SHIFT; + return wm831x_dcdc_ilim[val]; +} + +static struct regulator_ops wm831x_buckv_ops = { + .set_voltage_sel = wm831x_buckv_set_voltage_sel, + .get_voltage_sel = wm831x_buckv_get_voltage_sel, + .list_voltage = wm831x_buckv_list_voltage, + .map_voltage = wm831x_buckv_map_voltage, + .set_suspend_voltage = wm831x_buckv_set_suspend_voltage, + .set_current_limit = wm831x_buckv_set_current_limit, + .get_current_limit = wm831x_buckv_get_current_limit, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_status = wm831x_dcdc_get_status, + .get_mode = wm831x_dcdc_get_mode, + .set_mode = wm831x_dcdc_set_mode, + .set_suspend_mode = wm831x_dcdc_set_suspend_mode, +}; + +/* + * Set up DVS control. We just log errors since we can still run + * (with reduced performance) if we fail. + */ +static void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, + struct wm831x_buckv_pdata *pdata) +{ + struct wm831x *wm831x = dcdc->wm831x; + int ret; + u16 ctrl; + + if (!pdata || !pdata->dvs_gpio) + return; + + /* gpiolib won't let us read the GPIO status so pick the higher + * of the two existing voltages so we take it as platform data. + */ + dcdc->dvs_gpio_state = pdata->dvs_init_state; + + ret = gpio_request_one(pdata->dvs_gpio, + dcdc->dvs_gpio_state ? GPIOF_INIT_HIGH : 0, + "DCDC DVS"); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n", + dcdc->name, ret); + return; + } + + dcdc->dvs_gpio = pdata->dvs_gpio; + + switch (pdata->dvs_control_src) { + case 1: + ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT; + break; + case 2: + ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT; + break; + default: + dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n", + pdata->dvs_control_src, dcdc->name); + return; + } + + /* If DVS_VSEL is set to the minimum value then raise it to ON_VSEL + * to make bootstrapping a bit smoother. + */ + if (!dcdc->dvs_vsel) { + ret = wm831x_set_bits(wm831x, + dcdc->base + WM831X_DCDC_DVS_CONTROL, + WM831X_DC1_DVS_VSEL_MASK, dcdc->on_vsel); + if (ret == 0) + dcdc->dvs_vsel = dcdc->on_vsel; + else + dev_warn(wm831x->dev, "Failed to set DVS_VSEL: %d\n", + ret); + } + + ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL, + WM831X_DC1_DVS_SRC_MASK, ctrl); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n", + dcdc->name, ret); + } +} + +static int wm831x_buckv_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id; + struct wm831x_dcdc *dcdc; + struct resource *res; + int ret, irq; + + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + + dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); + + dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), + GFP_KERNEL); + if (dcdc == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + dcdc->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + dcdc->base = res->start; + + snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1); + dcdc->desc.name = dcdc->name; + + snprintf(dcdc->supply_name, sizeof(dcdc->supply_name), + "DC%dVDD", id + 1); + dcdc->desc.supply_name = dcdc->supply_name; + + dcdc->desc.id = id; + dcdc->desc.type = REGULATOR_VOLTAGE; + dcdc->desc.n_voltages = WM831X_BUCKV_MAX_SELECTOR + 1; + dcdc->desc.ops = &wm831x_buckv_ops; + dcdc->desc.owner = THIS_MODULE; + dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; + dcdc->desc.enable_mask = 1 << id; + + ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret); + goto err; + } + dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK; + + ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret); + goto err; + } + dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK; + + if (pdata && pdata->dcdc[id]) + wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data); + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->dcdc[id]; + config.driver_data = dcdc; + config.regmap = wm831x->regmap; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); + if (IS_ERR(dcdc->regulator)) { + ret = PTR_ERR(dcdc->regulator); + dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", + id + 1, ret); + goto err; + } + + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); + ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, + IRQF_TRIGGER_RISING, dcdc->name, dcdc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")); + ret = request_threaded_irq(irq, NULL, wm831x_dcdc_oc_irq, + IRQF_TRIGGER_RISING, dcdc->name, dcdc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n", + irq, ret); + goto err_uv; + } + + platform_set_drvdata(pdev, dcdc); + + return 0; + +err_uv: + free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); +err_regulator: + regulator_unregister(dcdc->regulator); +err: + if (dcdc->dvs_gpio) + gpio_free(dcdc->dvs_gpio); + return ret; +} + +static int wm831x_buckv_remove(struct platform_device *pdev) +{ + struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + struct wm831x *wm831x = dcdc->wm831x; + + platform_set_drvdata(pdev, NULL); + + free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), + dcdc); + free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); + regulator_unregister(dcdc->regulator); + if (dcdc->dvs_gpio) + gpio_free(dcdc->dvs_gpio); + + return 0; +} + +static struct platform_driver wm831x_buckv_driver = { + .probe = wm831x_buckv_probe, + .remove = wm831x_buckv_remove, + .driver = { + .name = "wm831x-buckv", + .owner = THIS_MODULE, + }, +}; + +/* + * BUCKP specifics + */ + +static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; + int sel; + + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return sel; + + return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, sel); +} + +static struct regulator_ops wm831x_buckp_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_suspend_voltage = wm831x_buckp_set_suspend_voltage, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_status = wm831x_dcdc_get_status, + .get_mode = wm831x_dcdc_get_mode, + .set_mode = wm831x_dcdc_set_mode, + .set_suspend_mode = wm831x_dcdc_set_suspend_mode, +}; + +static int wm831x_buckp_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id; + struct wm831x_dcdc *dcdc; + struct resource *res; + int ret, irq; + + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + + dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); + + dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), + GFP_KERNEL); + if (dcdc == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + dcdc->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + dcdc->base = res->start; + + snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1); + dcdc->desc.name = dcdc->name; + + snprintf(dcdc->supply_name, sizeof(dcdc->supply_name), + "DC%dVDD", id + 1); + dcdc->desc.supply_name = dcdc->supply_name; + + dcdc->desc.id = id; + dcdc->desc.type = REGULATOR_VOLTAGE; + dcdc->desc.n_voltages = WM831X_BUCKP_MAX_SELECTOR + 1; + dcdc->desc.ops = &wm831x_buckp_ops; + dcdc->desc.owner = THIS_MODULE; + dcdc->desc.vsel_reg = dcdc->base + WM831X_DCDC_ON_CONFIG; + dcdc->desc.vsel_mask = WM831X_DC3_ON_VSEL_MASK; + dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; + dcdc->desc.enable_mask = 1 << id; + dcdc->desc.min_uV = 850000; + dcdc->desc.uV_step = 25000; + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->dcdc[id]; + config.driver_data = dcdc; + config.regmap = wm831x->regmap; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); + if (IS_ERR(dcdc->regulator)) { + ret = PTR_ERR(dcdc->regulator); + dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", + id + 1, ret); + goto err; + } + + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); + ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, + IRQF_TRIGGER_RISING, dcdc->name, dcdc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + + platform_set_drvdata(pdev, dcdc); + + return 0; + +err_regulator: + regulator_unregister(dcdc->regulator); +err: + return ret; +} + +static int wm831x_buckp_remove(struct platform_device *pdev) +{ + struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); + regulator_unregister(dcdc->regulator); + + return 0; +} + +static struct platform_driver wm831x_buckp_driver = { + .probe = wm831x_buckp_probe, + .remove = wm831x_buckp_remove, + .driver = { + .name = "wm831x-buckp", + .owner = THIS_MODULE, + }, +}; + +/* + * DCDC boost convertors + */ + +static int wm831x_boostp_get_status(struct regulator_dev *rdev) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; + int ret; + + /* First, check for errors */ + ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS); + if (ret < 0) + return ret; + + if (ret & (1 << rdev_get_id(rdev))) { + dev_dbg(wm831x->dev, "DCDC%d under voltage\n", + rdev_get_id(rdev) + 1); + return REGULATOR_STATUS_ERROR; + } + + /* Is the regulator on? */ + ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS); + if (ret < 0) + return ret; + if (ret & (1 << rdev_get_id(rdev))) + return REGULATOR_STATUS_ON; + else + return REGULATOR_STATUS_OFF; +} + +static struct regulator_ops wm831x_boostp_ops = { + .get_status = wm831x_boostp_get_status, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +static int wm831x_boostp_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id = pdev->id % ARRAY_SIZE(pdata->dcdc); + struct wm831x_dcdc *dcdc; + struct resource *res; + int ret, irq; + + dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); + + if (pdata == NULL || pdata->dcdc[id] == NULL) + return -ENODEV; + + dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); + if (dcdc == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + dcdc->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + dcdc->base = res->start; + + snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1); + dcdc->desc.name = dcdc->name; + dcdc->desc.id = id; + dcdc->desc.type = REGULATOR_VOLTAGE; + dcdc->desc.ops = &wm831x_boostp_ops; + dcdc->desc.owner = THIS_MODULE; + dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; + dcdc->desc.enable_mask = 1 << id; + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->dcdc[id]; + config.driver_data = dcdc; + config.regmap = wm831x->regmap; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); + if (IS_ERR(dcdc->regulator)) { + ret = PTR_ERR(dcdc->regulator); + dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", + id + 1, ret); + goto err; + } + + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); + ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq, + IRQF_TRIGGER_RISING, dcdc->name, + dcdc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + + platform_set_drvdata(pdev, dcdc); + + return 0; + +err_regulator: + regulator_unregister(dcdc->regulator); +err: + return ret; +} + +static int wm831x_boostp_remove(struct platform_device *pdev) +{ + struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), + dcdc); + regulator_unregister(dcdc->regulator); + + return 0; +} + +static struct platform_driver wm831x_boostp_driver = { + .probe = wm831x_boostp_probe, + .remove = wm831x_boostp_remove, + .driver = { + .name = "wm831x-boostp", + .owner = THIS_MODULE, + }, +}; + +/* + * External Power Enable + * + * These aren't actually DCDCs but look like them in hardware so share + * code. + */ + +#define WM831X_EPE_BASE 6 + +static struct regulator_ops wm831x_epe_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_status = wm831x_dcdc_get_status, +}; + +static int wm831x_epe_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id = pdev->id % ARRAY_SIZE(pdata->epe); + struct wm831x_dcdc *dcdc; + int ret; + + dev_dbg(&pdev->dev, "Probing EPE%d\n", id + 1); + + dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); + if (dcdc == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + dcdc->wm831x = wm831x; + + /* For current parts this is correct; probably need to revisit + * in future. + */ + snprintf(dcdc->name, sizeof(dcdc->name), "EPE%d", id + 1); + dcdc->desc.name = dcdc->name; + dcdc->desc.id = id + WM831X_EPE_BASE; /* Offset in DCDC registers */ + dcdc->desc.ops = &wm831x_epe_ops; + dcdc->desc.type = REGULATOR_VOLTAGE; + dcdc->desc.owner = THIS_MODULE; + dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; + dcdc->desc.enable_mask = 1 << dcdc->desc.id; + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->epe[id]; + config.driver_data = dcdc; + config.regmap = wm831x->regmap; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); + if (IS_ERR(dcdc->regulator)) { + ret = PTR_ERR(dcdc->regulator); + dev_err(wm831x->dev, "Failed to register EPE%d: %d\n", + id + 1, ret); + goto err; + } + + platform_set_drvdata(pdev, dcdc); + + return 0; + +err: + return ret; +} + +static int wm831x_epe_remove(struct platform_device *pdev) +{ + struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(dcdc->regulator); + + return 0; +} + +static struct platform_driver wm831x_epe_driver = { + .probe = wm831x_epe_probe, + .remove = wm831x_epe_remove, + .driver = { + .name = "wm831x-epe", + .owner = THIS_MODULE, + }, +}; + +static int __init wm831x_dcdc_init(void) +{ + int ret; + ret = platform_driver_register(&wm831x_buckv_driver); + if (ret != 0) + pr_err("Failed to register WM831x BUCKV driver: %d\n", ret); + + ret = platform_driver_register(&wm831x_buckp_driver); + if (ret != 0) + pr_err("Failed to register WM831x BUCKP driver: %d\n", ret); + + ret = platform_driver_register(&wm831x_boostp_driver); + if (ret != 0) + pr_err("Failed to register WM831x BOOST driver: %d\n", ret); + + ret = platform_driver_register(&wm831x_epe_driver); + if (ret != 0) + pr_err("Failed to register WM831x EPE driver: %d\n", ret); + + return 0; +} +subsys_initcall(wm831x_dcdc_init); + +static void __exit wm831x_dcdc_exit(void) +{ + platform_driver_unregister(&wm831x_epe_driver); + platform_driver_unregister(&wm831x_boostp_driver); + platform_driver_unregister(&wm831x_buckp_driver); + platform_driver_unregister(&wm831x_buckv_driver); +} +module_exit(wm831x_dcdc_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown"); +MODULE_DESCRIPTION("WM831x DC-DC convertor driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-buckv"); +MODULE_ALIAS("platform:wm831x-buckp"); +MODULE_ALIAS("platform:wm831x-boostp"); +MODULE_ALIAS("platform:wm831x-epe"); diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c new file mode 100644 index 0000000..68586ee --- /dev/null +++ b/drivers/regulator/wm831x-isink.c @@ -0,0 +1,267 @@ +/* + * wm831x-isink.c -- Current sink driver for the WM831x series + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WM831X_ISINK_MAX_NAME 7 + +struct wm831x_isink { + char name[WM831X_ISINK_MAX_NAME]; + struct regulator_desc desc; + int reg; + struct wm831x *wm831x; + struct regulator_dev *regulator; +}; + +static int wm831x_isink_enable(struct regulator_dev *rdev) +{ + struct wm831x_isink *isink = rdev_get_drvdata(rdev); + struct wm831x *wm831x = isink->wm831x; + int ret; + + /* We have a two stage enable: first start the ISINK... */ + ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, + WM831X_CS1_ENA); + if (ret != 0) + return ret; + + /* ...then enable drive */ + ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, + WM831X_CS1_DRIVE); + if (ret != 0) + wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0); + + return ret; + +} + +static int wm831x_isink_disable(struct regulator_dev *rdev) +{ + struct wm831x_isink *isink = rdev_get_drvdata(rdev); + struct wm831x *wm831x = isink->wm831x; + int ret; + + ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0); + if (ret < 0) + return ret; + + ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0); + if (ret < 0) + return ret; + + return ret; + +} + +static int wm831x_isink_is_enabled(struct regulator_dev *rdev) +{ + struct wm831x_isink *isink = rdev_get_drvdata(rdev); + struct wm831x *wm831x = isink->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, isink->reg); + if (ret < 0) + return ret; + + if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) == + (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) + return 1; + else + return 0; +} + +static int wm831x_isink_set_current(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct wm831x_isink *isink = rdev_get_drvdata(rdev); + struct wm831x *wm831x = isink->wm831x; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(wm831x_isinkv_values); i++) { + int val = wm831x_isinkv_values[i]; + if (min_uA <= val && val <= max_uA) { + ret = wm831x_set_bits(wm831x, isink->reg, + WM831X_CS1_ISEL_MASK, i); + return ret; + } + } + + return -EINVAL; +} + +static int wm831x_isink_get_current(struct regulator_dev *rdev) +{ + struct wm831x_isink *isink = rdev_get_drvdata(rdev); + struct wm831x *wm831x = isink->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, isink->reg); + if (ret < 0) + return ret; + + ret &= WM831X_CS1_ISEL_MASK; + if (ret > WM831X_ISINK_MAX_ISEL) + ret = WM831X_ISINK_MAX_ISEL; + + return wm831x_isinkv_values[ret]; +} + +static struct regulator_ops wm831x_isink_ops = { + .is_enabled = wm831x_isink_is_enabled, + .enable = wm831x_isink_enable, + .disable = wm831x_isink_disable, + .set_current_limit = wm831x_isink_set_current, + .get_current_limit = wm831x_isink_get_current, +}; + +static irqreturn_t wm831x_isink_irq(int irq, void *data) +{ + struct wm831x_isink *isink = data; + + regulator_notifier_call_chain(isink->regulator, + REGULATOR_EVENT_OVER_CURRENT, + NULL); + + return IRQ_HANDLED; +} + + +static int wm831x_isink_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_isink *isink; + int id = pdev->id % ARRAY_SIZE(pdata->isink); + struct regulator_config config = { }; + struct resource *res; + int ret, irq; + + dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1); + + if (pdata == NULL || pdata->isink[id] == NULL) + return -ENODEV; + + isink = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_isink), + GFP_KERNEL); + if (isink == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + isink->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + isink->reg = res->start; + + /* For current parts this is correct; probably need to revisit + * in future. + */ + snprintf(isink->name, sizeof(isink->name), "ISINK%d", id + 1); + isink->desc.name = isink->name; + isink->desc.id = id; + isink->desc.ops = &wm831x_isink_ops; + isink->desc.type = REGULATOR_CURRENT; + isink->desc.owner = THIS_MODULE; + + config.dev = pdev->dev.parent; + config.init_data = pdata->isink[id]; + config.driver_data = isink; + + isink->regulator = regulator_register(&isink->desc, &config); + if (IS_ERR(isink->regulator)) { + ret = PTR_ERR(isink->regulator); + dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n", + id + 1, ret); + goto err; + } + + irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); + ret = request_threaded_irq(irq, NULL, wm831x_isink_irq, + IRQF_TRIGGER_RISING, isink->name, isink); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + + platform_set_drvdata(pdev, isink); + + return 0; + +err_regulator: + regulator_unregister(isink->regulator); +err: + return ret; +} + +static int wm831x_isink_remove(struct platform_device *pdev) +{ + struct wm831x_isink *isink = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); + + regulator_unregister(isink->regulator); + + return 0; +} + +static struct platform_driver wm831x_isink_driver = { + .probe = wm831x_isink_probe, + .remove = wm831x_isink_remove, + .driver = { + .name = "wm831x-isink", + .owner = THIS_MODULE, + }, +}; + +static int __init wm831x_isink_init(void) +{ + int ret; + ret = platform_driver_register(&wm831x_isink_driver); + if (ret != 0) + pr_err("Failed to register WM831x ISINK driver: %d\n", ret); + + return ret; +} +subsys_initcall(wm831x_isink_init); + +static void __exit wm831x_isink_exit(void) +{ + platform_driver_unregister(&wm831x_isink_driver); +} +module_exit(wm831x_isink_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown"); +MODULE_DESCRIPTION("WM831x current sink driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-isink"); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c new file mode 100644 index 0000000..1ec379a --- /dev/null +++ b/drivers/regulator/wm831x-ldo.c @@ -0,0 +1,793 @@ +/* + * wm831x-ldo.c -- LDO driver for the WM831x series + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WM831X_LDO_MAX_NAME 9 + +#define WM831X_LDO_CONTROL 0 +#define WM831X_LDO_ON_CONTROL 1 +#define WM831X_LDO_SLEEP_CONTROL 2 + +#define WM831X_ALIVE_LDO_ON_CONTROL 0 +#define WM831X_ALIVE_LDO_SLEEP_CONTROL 1 + +struct wm831x_ldo { + char name[WM831X_LDO_MAX_NAME]; + char supply_name[WM831X_LDO_MAX_NAME]; + struct regulator_desc desc; + int base; + struct wm831x *wm831x; + struct regulator_dev *regulator; +}; + +/* + * Shared + */ + +static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data) +{ + struct wm831x_ldo *ldo = data; + + regulator_notifier_call_chain(ldo->regulator, + REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + + return IRQ_HANDLED; +} + +/* + * General purpose LDOs + */ + +#define WM831X_GP_LDO_SELECTOR_LOW 0xe +#define WM831X_GP_LDO_MAX_SELECTOR 0x1f + +static int wm831x_gp_ldo_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + /* 0.9-1.6V in 50mV steps */ + if (selector <= WM831X_GP_LDO_SELECTOR_LOW) + return 900000 + (selector * 50000); + /* 1.7-3.3V in 100mV steps */ + if (selector <= WM831X_GP_LDO_MAX_SELECTOR) + return 1600000 + ((selector - WM831X_GP_LDO_SELECTOR_LOW) + * 100000); + return -EINVAL; +} + +static int wm831x_gp_ldo_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int volt, vsel; + + if (min_uV < 900000) + vsel = 0; + else if (min_uV < 1700000) + vsel = ((min_uV - 900000) / 50000); + else + vsel = ((min_uV - 1700000) / 100000) + + WM831X_GP_LDO_SELECTOR_LOW + 1; + + volt = wm831x_gp_ldo_list_voltage(rdev, vsel); + if (volt < min_uV || volt > max_uV) + return -EINVAL; + + return vsel; +} + +static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev, + int uV) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; + + sel = wm831x_gp_ldo_map_voltage(rdev, uV, uV); + if (sel < 0) + return sel; + + return wm831x_set_bits(wm831x, reg, WM831X_LDO1_ON_VSEL_MASK, sel); +} + +static unsigned int wm831x_gp_ldo_get_mode(struct regulator_dev *rdev) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int ctrl_reg = ldo->base + WM831X_LDO_CONTROL; + int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; + int ret; + + ret = wm831x_reg_read(wm831x, on_reg); + if (ret < 0) + return ret; + + if (!(ret & WM831X_LDO1_ON_MODE)) + return REGULATOR_MODE_NORMAL; + + ret = wm831x_reg_read(wm831x, ctrl_reg); + if (ret < 0) + return ret; + + if (ret & WM831X_LDO1_LP_MODE) + return REGULATOR_MODE_STANDBY; + else + return REGULATOR_MODE_IDLE; +} + +static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int ctrl_reg = ldo->base + WM831X_LDO_CONTROL; + int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; + int ret; + + + switch (mode) { + case REGULATOR_MODE_NORMAL: + ret = wm831x_set_bits(wm831x, on_reg, + WM831X_LDO1_ON_MODE, 0); + if (ret < 0) + return ret; + break; + + case REGULATOR_MODE_IDLE: + ret = wm831x_set_bits(wm831x, ctrl_reg, + WM831X_LDO1_LP_MODE, 0); + if (ret < 0) + return ret; + + ret = wm831x_set_bits(wm831x, on_reg, + WM831X_LDO1_ON_MODE, + WM831X_LDO1_ON_MODE); + if (ret < 0) + return ret; + break; + + case REGULATOR_MODE_STANDBY: + ret = wm831x_set_bits(wm831x, ctrl_reg, + WM831X_LDO1_LP_MODE, + WM831X_LDO1_LP_MODE); + if (ret < 0) + return ret; + + ret = wm831x_set_bits(wm831x, on_reg, + WM831X_LDO1_ON_MODE, + WM831X_LDO1_ON_MODE); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm831x_gp_ldo_get_status(struct regulator_dev *rdev) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int mask = 1 << rdev_get_id(rdev); + int ret; + + /* Is the regulator on? */ + ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS); + if (ret < 0) + return ret; + if (!(ret & mask)) + return REGULATOR_STATUS_OFF; + + /* Is it reporting under voltage? */ + ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS); + if (ret < 0) + return ret; + if (ret & mask) + return REGULATOR_STATUS_ERROR; + + ret = wm831x_gp_ldo_get_mode(rdev); + if (ret < 0) + return ret; + else + return regulator_mode_to_status(ret); +} + +static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, + int output_uV, int load_uA) +{ + if (load_uA < 20000) + return REGULATOR_MODE_STANDBY; + if (load_uA < 50000) + return REGULATOR_MODE_IDLE; + return REGULATOR_MODE_NORMAL; +} + + +static struct regulator_ops wm831x_gp_ldo_ops = { + .list_voltage = wm831x_gp_ldo_list_voltage, + .map_voltage = wm831x_gp_ldo_map_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage, + .get_mode = wm831x_gp_ldo_get_mode, + .set_mode = wm831x_gp_ldo_set_mode, + .get_status = wm831x_gp_ldo_get_status, + .get_optimum_mode = wm831x_gp_ldo_get_optimum_mode, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +static int wm831x_gp_ldo_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id; + struct wm831x_ldo *ldo; + struct resource *res; + int ret, irq; + + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); + + ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL); + if (ldo == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + ldo->base = res->start; + + snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1); + ldo->desc.name = ldo->name; + + snprintf(ldo->supply_name, sizeof(ldo->supply_name), + "LDO%dVDD", id + 1); + ldo->desc.supply_name = ldo->supply_name; + + ldo->desc.id = id; + ldo->desc.type = REGULATOR_VOLTAGE; + ldo->desc.n_voltages = WM831X_GP_LDO_MAX_SELECTOR + 1; + ldo->desc.ops = &wm831x_gp_ldo_ops; + ldo->desc.owner = THIS_MODULE; + ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL; + ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK; + ldo->desc.enable_reg = WM831X_LDO_ENABLE; + ldo->desc.enable_mask = 1 << id; + ldo->desc.bypass_reg = ldo->base; + ldo->desc.bypass_mask = WM831X_LDO1_SWI; + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->ldo[id]; + config.driver_data = ldo; + config.regmap = wm831x->regmap; + + ldo->regulator = regulator_register(&ldo->desc, &config); + if (IS_ERR(ldo->regulator)) { + ret = PTR_ERR(ldo->regulator); + dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", + id + 1, ret); + goto err; + } + + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); + ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq, + IRQF_TRIGGER_RISING, ldo->name, + ldo); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + + platform_set_drvdata(pdev, ldo); + + return 0; + +err_regulator: + regulator_unregister(ldo->regulator); +err: + return ret; +} + +static int wm831x_gp_ldo_remove(struct platform_device *pdev) +{ + struct wm831x_ldo *ldo = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + free_irq(wm831x_irq(ldo->wm831x, + platform_get_irq_byname(pdev, "UV")), ldo); + regulator_unregister(ldo->regulator); + + return 0; +} + +static struct platform_driver wm831x_gp_ldo_driver = { + .probe = wm831x_gp_ldo_probe, + .remove = wm831x_gp_ldo_remove, + .driver = { + .name = "wm831x-ldo", + .owner = THIS_MODULE, + }, +}; + +/* + * Analogue LDOs + */ + + +#define WM831X_ALDO_SELECTOR_LOW 0xc +#define WM831X_ALDO_MAX_SELECTOR 0x1f + +static int wm831x_aldo_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + /* 1-1.6V in 50mV steps */ + if (selector <= WM831X_ALDO_SELECTOR_LOW) + return 1000000 + (selector * 50000); + /* 1.7-3.5V in 100mV steps */ + if (selector <= WM831X_ALDO_MAX_SELECTOR) + return 1600000 + ((selector - WM831X_ALDO_SELECTOR_LOW) + * 100000); + return -EINVAL; +} + +static int wm831x_aldo_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int volt, vsel; + + if (min_uV < 1000000) + vsel = 0; + else if (min_uV < 1700000) + vsel = ((min_uV - 1000000) / 50000); + else + vsel = ((min_uV - 1700000) / 100000) + + WM831X_ALDO_SELECTOR_LOW + 1; + + volt = wm831x_aldo_list_voltage(rdev, vsel); + if (volt < min_uV || volt > max_uV) + return -EINVAL; + + return vsel; + +} + +static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev, + int uV) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; + + sel = wm831x_aldo_map_voltage(rdev, uV, uV); + if (sel < 0) + return sel; + + return wm831x_set_bits(wm831x, reg, WM831X_LDO7_ON_VSEL_MASK, sel); +} + +static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; + int ret; + + ret = wm831x_reg_read(wm831x, on_reg); + if (ret < 0) + return 0; + + if (ret & WM831X_LDO7_ON_MODE) + return REGULATOR_MODE_IDLE; + else + return REGULATOR_MODE_NORMAL; +} + +static int wm831x_aldo_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; + int ret; + + + switch (mode) { + case REGULATOR_MODE_NORMAL: + ret = wm831x_set_bits(wm831x, on_reg, WM831X_LDO7_ON_MODE, 0); + if (ret < 0) + return ret; + break; + + case REGULATOR_MODE_IDLE: + ret = wm831x_set_bits(wm831x, on_reg, WM831X_LDO7_ON_MODE, + WM831X_LDO7_ON_MODE); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm831x_aldo_get_status(struct regulator_dev *rdev) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int mask = 1 << rdev_get_id(rdev); + int ret; + + /* Is the regulator on? */ + ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS); + if (ret < 0) + return ret; + if (!(ret & mask)) + return REGULATOR_STATUS_OFF; + + /* Is it reporting under voltage? */ + ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS); + if (ret < 0) + return ret; + if (ret & mask) + return REGULATOR_STATUS_ERROR; + + ret = wm831x_aldo_get_mode(rdev); + if (ret < 0) + return ret; + else + return regulator_mode_to_status(ret); +} + +static struct regulator_ops wm831x_aldo_ops = { + .list_voltage = wm831x_aldo_list_voltage, + .map_voltage = wm831x_aldo_map_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_suspend_voltage = wm831x_aldo_set_suspend_voltage, + .get_mode = wm831x_aldo_get_mode, + .set_mode = wm831x_aldo_set_mode, + .get_status = wm831x_aldo_get_status, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +static int wm831x_aldo_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id; + struct wm831x_ldo *ldo; + struct resource *res; + int ret, irq; + + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); + + ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL); + if (ldo == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + ldo->base = res->start; + + snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1); + ldo->desc.name = ldo->name; + + snprintf(ldo->supply_name, sizeof(ldo->supply_name), + "LDO%dVDD", id + 1); + ldo->desc.supply_name = ldo->supply_name; + + ldo->desc.id = id; + ldo->desc.type = REGULATOR_VOLTAGE; + ldo->desc.n_voltages = WM831X_ALDO_MAX_SELECTOR + 1; + ldo->desc.ops = &wm831x_aldo_ops; + ldo->desc.owner = THIS_MODULE; + ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL; + ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK; + ldo->desc.enable_reg = WM831X_LDO_ENABLE; + ldo->desc.enable_mask = 1 << id; + ldo->desc.bypass_reg = ldo->base; + ldo->desc.bypass_mask = WM831X_LDO7_SWI; + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->ldo[id]; + config.driver_data = ldo; + config.regmap = wm831x->regmap; + + ldo->regulator = regulator_register(&ldo->desc, &config); + if (IS_ERR(ldo->regulator)) { + ret = PTR_ERR(ldo->regulator); + dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", + id + 1, ret); + goto err; + } + + irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")); + ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq, + IRQF_TRIGGER_RISING, ldo->name, ldo); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", + irq, ret); + goto err_regulator; + } + + platform_set_drvdata(pdev, ldo); + + return 0; + +err_regulator: + regulator_unregister(ldo->regulator); +err: + return ret; +} + +static int wm831x_aldo_remove(struct platform_device *pdev) +{ + struct wm831x_ldo *ldo = platform_get_drvdata(pdev); + + free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")), + ldo); + regulator_unregister(ldo->regulator); + + return 0; +} + +static struct platform_driver wm831x_aldo_driver = { + .probe = wm831x_aldo_probe, + .remove = wm831x_aldo_remove, + .driver = { + .name = "wm831x-aldo", + .owner = THIS_MODULE, + }, +}; + +/* + * Alive LDO + */ + +#define WM831X_ALIVE_LDO_MAX_SELECTOR 0xf + +static int wm831x_alive_ldo_set_suspend_voltage(struct regulator_dev *rdev, + int uV) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int sel, reg = ldo->base + WM831X_ALIVE_LDO_SLEEP_CONTROL; + + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return sel; + + return wm831x_set_bits(wm831x, reg, WM831X_LDO11_ON_VSEL_MASK, sel); +} + +static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev) +{ + struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); + struct wm831x *wm831x = ldo->wm831x; + int mask = 1 << rdev_get_id(rdev); + int ret; + + /* Is the regulator on? */ + ret = wm831x_reg_read(wm831x, WM831X_LDO_STATUS); + if (ret < 0) + return ret; + if (ret & mask) + return REGULATOR_STATUS_ON; + else + return REGULATOR_STATUS_OFF; +} + +static struct regulator_ops wm831x_alive_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_suspend_voltage = wm831x_alive_ldo_set_suspend_voltage, + .get_status = wm831x_alive_ldo_get_status, + + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + +static int wm831x_alive_ldo_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; + int id; + struct wm831x_ldo *ldo; + struct resource *res; + int ret; + + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + + + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); + + ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL); + if (ldo == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo->wm831x = wm831x; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No REG resource\n"); + ret = -EINVAL; + goto err; + } + ldo->base = res->start; + + snprintf(ldo->name, sizeof(ldo->name), "LDO%d", id + 1); + ldo->desc.name = ldo->name; + + snprintf(ldo->supply_name, sizeof(ldo->supply_name), + "LDO%dVDD", id + 1); + ldo->desc.supply_name = ldo->supply_name; + + ldo->desc.id = id; + ldo->desc.type = REGULATOR_VOLTAGE; + ldo->desc.n_voltages = WM831X_ALIVE_LDO_MAX_SELECTOR + 1; + ldo->desc.ops = &wm831x_alive_ldo_ops; + ldo->desc.owner = THIS_MODULE; + ldo->desc.vsel_reg = ldo->base + WM831X_ALIVE_LDO_ON_CONTROL; + ldo->desc.vsel_mask = WM831X_LDO11_ON_VSEL_MASK; + ldo->desc.enable_reg = WM831X_LDO_ENABLE; + ldo->desc.enable_mask = 1 << id; + ldo->desc.min_uV = 800000; + ldo->desc.uV_step = 50000; + ldo->desc.enable_time = 1000; + + config.dev = pdev->dev.parent; + if (pdata) + config.init_data = pdata->ldo[id]; + config.driver_data = ldo; + config.regmap = wm831x->regmap; + + ldo->regulator = regulator_register(&ldo->desc, &config); + if (IS_ERR(ldo->regulator)) { + ret = PTR_ERR(ldo->regulator); + dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", + id + 1, ret); + goto err; + } + + platform_set_drvdata(pdev, ldo); + + return 0; + +err: + return ret; +} + +static int wm831x_alive_ldo_remove(struct platform_device *pdev) +{ + struct wm831x_ldo *ldo = platform_get_drvdata(pdev); + + regulator_unregister(ldo->regulator); + + return 0; +} + +static struct platform_driver wm831x_alive_ldo_driver = { + .probe = wm831x_alive_ldo_probe, + .remove = wm831x_alive_ldo_remove, + .driver = { + .name = "wm831x-alive-ldo", + .owner = THIS_MODULE, + }, +}; + +static int __init wm831x_ldo_init(void) +{ + int ret; + + ret = platform_driver_register(&wm831x_gp_ldo_driver); + if (ret != 0) + pr_err("Failed to register WM831x GP LDO driver: %d\n", ret); + + ret = platform_driver_register(&wm831x_aldo_driver); + if (ret != 0) + pr_err("Failed to register WM831x ALDO driver: %d\n", ret); + + ret = platform_driver_register(&wm831x_alive_ldo_driver); + if (ret != 0) + pr_err("Failed to register WM831x alive LDO driver: %d\n", + ret); + + return 0; +} +subsys_initcall(wm831x_ldo_init); + +static void __exit wm831x_ldo_exit(void) +{ + platform_driver_unregister(&wm831x_alive_ldo_driver); + platform_driver_unregister(&wm831x_aldo_driver); + platform_driver_unregister(&wm831x_gp_ldo_driver); +} +module_exit(wm831x_ldo_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM831x LDO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-ldo"); +MODULE_ALIAS("platform:wm831x-aldo"); +MODULE_ALIAS("platform:wm831x-aliveldo"); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c new file mode 100644 index 0000000..7f0fa22 --- /dev/null +++ b/drivers/regulator/wm8350-regulator.c @@ -0,0 +1,1422 @@ +/* + * wm8350.c -- Voltage and current regulation for the Wolfson WM8350 PMIC + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum value possible for VSEL */ +#define WM8350_DCDC_MAX_VSEL 0x66 + +/* Microamps */ +static const int isink_cur[] = { + 4, + 5, + 6, + 7, + 8, + 10, + 11, + 14, + 16, + 19, + 23, + 27, + 32, + 39, + 46, + 54, + 65, + 77, + 92, + 109, + 130, + 154, + 183, + 218, + 259, + 308, + 367, + 436, + 518, + 616, + 733, + 872, + 1037, + 1233, + 1466, + 1744, + 2073, + 2466, + 2933, + 3487, + 4147, + 4932, + 5865, + 6975, + 8294, + 9864, + 11730, + 13949, + 16589, + 19728, + 23460, + 27899, + 33178, + 39455, + 46920, + 55798, + 66355, + 78910, + 93840, + 111596, + 132710, + 157820, + 187681, + 223191 +}; + +static int get_isink_val(int min_uA, int max_uA, u16 *setting) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(isink_cur); i++) { + if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) { + *setting = i; + return 0; + } + } + return -EINVAL; +} + +static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + u16 val, setting; + int ret; + + ret = get_isink_val(min_uA, max_uA, &setting); + if (ret != 0) + return ret; + + switch (isink) { + case WM8350_ISINK_A: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + ~WM8350_CS1_ISEL_MASK; + wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A, + val | setting); + break; + case WM8350_ISINK_B: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + ~WM8350_CS1_ISEL_MASK; + wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B, + val | setting); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_isink_get_current(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + u16 val; + + switch (isink) { + case WM8350_ISINK_A: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + WM8350_CS1_ISEL_MASK; + break; + case WM8350_ISINK_B: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + WM8350_CS1_ISEL_MASK; + break; + default: + return 0; + } + + return isink_cur[val]; +} + +/* turn on ISINK followed by DCDC */ +static int wm8350_isink_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + switch (wm8350->pmic.isink_A_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS1_ENA); + wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL, + WM8350_CS1_DRIVE); + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_A_dcdc - + WM8350_DCDC_1)); + break; + default: + return -EINVAL; + } + break; + case WM8350_ISINK_B: + switch (wm8350->pmic.isink_B_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS2_ENA); + wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL, + WM8350_CS2_DRIVE); + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_B_dcdc - + WM8350_DCDC_1)); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_isink_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + switch (wm8350->pmic.isink_A_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_A_dcdc - + WM8350_DCDC_1)); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS1_ENA); + break; + default: + return -EINVAL; + } + break; + case WM8350_ISINK_B: + switch (wm8350->pmic.isink_B_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_B_dcdc - + WM8350_DCDC_1)); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS2_ENA); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_isink_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + 0x8000; + case WM8350_ISINK_B: + return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + 0x8000; + } + return -EINVAL; +} + +static int wm8350_isink_enable_time(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + int reg; + + switch (isink) { + case WM8350_ISINK_A: + reg = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL); + break; + case WM8350_ISINK_B: + reg = wm8350_reg_read(wm8350, WM8350_CSB_FLASH_CONTROL); + break; + default: + return -EINVAL; + } + + if (reg & WM8350_CS1_FLASH_MODE) { + switch (reg & WM8350_CS1_ON_RAMP_MASK) { + case 0: + return 0; + case 1: + return 1950; + case 2: + return 3910; + case 3: + return 7800; + } + } else { + switch (reg & WM8350_CS1_ON_RAMP_MASK) { + case 0: + return 0; + case 1: + return 250000; + case 2: + return 500000; + case 3: + return 1000000; + } + } + + return -EINVAL; +} + + +int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, + u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, + u16 drive) +{ + switch (isink) { + case WM8350_ISINK_A: + wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, + (mode ? WM8350_CS1_FLASH_MODE : 0) | + (trigger ? WM8350_CS1_TRIGSRC : 0) | + duration | on_ramp | off_ramp | drive); + break; + case WM8350_ISINK_B: + wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL, + (mode ? WM8350_CS2_FLASH_MODE : 0) | + (trigger ? WM8350_CS2_TRIGSRC : 0) | + duration | on_ramp | off_ramp | drive); + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_isink_set_flash); + +static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int sel, volt_reg, dcdc = rdev_get_id(rdev); + u16 val; + + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, uV / 1000); + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_LOW_POWER; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_LOW_POWER; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_LOW_POWER; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_LOW_POWER; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return -EINVAL; + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, val | sel); + return 0; +} + +static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, + val | wm8350->pmic.dcdc1_hib_mode); + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, + val | wm8350->pmic.dcdc3_hib_mode); + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, + val | wm8350->pmic.dcdc4_hib_mode); + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, + val | wm8350->pmic.dcdc6_hib_mode); + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); + wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, + val | WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); + wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, + val | WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); + wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, + val | WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); + wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, + val | WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + (WM8350_DC2_HIB_MODE_ACTIVE << WM8350_DC2_HIB_MODE_SHIFT)); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~WM8350_DC5_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + (WM8350_DC5_HIB_MODE_ACTIVE << WM8350_DC5_HIB_MODE_SHIFT)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + (WM8350_DC2_HIB_MODE_DISABLE << WM8350_DC2_HIB_MODE_SHIFT)); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~WM8350_DC5_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + (WM8350_DC5_HIB_MODE_DISABLE << WM8350_DC5_HIB_MODE_SHIFT)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 *hib_mode; + + switch (dcdc) { + case WM8350_DCDC_1: + hib_mode = &wm8350->pmic.dcdc1_hib_mode; + break; + case WM8350_DCDC_3: + hib_mode = &wm8350->pmic.dcdc3_hib_mode; + break; + case WM8350_DCDC_4: + hib_mode = &wm8350->pmic.dcdc4_hib_mode; + break; + case WM8350_DCDC_6: + hib_mode = &wm8350->pmic.dcdc6_hib_mode; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_NORMAL: + *hib_mode = WM8350_DCDC_HIB_MODE_IMAGE; + break; + case REGULATOR_MODE_IDLE: + *hib_mode = WM8350_DCDC_HIB_MODE_STANDBY; + break; + case REGULATOR_MODE_STANDBY: + *hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector > WM8350_LDO1_VSEL_MASK) + return -EINVAL; + + if (selector < 16) + return (selector * 50000) + 900000; + else + return ((selector - 16) * 100000) + 1800000; +} + +static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + int volt, sel; + int min_mV = min_uV / 1000; + int max_mV = max_uV / 1000; + + if (min_mV < 900 || min_mV > 3300) + return -EINVAL; + if (max_mV < 900 || max_mV > 3300) + return -EINVAL; + + if (min_mV < 1800) /* step size is 50mV < 1800mV */ + sel = DIV_ROUND_UP(min_uV - 900, 50); + else /* step size is 100mV > 1800mV */ + sel = DIV_ROUND_UP(min_uV - 1800, 100) + 16; + + volt = wm8350_ldo_list_voltage(rdev, sel); + if (volt < min_uV || volt > max_uV) + return -EINVAL; + + return sel; +} + +static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int sel, volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, uV / 1000); + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + sel = wm8350_ldo_map_voltage(rdev, uV, uV); + if (sel < 0) + return -EINVAL; + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, val | sel); + return 0; +} + +static int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; + wm8350_reg_write(wm8350, volt_reg, val); + return 0; +} + +static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; + wm8350_reg_write(wm8350, volt_reg, val | WM8350_LDO1_HIB_MODE_DIS); + return 0; +} + +int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, + u16 stop, u16 fault) +{ + int slot_reg; + u16 val; + + dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", + __func__, dcdc, start, stop); + + /* slot valid ? */ + if (start > 15 || stop > 15) + return -EINVAL; + + switch (dcdc) { + case WM8350_DCDC_1: + slot_reg = WM8350_DCDC1_TIMEOUTS; + break; + case WM8350_DCDC_2: + slot_reg = WM8350_DCDC2_TIMEOUTS; + break; + case WM8350_DCDC_3: + slot_reg = WM8350_DCDC3_TIMEOUTS; + break; + case WM8350_DCDC_4: + slot_reg = WM8350_DCDC4_TIMEOUTS; + break; + case WM8350_DCDC_5: + slot_reg = WM8350_DCDC5_TIMEOUTS; + break; + case WM8350_DCDC_6: + slot_reg = WM8350_DCDC6_TIMEOUTS; + break; + default: + return -EINVAL; + } + + val = wm8350_reg_read(wm8350, slot_reg) & + ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK | + WM8350_DC1_ERRACT_MASK); + wm8350_reg_write(wm8350, slot_reg, + val | (start << WM8350_DC1_ENSLOT_SHIFT) | + (stop << WM8350_DC1_SDSLOT_SHIFT) | + (fault << WM8350_DC1_ERRACT_SHIFT)); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot); + +int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop) +{ + int slot_reg; + u16 val; + + dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", + __func__, ldo, start, stop); + + /* slot valid ? */ + if (start > 15 || stop > 15) + return -EINVAL; + + switch (ldo) { + case WM8350_LDO_1: + slot_reg = WM8350_LDO1_TIMEOUTS; + break; + case WM8350_LDO_2: + slot_reg = WM8350_LDO2_TIMEOUTS; + break; + case WM8350_LDO_3: + slot_reg = WM8350_LDO3_TIMEOUTS; + break; + case WM8350_LDO_4: + slot_reg = WM8350_LDO4_TIMEOUTS; + break; + default: + return -EINVAL; + } + + val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK; + wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6))); + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_ldo_set_slot); + +int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, + u16 ilim, u16 ramp, u16 feedback) +{ + u16 val; + + dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc, + mode ? "normal" : "boost", ilim ? "low" : "normal"); + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK | + WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK); + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + (mode << WM8350_DC2_MODE_SHIFT) | + (ilim << WM8350_DC2_ILIM_SHIFT) | + (ramp << WM8350_DC2_RMP_SHIFT) | + (feedback << WM8350_DC2_FBSRC_SHIFT)); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK | + WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK); + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + (mode << WM8350_DC5_MODE_SHIFT) | + (ilim << WM8350_DC5_ILIM_SHIFT) | + (ramp << WM8350_DC5_RMP_SHIFT) | + (feedback << WM8350_DC5_FBSRC_SHIFT)); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode); + +static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable) +{ + int reg = 0, ret; + + switch (dcdc) { + case WM8350_DCDC_1: + reg = WM8350_DCDC1_FORCE_PWM; + break; + case WM8350_DCDC_3: + reg = WM8350_DCDC3_FORCE_PWM; + break; + case WM8350_DCDC_4: + reg = WM8350_DCDC4_FORCE_PWM; + break; + case WM8350_DCDC_6: + reg = WM8350_DCDC6_FORCE_PWM; + break; + default: + return -EINVAL; + } + + if (enable) + ret = wm8350_set_bits(wm8350, reg, + WM8350_DCDC1_FORCE_PWM_ENA); + else + ret = wm8350_clear_bits(wm8350, reg, + WM8350_DCDC1_FORCE_PWM_ENA); + return ret; +} + +static int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) + return -EINVAL; + + val = 1 << (dcdc - WM8350_DCDC_1); + + switch (mode) { + case REGULATOR_MODE_FAST: + /* force continuous mode */ + wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + force_continuous_enable(wm8350, dcdc, 1); + break; + case REGULATOR_MODE_NORMAL: + /* active / pulse skipping */ + wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + force_continuous_enable(wm8350, dcdc, 0); + break; + case REGULATOR_MODE_IDLE: + /* standby mode */ + force_continuous_enable(wm8350, dcdc, 0); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + break; + case REGULATOR_MODE_STANDBY: + /* LDO mode */ + force_continuous_enable(wm8350, dcdc, 0); + wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + break; + } + + return 0; +} + +static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 mask, sleep, active, force; + int mode = REGULATOR_MODE_NORMAL; + int reg; + + switch (dcdc) { + case WM8350_DCDC_1: + reg = WM8350_DCDC1_FORCE_PWM; + break; + case WM8350_DCDC_3: + reg = WM8350_DCDC3_FORCE_PWM; + break; + case WM8350_DCDC_4: + reg = WM8350_DCDC4_FORCE_PWM; + break; + case WM8350_DCDC_6: + reg = WM8350_DCDC6_FORCE_PWM; + break; + default: + return -EINVAL; + } + + mask = 1 << (dcdc - WM8350_DCDC_1); + active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask; + force = wm8350_reg_read(wm8350, reg) & WM8350_DCDC1_FORCE_PWM_ENA; + sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask; + + dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x", + mask, active, sleep, force); + + if (active && !sleep) { + if (force) + mode = REGULATOR_MODE_FAST; + else + mode = REGULATOR_MODE_NORMAL; + } else if (!active && !sleep) + mode = REGULATOR_MODE_IDLE; + else if (sleep) + mode = REGULATOR_MODE_STANDBY; + + return mode; +} + +static unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev) +{ + return REGULATOR_MODE_NORMAL; +} + +struct wm8350_dcdc_efficiency { + int uA_load_min; + int uA_load_max; + unsigned int mode; +}; + +static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = { + {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ + {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ + {100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ + {-1, -1, REGULATOR_MODE_NORMAL}, +}; + +static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = { + {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ + {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ + {100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ + {-1, -1, REGULATOR_MODE_NORMAL}, +}; + +static unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff) +{ + int i = 0; + + while (eff[i].uA_load_min != -1) { + if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max) + return eff[i].mode; + } + return REGULATOR_MODE_NORMAL; +} + +/* Query the regulator for it's most efficient mode @ uV,uA + * WM8350 regulator efficiency is pretty similar over + * different input and output uV. + */ +static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, + int output_uA) +{ + int dcdc = rdev_get_id(rdev), mode; + + switch (dcdc) { + case WM8350_DCDC_1: + case WM8350_DCDC_6: + mode = get_mode(output_uA, dcdc1_6_efficiency); + break; + case WM8350_DCDC_3: + case WM8350_DCDC_4: + mode = get_mode(output_uA, dcdc3_4_efficiency); + break; + default: + mode = REGULATOR_MODE_NORMAL; + break; + } + return mode; +} + +static struct regulator_ops wm8350_dcdc_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_mode = wm8350_dcdc_get_mode, + .set_mode = wm8350_dcdc_set_mode, + .get_optimum_mode = wm8350_dcdc_get_optimum_mode, + .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage, + .set_suspend_enable = wm8350_dcdc_set_suspend_enable, + .set_suspend_disable = wm8350_dcdc_set_suspend_disable, + .set_suspend_mode = wm8350_dcdc_set_suspend_mode, +}; + +static struct regulator_ops wm8350_dcdc2_5_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = wm8350_dcdc25_set_suspend_enable, + .set_suspend_disable = wm8350_dcdc25_set_suspend_disable, +}; + +static struct regulator_ops wm8350_ldo_ops = { + .map_voltage = wm8350_ldo_map_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = wm8350_ldo_list_voltage, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_mode = wm8350_ldo_get_mode, + .set_suspend_voltage = wm8350_ldo_set_suspend_voltage, + .set_suspend_enable = wm8350_ldo_set_suspend_enable, + .set_suspend_disable = wm8350_ldo_set_suspend_disable, +}; + +static struct regulator_ops wm8350_isink_ops = { + .set_current_limit = wm8350_isink_set_current, + .get_current_limit = wm8350_isink_get_current, + .enable = wm8350_isink_enable, + .disable = wm8350_isink_disable, + .is_enabled = wm8350_isink_is_enabled, + .enable_time = wm8350_isink_enable_time, +}; + +static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { + { + .name = "DCDC1", + .id = WM8350_DCDC_1, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC1, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, + .vsel_reg = WM8350_DCDC1_CONTROL, + .vsel_mask = WM8350_DC1_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC1_ENA, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = WM8350_DCDC_2, + .ops = &wm8350_dcdc2_5_ops, + .irq = WM8350_IRQ_UV_DC2, + .type = REGULATOR_VOLTAGE, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC2_ENA, + .owner = THIS_MODULE, + }, + { + .name = "DCDC3", + .id = WM8350_DCDC_3, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC3, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, + .vsel_reg = WM8350_DCDC3_CONTROL, + .vsel_mask = WM8350_DC3_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC3_ENA, + .owner = THIS_MODULE, + }, + { + .name = "DCDC4", + .id = WM8350_DCDC_4, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC4, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, + .vsel_reg = WM8350_DCDC4_CONTROL, + .vsel_mask = WM8350_DC4_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC4_ENA, + .owner = THIS_MODULE, + }, + { + .name = "DCDC5", + .id = WM8350_DCDC_5, + .ops = &wm8350_dcdc2_5_ops, + .irq = WM8350_IRQ_UV_DC5, + .type = REGULATOR_VOLTAGE, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC5_ENA, + .owner = THIS_MODULE, + }, + { + .name = "DCDC6", + .id = WM8350_DCDC_6, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC6, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, + .vsel_reg = WM8350_DCDC6_CONTROL, + .vsel_mask = WM8350_DC6_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC6_ENA, + .owner = THIS_MODULE, + }, + { + .name = "LDO1", + .id = WM8350_LDO_1, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO1, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO1_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO1_CONTROL, + .vsel_mask = WM8350_LDO1_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO1_ENA, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = WM8350_LDO_2, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO2, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO2_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO2_CONTROL, + .vsel_mask = WM8350_LDO2_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO2_ENA, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = WM8350_LDO_3, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO3, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO3_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO3_CONTROL, + .vsel_mask = WM8350_LDO3_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO3_ENA, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = WM8350_LDO_4, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO4, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO4_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO4_CONTROL, + .vsel_mask = WM8350_LDO4_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO4_ENA, + .owner = THIS_MODULE, + }, + { + .name = "ISINKA", + .id = WM8350_ISINK_A, + .ops = &wm8350_isink_ops, + .irq = WM8350_IRQ_CS1, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, + { + .name = "ISINKB", + .id = WM8350_ISINK_B, + .ops = &wm8350_isink_ops, + .irq = WM8350_IRQ_CS2, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, +}; + +static irqreturn_t pmic_uv_handler(int irq, void *data) +{ + struct regulator_dev *rdev = (struct regulator_dev *)data; + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + + mutex_lock(&rdev->mutex); + if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_REGULATION_OUT, + wm8350); + else + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_UNDER_VOLTAGE, + wm8350); + mutex_unlock(&rdev->mutex); + + return IRQ_HANDLED; +} + +static int wm8350_regulator_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + struct regulator_config config = { }; + struct regulator_dev *rdev; + int ret; + u16 val; + + if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B) + return -ENODEV; + + /* do any regulatior specific init */ + switch (pdev->id) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); + wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); + wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); + wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); + wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + } + + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = dev_get_drvdata(&pdev->dev); + config.regmap = wm8350->regmap; + + /* register regulator */ + rdev = regulator_register(&wm8350_reg[pdev->id], &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", + wm8350_reg[pdev->id].name); + return PTR_ERR(rdev); + } + + /* register regulator IRQ */ + ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq, + pmic_uv_handler, 0, "UV", rdev); + if (ret < 0) { + regulator_unregister(rdev); + dev_err(&pdev->dev, "failed to register regulator %s IRQ\n", + wm8350_reg[pdev->id].name); + return ret; + } + + return 0; +} + +static int wm8350_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + + wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq, rdev); + + regulator_unregister(rdev); + + return 0; +} + +int wm8350_register_regulator(struct wm8350 *wm8350, int reg, + struct regulator_init_data *initdata) +{ + struct platform_device *pdev; + int ret; + if (reg < 0 || reg >= NUM_WM8350_REGULATORS) + return -EINVAL; + + if (wm8350->pmic.pdev[reg]) + return -EBUSY; + + if (reg >= WM8350_DCDC_1 && reg <= WM8350_DCDC_6 && + reg > wm8350->pmic.max_dcdc) + return -ENODEV; + if (reg >= WM8350_ISINK_A && reg <= WM8350_ISINK_B && + reg > wm8350->pmic.max_isink) + return -ENODEV; + + pdev = platform_device_alloc("wm8350-regulator", reg); + if (!pdev) + return -ENOMEM; + + wm8350->pmic.pdev[reg] = pdev; + + initdata->driver_data = wm8350; + + pdev->dev.platform_data = initdata; + pdev->dev.parent = wm8350->dev; + platform_set_drvdata(pdev, wm8350); + + ret = platform_device_add(pdev); + + if (ret != 0) { + dev_err(wm8350->dev, "Failed to register regulator %d: %d\n", + reg, ret); + platform_device_put(pdev); + wm8350->pmic.pdev[reg] = NULL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_register_regulator); + +/** + * wm8350_register_led - Register a WM8350 LED output + * + * @param wm8350 The WM8350 device to configure. + * @param lednum LED device index to create. + * @param dcdc The DCDC to use for the LED. + * @param isink The ISINK to use for the LED. + * @param pdata Configuration for the LED. + * + * The WM8350 supports the use of an ISINK together with a DCDC to + * provide a power-efficient LED driver. This function registers the + * regulators and instantiates the platform device for a LED. The + * operating modes for the LED regulators must be configured using + * wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and + * wm8350_dcdc_set_slot() prior to calling this function. + */ +int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink, + struct wm8350_led_platform_data *pdata) +{ + struct wm8350_led *led; + struct platform_device *pdev; + int ret; + + if (lednum >= ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) { + dev_err(wm8350->dev, "Invalid LED index %d\n", lednum); + return -ENODEV; + } + + led = &wm8350->pmic.led[lednum]; + + if (led->pdev) { + dev_err(wm8350->dev, "LED %d already allocated\n", lednum); + return -EINVAL; + } + + pdev = platform_device_alloc("wm8350-led", lednum); + if (pdev == NULL) { + dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum); + return -ENOMEM; + } + + led->isink_consumer.dev_name = dev_name(&pdev->dev); + led->isink_consumer.supply = "led_isink"; + led->isink_init.num_consumer_supplies = 1; + led->isink_init.consumer_supplies = &led->isink_consumer; + led->isink_init.constraints.min_uA = 0; + led->isink_init.constraints.max_uA = pdata->max_uA; + led->isink_init.constraints.valid_ops_mask + = REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_STATUS; + led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL; + ret = wm8350_register_regulator(wm8350, isink, &led->isink_init); + if (ret != 0) { + platform_device_put(pdev); + return ret; + } + + led->dcdc_consumer.dev_name = dev_name(&pdev->dev); + led->dcdc_consumer.supply = "led_vcc"; + led->dcdc_init.num_consumer_supplies = 1; + led->dcdc_init.consumer_supplies = &led->dcdc_consumer; + led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL; + led->dcdc_init.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; + ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init); + if (ret != 0) { + platform_device_put(pdev); + return ret; + } + + switch (isink) { + case WM8350_ISINK_A: + wm8350->pmic.isink_A_dcdc = dcdc; + break; + case WM8350_ISINK_B: + wm8350->pmic.isink_B_dcdc = dcdc; + break; + } + + pdev->dev.platform_data = pdata; + pdev->dev.parent = wm8350->dev; + ret = platform_device_add(pdev); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to register LED %d: %d\n", + lednum, ret); + platform_device_put(pdev); + return ret; + } + + led->pdev = pdev; + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_register_led); + +static struct platform_driver wm8350_regulator_driver = { + .probe = wm8350_regulator_probe, + .remove = wm8350_regulator_remove, + .driver = { + .name = "wm8350-regulator", + }, +}; + +static int __init wm8350_regulator_init(void) +{ + return platform_driver_register(&wm8350_regulator_driver); +} +subsys_initcall(wm8350_regulator_init); + +static void __exit wm8350_regulator_exit(void) +{ + platform_driver_unregister(&wm8350_regulator_driver); +} +module_exit(wm8350_regulator_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("WM8350 voltage and current regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-regulator"); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c new file mode 100644 index 0000000..c6a32ea --- /dev/null +++ b/drivers/regulator/wm8400-regulator.c @@ -0,0 +1,312 @@ +/* + * Regulator support for WM8400 + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +static int wm8400_ldo_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + if (selector > WM8400_LDO1_VSEL_MASK) + return -EINVAL; + + if (selector < 15) + return 900000 + (selector * 50000); + else + return 1700000 + ((selector - 15) * 100000); +} + +static int wm8400_ldo_map_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + u16 val; + int volt; + + if (min_uV < 900000 || min_uV > 3300000) + return -EINVAL; + + if (min_uV < 1700000) /* Steps of 50mV from 900mV; */ + val = DIV_ROUND_UP(min_uV - 900000, 50000); + else /* Steps of 100mV from 1700mV */ + val = DIV_ROUND_UP(min_uV - 1700000, 100000) + 15; + + volt = wm8400_ldo_list_voltage(dev, val); + if (volt < min_uV || volt > max_uV) + return -EINVAL; + + return val; +} + +static struct regulator_ops wm8400_ldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = wm8400_ldo_list_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .map_voltage = wm8400_ldo_map_voltage, +}; + +static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + u16 data[2]; + int ret; + + ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2, + data); + if (ret != 0) + return 0; + + /* Datasheet: hibernate */ + if (data[0] & WM8400_DC1_SLEEP) + return REGULATOR_MODE_STANDBY; + + /* Datasheet: standby */ + if (!(data[0] & WM8400_DC1_ACTIVE)) + return REGULATOR_MODE_IDLE; + + /* Datasheet: active with or without force PWM */ + if (data[1] & WM8400_DC1_FRC_PWM) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + int ret; + + switch (mode) { + case REGULATOR_MODE_FAST: + /* Datasheet: active with force PWM */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, + WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM); + if (ret != 0) + return ret; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, + WM8400_DC1_ACTIVE); + + case REGULATOR_MODE_NORMAL: + /* Datasheet: active */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, + WM8400_DC1_FRC_PWM, 0); + if (ret != 0) + return ret; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, + WM8400_DC1_ACTIVE); + + case REGULATOR_MODE_IDLE: + /* Datasheet: standby */ + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0); + default: + return -EINVAL; + } +} + +static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev, + int input_uV, int output_uV, + int load_uA) +{ + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops wm8400_dcdc_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_mode = wm8400_dcdc_get_mode, + .set_mode = wm8400_dcdc_set_mode, + .get_optimum_mode = wm8400_dcdc_get_optimum_mode, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = WM8400_LDO1, + .ops = &wm8400_ldo_ops, + .enable_reg = WM8400_LDO1_CONTROL, + .enable_mask = WM8400_LDO1_ENA, + .n_voltages = WM8400_LDO1_VSEL_MASK + 1, + .vsel_reg = WM8400_LDO1_CONTROL, + .vsel_mask = WM8400_LDO1_VSEL_MASK, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = WM8400_LDO2, + .ops = &wm8400_ldo_ops, + .enable_reg = WM8400_LDO2_CONTROL, + .enable_mask = WM8400_LDO2_ENA, + .n_voltages = WM8400_LDO2_VSEL_MASK + 1, + .type = REGULATOR_VOLTAGE, + .vsel_reg = WM8400_LDO2_CONTROL, + .vsel_mask = WM8400_LDO2_VSEL_MASK, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = WM8400_LDO3, + .ops = &wm8400_ldo_ops, + .enable_reg = WM8400_LDO3_CONTROL, + .enable_mask = WM8400_LDO3_ENA, + .n_voltages = WM8400_LDO3_VSEL_MASK + 1, + .vsel_reg = WM8400_LDO3_CONTROL, + .vsel_mask = WM8400_LDO3_VSEL_MASK, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = WM8400_LDO4, + .ops = &wm8400_ldo_ops, + .enable_reg = WM8400_LDO4_CONTROL, + .enable_mask = WM8400_LDO4_ENA, + .n_voltages = WM8400_LDO4_VSEL_MASK + 1, + .vsel_reg = WM8400_LDO4_CONTROL, + .vsel_mask = WM8400_LDO4_VSEL_MASK, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC1", + .id = WM8400_DCDC1, + .ops = &wm8400_dcdc_ops, + .enable_reg = WM8400_DCDC1_CONTROL_1, + .enable_mask = WM8400_DC1_ENA_MASK, + .n_voltages = WM8400_DC1_VSEL_MASK + 1, + .vsel_reg = WM8400_DCDC1_CONTROL_1, + .vsel_mask = WM8400_DC1_VSEL_MASK, + .min_uV = 850000, + .uV_step = 25000, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = WM8400_DCDC2, + .ops = &wm8400_dcdc_ops, + .enable_reg = WM8400_DCDC2_CONTROL_1, + .enable_mask = WM8400_DC1_ENA_MASK, + .n_voltages = WM8400_DC2_VSEL_MASK + 1, + .vsel_reg = WM8400_DCDC2_CONTROL_1, + .vsel_mask = WM8400_DC2_VSEL_MASK, + .min_uV = 850000, + .uV_step = 25000, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int wm8400_regulator_probe(struct platform_device *pdev) +{ + struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]); + struct regulator_config config = { }; + struct regulator_dev *rdev; + + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = wm8400; + config.regmap = wm8400->regmap; + + rdev = regulator_register(®ulators[pdev->id], &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int wm8400_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver wm8400_regulator_driver = { + .driver = { + .name = "wm8400-regulator", + }, + .probe = wm8400_regulator_probe, + .remove = wm8400_regulator_remove, +}; + +/** + * wm8400_register_regulator - enable software control of a WM8400 regulator + * + * This function enables software control of a WM8400 regulator via + * the regulator API. It is intended to be called from the + * platform_init() callback of the WM8400 MFD driver. + * + * @param dev The WM8400 device to operate on. + * @param reg The regulator to control. + * @param initdata Regulator initdata for the regulator. + */ +int wm8400_register_regulator(struct device *dev, int reg, + struct regulator_init_data *initdata) +{ + struct wm8400 *wm8400 = dev_get_drvdata(dev); + + if (wm8400->regulators[reg].name) + return -EBUSY; + + initdata->driver_data = wm8400; + + wm8400->regulators[reg].name = "wm8400-regulator"; + wm8400->regulators[reg].id = reg; + wm8400->regulators[reg].dev.parent = dev; + wm8400->regulators[reg].dev.platform_data = initdata; + + return platform_device_register(&wm8400->regulators[reg]); +} +EXPORT_SYMBOL_GPL(wm8400_register_regulator); + +static int __init wm8400_regulator_init(void) +{ + return platform_driver_register(&wm8400_regulator_driver); +} +subsys_initcall(wm8400_regulator_init); + +static void __exit wm8400_regulator_exit(void) +{ + platform_driver_unregister(&wm8400_regulator_driver); +} +module_exit(wm8400_regulator_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM8400 regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8400-regulator"); diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c new file mode 100644 index 0000000..a612c35 --- /dev/null +++ b/drivers/regulator/wm8994-regulator.c @@ -0,0 +1,210 @@ +/* + * wm8994-regulator.c -- Regulator driver for the WM8994 + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct wm8994_ldo { + struct regulator_dev *regulator; + struct wm8994 *wm8994; + struct regulator_consumer_supply supply; + struct regulator_init_data init_data; +}; + +#define WM8994_LDO1_MAX_SELECTOR 0x7 +#define WM8994_LDO2_MAX_SELECTOR 0x3 + +static struct regulator_ops wm8994_ldo1_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + + if (selector > WM8994_LDO2_MAX_SELECTOR) + return -EINVAL; + + switch (ldo->wm8994->type) { + case WM8994: + return (selector * 100000) + 900000; + case WM8958: + return (selector * 100000) + 1000000; + case WM1811: + switch (selector) { + case 0: + return -EINVAL; + default: + return (selector * 100000) + 950000; + } + break; + default: + return -EINVAL; + } +} + +static struct regulator_ops wm8994_ldo2_ops = { + .list_voltage = wm8994_ldo2_list_voltage, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_desc wm8994_ldo_desc[] = { + { + .name = "LDO1", + .id = 1, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8994_LDO1_MAX_SELECTOR + 1, + .vsel_reg = WM8994_LDO_1, + .vsel_mask = WM8994_LDO1_VSEL_MASK, + .ops = &wm8994_ldo1_ops, + .min_uV = 2400000, + .uV_step = 100000, + .enable_time = 3000, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = 2, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8994_LDO2_MAX_SELECTOR + 1, + .vsel_reg = WM8994_LDO_2, + .vsel_mask = WM8994_LDO2_VSEL_MASK, + .ops = &wm8994_ldo2_ops, + .enable_time = 3000, + .owner = THIS_MODULE, + }, +}; + +static const struct regulator_consumer_supply wm8994_ldo_consumer[] = { + { .supply = "AVDD1" }, + { .supply = "DCVDD" }, +}; + +static const struct regulator_init_data wm8994_ldo_default[] = { + { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + }, + { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + }, +}; + +static int wm8994_ldo_probe(struct platform_device *pdev) +{ + struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); + struct wm8994_pdata *pdata = wm8994->dev->platform_data; + int id = pdev->id % ARRAY_SIZE(pdata->ldo); + struct regulator_config config = { }; + struct wm8994_ldo *ldo; + int ret; + + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); + + ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_ldo), GFP_KERNEL); + if (ldo == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo->wm8994 = wm8994; + ldo->supply = wm8994_ldo_consumer[id]; + ldo->supply.dev_name = dev_name(wm8994->dev); + + config.dev = wm8994->dev; + config.driver_data = ldo; + config.regmap = wm8994->regmap; + config.init_data = &ldo->init_data; + if (pdata) + config.ena_gpio = pdata->ldo[id].enable; + else if (wm8994->dev->of_node) + config.ena_gpio = wm8994->pdata.ldo[id].enable; + + /* Use default constraints if none set up */ + if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) { + dev_dbg(wm8994->dev, "Using default init data, supply %s %s\n", + ldo->supply.dev_name, ldo->supply.supply); + + ldo->init_data = wm8994_ldo_default[id]; + ldo->init_data.consumer_supplies = &ldo->supply; + if (!config.ena_gpio) + ldo->init_data.constraints.valid_ops_mask = 0; + } else { + ldo->init_data = *pdata->ldo[id].init_data; + } + + ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config); + if (IS_ERR(ldo->regulator)) { + ret = PTR_ERR(ldo->regulator); + dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", + id + 1, ret); + goto err; + } + + platform_set_drvdata(pdev, ldo); + + return 0; + +err: + return ret; +} + +static int wm8994_ldo_remove(struct platform_device *pdev) +{ + struct wm8994_ldo *ldo = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + regulator_unregister(ldo->regulator); + + return 0; +} + +static struct platform_driver wm8994_ldo_driver = { + .probe = wm8994_ldo_probe, + .remove = wm8994_ldo_remove, + .driver = { + .name = "wm8994-ldo", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(wm8994_ldo_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM8994 LDO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8994-ldo"); -- cgit v1.2.3