summaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/regulator/88pg86x.c114
-rw-r--r--drivers/regulator/88pm800-regulator.c305
-rw-r--r--drivers/regulator/88pm8607.c442
-rw-r--r--drivers/regulator/Kconfig1013
-rw-r--r--drivers/regulator/Makefile132
-rw-r--r--drivers/regulator/aat2870-regulator.c213
-rw-r--r--drivers/regulator/ab3100.c745
-rw-r--r--drivers/regulator/ab8500-ext.c897
-rw-r--r--drivers/regulator/ab8500.c1682
-rw-r--r--drivers/regulator/act8865-regulator.c625
-rw-r--r--drivers/regulator/act8945a-regulator.c165
-rw-r--r--drivers/regulator/ad5398.c279
-rw-r--r--drivers/regulator/anatop-regulator.c357
-rw-r--r--drivers/regulator/arizona-ldo1.c371
-rw-r--r--drivers/regulator/arizona-micsupp.c337
-rw-r--r--drivers/regulator/as3711-regulator.c291
-rw-r--r--drivers/regulator/as3722-regulator.c973
-rw-r--r--drivers/regulator/axp20x-regulator.c860
-rw-r--r--drivers/regulator/bcm590xx-regulator.c466
-rw-r--r--drivers/regulator/bd71837-regulator.c645
-rw-r--r--drivers/regulator/bd9571mwv-regulator.c365
-rw-r--r--drivers/regulator/core.c4958
-rw-r--r--drivers/regulator/cpcap-regulator.c582
-rw-r--r--drivers/regulator/da903x.c498
-rw-r--r--drivers/regulator/da9052-regulator.c488
-rw-r--r--drivers/regulator/da9055-regulator.c686
-rw-r--r--drivers/regulator/da9062-regulator.c1120
-rw-r--r--drivers/regulator/da9063-regulator.c917
-rw-r--r--drivers/regulator/da9210-regulator.c293
-rw-r--r--drivers/regulator/da9210-regulator.h288
-rw-r--r--drivers/regulator/da9211-regulator.c535
-rw-r--r--drivers/regulator/da9211-regulator.h275
-rw-r--r--drivers/regulator/db8500-prcmu.c557
-rw-r--r--drivers/regulator/dbx500-prcmu.c206
-rw-r--r--drivers/regulator/dbx500-prcmu.h61
-rw-r--r--drivers/regulator/devres.c486
-rw-r--r--drivers/regulator/dummy.c93
-rw-r--r--drivers/regulator/dummy.h27
-rw-r--r--drivers/regulator/fan53555.c496
-rw-r--r--drivers/regulator/fixed-helper.c62
-rw-r--r--drivers/regulator/fixed.c225
-rw-r--r--drivers/regulator/gpio-regulator.c430
-rw-r--r--drivers/regulator/helpers.c530
-rw-r--r--drivers/regulator/hi6421-regulator.c641
-rw-r--r--drivers/regulator/hi6421v530-regulator.c214
-rw-r--r--drivers/regulator/hi655x-regulator.c234
-rw-r--r--drivers/regulator/internal.h116
-rw-r--r--drivers/regulator/isl6271a-regulator.c179
-rw-r--r--drivers/regulator/isl9305.c209
-rw-r--r--drivers/regulator/lm363x-regulator.c291
-rw-r--r--drivers/regulator/lp3971.c464
-rw-r--r--drivers/regulator/lp3972.c572
-rw-r--r--drivers/regulator/lp872x.c1019
-rw-r--r--drivers/regulator/lp873x-regulator.c240
-rw-r--r--drivers/regulator/lp8755.c548
-rw-r--r--drivers/regulator/lp87565-regulator.c241
-rw-r--r--drivers/regulator/lp8788-buck.c554
-rw-r--r--drivers/regulator/lp8788-ldo.c637
-rw-r--r--drivers/regulator/ltc3589.c579
-rw-r--r--drivers/regulator/ltc3676.c445
-rw-r--r--drivers/regulator/max14577-regulator.c327
-rw-r--r--drivers/regulator/max1586.c329
-rw-r--r--drivers/regulator/max77620-regulator.c907
-rw-r--r--drivers/regulator/max77686-regulator.c551
-rw-r--r--drivers/regulator/max77693-regulator.c304
-rw-r--r--drivers/regulator/max77802-regulator.c600
-rw-r--r--drivers/regulator/max8649.c275
-rw-r--r--drivers/regulator/max8660.c542
-rw-r--r--drivers/regulator/max8907-regulator.c399
-rw-r--r--drivers/regulator/max8925-regulator.c346
-rw-r--r--drivers/regulator/max8952.c353
-rw-r--r--drivers/regulator/max8973-regulator.c852
-rw-r--r--drivers/regulator/max8997-regulator.c1224
-rw-r--r--drivers/regulator/max8998.c903
-rw-r--r--drivers/regulator/mc13783-regulator.c477
-rw-r--r--drivers/regulator/mc13892-regulator.c651
-rw-r--r--drivers/regulator/mc13xxx-regulator-core.c220
-rw-r--r--drivers/regulator/mc13xxx.h112
-rw-r--r--drivers/regulator/mt6311-regulator.c180
-rw-r--r--drivers/regulator/mt6311-regulator.h65
-rw-r--r--drivers/regulator/mt6323-regulator.c425
-rw-r--r--drivers/regulator/mt6380-regulator.c352
-rw-r--r--drivers/regulator/mt6397-regulator.c416
-rw-r--r--drivers/regulator/of_regulator.c586
-rw-r--r--drivers/regulator/palmas-regulator.c1707
-rw-r--r--drivers/regulator/pbias-regulator.c262
-rw-r--r--drivers/regulator/pcap-regulator.c281
-rw-r--r--drivers/regulator/pcf50633-regulator.c128
-rw-r--r--drivers/regulator/pfuze100-regulator.c800
-rw-r--r--drivers/regulator/pv88060-regulator.c434
-rw-r--r--drivers/regulator/pv88060-regulator.h69
-rw-r--r--drivers/regulator/pv88080-regulator.c621
-rw-r--r--drivers/regulator/pv88080-regulator.h118
-rw-r--r--drivers/regulator/pv88090-regulator.c460
-rw-r--r--drivers/regulator/pv88090-regulator.h98
-rw-r--r--drivers/regulator/pwm-regulator.c418
-rw-r--r--drivers/regulator/qcom-rpmh-regulator.c769
-rw-r--r--drivers/regulator/qcom_rpm-regulator.c1020
-rw-r--r--drivers/regulator/qcom_smd-regulator.c791
-rw-r--r--drivers/regulator/qcom_spmi-regulator.c1883
-rw-r--r--drivers/regulator/rc5t583-regulator.c203
-rw-r--r--drivers/regulator/rk808-regulator.c804
-rw-r--r--drivers/regulator/rn5t618-regulator.c160
-rw-r--r--drivers/regulator/rt5033-regulator.c129
-rw-r--r--drivers/regulator/s2mpa01.c415
-rw-r--r--drivers/regulator/s2mps11.c1233
-rw-r--r--drivers/regulator/s5m8767.c1015
-rw-r--r--drivers/regulator/sc2731-regulator.c256
-rw-r--r--drivers/regulator/sky81452-regulator.c102
-rw-r--r--drivers/regulator/stm32-vrefbuf.c202
-rw-r--r--drivers/regulator/stw481x-vmmc.c104
-rw-r--r--drivers/regulator/sy8106a-regulator.c167
-rw-r--r--drivers/regulator/ti-abb-regulator.c902
-rw-r--r--drivers/regulator/tps51632-regulator.c388
-rw-r--r--drivers/regulator/tps6105x-regulator.c114
-rw-r--r--drivers/regulator/tps62360-regulator.c539
-rw-r--r--drivers/regulator/tps65023-regulator.c365
-rw-r--r--drivers/regulator/tps6507x-regulator.c541
-rw-r--r--drivers/regulator/tps65086-regulator.c254
-rw-r--r--drivers/regulator/tps65090-regulator.c531
-rw-r--r--drivers/regulator/tps65132-regulator.c284
-rw-r--r--drivers/regulator/tps65217-regulator.c286
-rw-r--r--drivers/regulator/tps65218-regulator.c371
-rw-r--r--drivers/regulator/tps6524x-regulator.c640
-rw-r--r--drivers/regulator/tps6586x-regulator.c545
-rw-r--r--drivers/regulator/tps65910-regulator.c1284
-rw-r--r--drivers/regulator/tps65912-regulator.c169
-rw-r--r--drivers/regulator/tps80031-regulator.c768
-rw-r--r--drivers/regulator/twl-regulator.c674
-rw-r--r--drivers/regulator/twl6030-regulator.c791
-rw-r--r--drivers/regulator/uniphier-regulator.c214
-rw-r--r--drivers/regulator/userspace-consumer.c186
-rw-r--r--drivers/regulator/vctrl-regulator.c546
-rw-r--r--drivers/regulator/vexpress-regulator.c121
-rw-r--r--drivers/regulator/virtual.c349
-rw-r--r--drivers/regulator/wm831x-dcdc.c913
-rw-r--r--drivers/regulator/wm831x-isink.c252
-rw-r--r--drivers/regulator/wm831x-ldo.c680
-rw-r--r--drivers/regulator/wm8350-regulator.c1397
-rw-r--r--drivers/regulator/wm8400-regulator.c282
-rw-r--r--drivers/regulator/wm8994-regulator.c199
141 files changed, 72903 insertions, 0 deletions
diff --git a/drivers/regulator/88pg86x.c b/drivers/regulator/88pg86x.c
new file mode 100644
index 000000000..d5ef55c81
--- /dev/null
+++ b/drivers/regulator/88pg86x.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+
+static const struct regulator_ops pg86x_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+};
+
+static const struct regulator_linear_range pg86x_buck1_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 0, 0, 10, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 11, 34, 25000),
+ REGULATOR_LINEAR_RANGE(1600000, 35, 47, 50000),
+};
+
+static const struct regulator_linear_range pg86x_buck2_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 0, 0, 15, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 16, 39, 25000),
+ REGULATOR_LINEAR_RANGE(1600000, 40, 52, 50000),
+};
+
+static const struct regulator_desc pg86x_regulators[] = {
+ {
+ .id = 0,
+ .type = REGULATOR_VOLTAGE,
+ .name = "buck1",
+ .of_match = of_match_ptr("buck1"),
+ .n_voltages = 11 + 24 + 13,
+ .linear_ranges = pg86x_buck1_ranges,
+ .n_linear_ranges = 3,
+ .vsel_reg = 0x24,
+ .vsel_mask = 0xff,
+ .ops = &pg86x_ops,
+ .owner = THIS_MODULE
+ },
+ {
+ .id = 1,
+ .type = REGULATOR_VOLTAGE,
+ .name = "buck2",
+ .of_match = of_match_ptr("buck2"),
+ .n_voltages = 16 + 24 + 13,
+ .linear_ranges = pg86x_buck2_ranges,
+ .n_linear_ranges = 3,
+ .vsel_reg = 0x13,
+ .vsel_mask = 0xff,
+ .ops = &pg86x_ops,
+ .owner = THIS_MODULE
+ },
+};
+
+static const struct regmap_config pg86x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int pg86x_i2c_probe(struct i2c_client *i2c)
+{
+ int id, ret;
+ struct regulator_config config = {.dev = &i2c->dev};
+ struct regmap *regmap = devm_regmap_init_i2c(i2c, &pg86x_regmap);
+
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ for (id = 0; id < ARRAY_SIZE(pg86x_regulators); id++) {
+ struct regulator_dev *rdev;
+ rdev = devm_regulator_register(&i2c->dev,
+ &pg86x_regulators[id],
+ &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&i2c->dev, "failed to register %s: %d\n",
+ pg86x_regulators[id].name, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id pg86x_dt_ids [] = {
+ { .compatible = "marvell,88pg867" },
+ { .compatible = "marvell,88pg868" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pg86x_dt_ids);
+
+static const struct i2c_device_id pg86x_i2c_id[] = {
+ { "88pg867", },
+ { "88pg868", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pg86x_i2c_id);
+
+static struct i2c_driver pg86x_regulator_driver = {
+ .driver = {
+ .name = "88pg86x",
+ .of_match_table = of_match_ptr(pg86x_dt_ids),
+ },
+ .probe_new = pg86x_i2c_probe,
+ .id_table = pg86x_i2c_id,
+};
+
+module_i2c_driver(pg86x_regulator_driver);
+
+MODULE_DESCRIPTION("Marvell 88PG86X voltage regulator");
+MODULE_AUTHOR("Alexander Monakov <amonakov@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/88pm800-regulator.c b/drivers/regulator/88pm800-regulator.c
new file mode 100644
index 000000000..89bbd6e8b
--- /dev/null
+++ b/drivers/regulator/88pm800-regulator.c
@@ -0,0 +1,305 @@
+/*
+ * Regulators driver for Marvell 88PM800
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Yi Zhang <yizhang@marvell.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+
+/* LDO1 with DVC[0..3] */
+#define PM800_LDO1_VOUT (0x08) /* VOUT1 */
+#define PM800_LDO1_VOUT_2 (0x09)
+#define PM800_LDO1_VOUT_3 (0x0A)
+#define PM800_LDO2_VOUT (0x0B)
+#define PM800_LDO3_VOUT (0x0C)
+#define PM800_LDO4_VOUT (0x0D)
+#define PM800_LDO5_VOUT (0x0E)
+#define PM800_LDO6_VOUT (0x0F)
+#define PM800_LDO7_VOUT (0x10)
+#define PM800_LDO8_VOUT (0x11)
+#define PM800_LDO9_VOUT (0x12)
+#define PM800_LDO10_VOUT (0x13)
+#define PM800_LDO11_VOUT (0x14)
+#define PM800_LDO12_VOUT (0x15)
+#define PM800_LDO13_VOUT (0x16)
+#define PM800_LDO14_VOUT (0x17)
+#define PM800_LDO15_VOUT (0x18)
+#define PM800_LDO16_VOUT (0x19)
+#define PM800_LDO17_VOUT (0x1A)
+#define PM800_LDO18_VOUT (0x1B)
+#define PM800_LDO19_VOUT (0x1C)
+
+/* BUCK1 with DVC[0..3] */
+#define PM800_BUCK1 (0x3C)
+#define PM800_BUCK1_1 (0x3D)
+#define PM800_BUCK1_2 (0x3E)
+#define PM800_BUCK1_3 (0x3F)
+#define PM800_BUCK2 (0x40)
+#define PM800_BUCK3 (0x41)
+#define PM800_BUCK4 (0x42)
+#define PM800_BUCK4_1 (0x43)
+#define PM800_BUCK4_2 (0x44)
+#define PM800_BUCK4_3 (0x45)
+#define PM800_BUCK5 (0x46)
+
+#define PM800_BUCK_ENA (0x50)
+#define PM800_LDO_ENA1_1 (0x51)
+#define PM800_LDO_ENA1_2 (0x52)
+#define PM800_LDO_ENA1_3 (0x53)
+
+#define PM800_LDO_ENA2_1 (0x56)
+#define PM800_LDO_ENA2_2 (0x57)
+#define PM800_LDO_ENA2_3 (0x58)
+
+#define PM800_BUCK1_MISC1 (0x78)
+#define PM800_BUCK3_MISC1 (0x7E)
+#define PM800_BUCK4_MISC1 (0x81)
+#define PM800_BUCK5_MISC1 (0x84)
+
+struct pm800_regulator_info {
+ struct regulator_desc desc;
+ int max_ua;
+};
+
+struct pm800_regulators {
+ struct pm80x_chip *chip;
+ struct regmap *map;
+};
+
+/*
+ * vreg - the buck regs string.
+ * ereg - the string for the enable register.
+ * ebit - the bit number in the enable register.
+ * amax - the current
+ * Buck has 2 kinds of voltage steps. It is easy to find voltage by ranges,
+ * not the constant voltage table.
+ * n_volt - Number of available selectors
+ */
+#define PM800_BUCK(match, vreg, ereg, ebit, amax, volt_ranges, n_volt) \
+{ \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(#match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &pm800_volt_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PM800_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = n_volt, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = PM800_##vreg, \
+ .vsel_mask = 0x7f, \
+ .enable_reg = PM800_##ereg, \
+ .enable_mask = 1 << (ebit), \
+ }, \
+ .max_ua = (amax), \
+}
+
+/*
+ * vreg - the LDO regs string
+ * ereg - the string for the enable register.
+ * ebit - the bit number in the enable register.
+ * amax - the current
+ * volt_table - the LDO voltage table
+ * For all the LDOes, there are too many ranges. Using volt_table will be
+ * simpler and faster.
+ */
+#define PM800_LDO(match, vreg, ereg, ebit, amax, ldo_volt_table) \
+{ \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(#match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &pm800_volt_table_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PM800_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(ldo_volt_table), \
+ .vsel_reg = PM800_##vreg##_VOUT, \
+ .vsel_mask = 0xf, \
+ .enable_reg = PM800_##ereg, \
+ .enable_mask = 1 << (ebit), \
+ .volt_table = ldo_volt_table, \
+ }, \
+ .max_ua = (amax), \
+}
+
+/* Ranges are sorted in ascending order. */
+static const struct regulator_linear_range buck1_volt_range[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 0x4f, 12500),
+ REGULATOR_LINEAR_RANGE(1600000, 0x50, 0x54, 50000),
+};
+
+/* BUCK 2~5 have same ranges. */
+static const struct regulator_linear_range buck2_5_volt_range[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 0x4f, 12500),
+ REGULATOR_LINEAR_RANGE(1600000, 0x50, 0x72, 50000),
+};
+
+static const unsigned int ldo1_volt_table[] = {
+ 600000, 650000, 700000, 750000, 800000, 850000, 900000, 950000,
+ 1000000, 1050000, 1100000, 1150000, 1200000, 1300000, 1400000, 1500000,
+};
+
+static const unsigned int ldo2_volt_table[] = {
+ 1700000, 1800000, 1900000, 2000000, 2100000, 2500000, 2700000, 2800000,
+};
+
+/* LDO 3~17 have same voltage table. */
+static const unsigned int ldo3_17_volt_table[] = {
+ 1200000, 1250000, 1700000, 1800000, 1850000, 1900000, 2500000, 2600000,
+ 2700000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000,
+};
+
+/* LDO 18~19 have same voltage table. */
+static const unsigned int ldo18_19_volt_table[] = {
+ 1700000, 1800000, 1900000, 2500000, 2800000, 2900000, 3100000, 3300000,
+};
+
+static int pm800_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pm800_regulator_info *info = rdev_get_drvdata(rdev);
+
+ return info->max_ua;
+}
+
+static const struct regulator_ops pm800_volt_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .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,
+ .get_current_limit = pm800_get_current_limit,
+};
+
+static const struct regulator_ops pm800_volt_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .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,
+ .get_current_limit = pm800_get_current_limit,
+};
+
+/* The array is indexed by id(PM800_ID_XXX) */
+static struct pm800_regulator_info pm800_regulator_info[] = {
+ PM800_BUCK(buck1, BUCK1, BUCK_ENA, 0, 3000000, buck1_volt_range, 0x55),
+ PM800_BUCK(buck2, BUCK2, BUCK_ENA, 1, 1200000, buck2_5_volt_range, 0x73),
+ PM800_BUCK(buck3, BUCK3, BUCK_ENA, 2, 1200000, buck2_5_volt_range, 0x73),
+ PM800_BUCK(buck4, BUCK4, BUCK_ENA, 3, 1200000, buck2_5_volt_range, 0x73),
+ PM800_BUCK(buck5, BUCK5, BUCK_ENA, 4, 1200000, buck2_5_volt_range, 0x73),
+
+ PM800_LDO(ldo1, LDO1, LDO_ENA1_1, 0, 200000, ldo1_volt_table),
+ PM800_LDO(ldo2, LDO2, LDO_ENA1_1, 1, 10000, ldo2_volt_table),
+ PM800_LDO(ldo3, LDO3, LDO_ENA1_1, 2, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo4, LDO4, LDO_ENA1_1, 3, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo5, LDO5, LDO_ENA1_1, 4, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo6, LDO6, LDO_ENA1_1, 5, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo7, LDO7, LDO_ENA1_1, 6, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo8, LDO8, LDO_ENA1_1, 7, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo9, LDO9, LDO_ENA1_2, 0, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo10, LDO10, LDO_ENA1_2, 1, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo11, LDO11, LDO_ENA1_2, 2, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo12, LDO12, LDO_ENA1_2, 3, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo13, LDO13, LDO_ENA1_2, 4, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo14, LDO14, LDO_ENA1_2, 5, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo15, LDO15, LDO_ENA1_2, 6, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo16, LDO16, LDO_ENA1_2, 7, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo17, LDO17, LDO_ENA1_3, 0, 300000, ldo3_17_volt_table),
+ PM800_LDO(ldo18, LDO18, LDO_ENA1_3, 1, 200000, ldo18_19_volt_table),
+ PM800_LDO(ldo19, LDO19, LDO_ENA1_3, 2, 200000, ldo18_19_volt_table),
+};
+
+static int pm800_regulator_probe(struct platform_device *pdev)
+{
+ struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm80x_platform_data *pdata = dev_get_platdata(pdev->dev.parent);
+ struct pm800_regulators *pm800_data;
+ struct regulator_config config = { };
+ struct regulator_init_data *init_data;
+ int i, ret;
+
+ if (pdata && pdata->num_regulators) {
+ unsigned int count = 0;
+
+ /* Check whether num_regulator is valid. */
+ for (i = 0; i < ARRAY_SIZE(pdata->regulators); i++) {
+ if (pdata->regulators[i])
+ count++;
+ }
+ if (count != pdata->num_regulators)
+ return -EINVAL;
+ }
+
+ pm800_data = devm_kzalloc(&pdev->dev, sizeof(*pm800_data),
+ GFP_KERNEL);
+ if (!pm800_data)
+ return -ENOMEM;
+
+ pm800_data->map = chip->subchip->regmap_power;
+ pm800_data->chip = chip;
+
+ platform_set_drvdata(pdev, pm800_data);
+
+ config.dev = chip->dev;
+ config.regmap = pm800_data->map;
+ for (i = 0; i < PM800_ID_RG_MAX; i++) {
+ struct regulator_dev *regulator;
+
+ if (pdata && pdata->num_regulators) {
+ init_data = pdata->regulators[i];
+ if (!init_data)
+ continue;
+
+ config.init_data = init_data;
+ }
+
+ config.driver_data = &pm800_regulator_info[i];
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &pm800_regulator_info[i].desc, &config);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+ dev_err(&pdev->dev, "Failed to register %s\n",
+ pm800_regulator_info[i].desc.name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver pm800_regulator_driver = {
+ .driver = {
+ .name = "88pm80x-regulator",
+ },
+ .probe = pm800_regulator_probe,
+};
+
+module_platform_driver(pm800_regulator_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joseph(Yossi) Hanin <yhanin@marvell.com>");
+MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM800 PMIC");
+MODULE_ALIAS("platform:88pm800-regulator");
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
new file mode 100644
index 000000000..fd86446e4
--- /dev/null
+++ b/drivers/regulator/88pm8607.c
@@ -0,0 +1,442 @@
+/*
+ * Regulators driver for Marvell 88PM8607
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/module.h>
+
+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 const 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 const 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 = pdev->dev.parent->of_node;
+ if (!nproot)
+ return -ENODEV;
+ nproot = of_get_child_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,
+ &info->desc);
+ 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 = dev_get_platdata(&pdev->dev);
+ 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 = devm_regulator_register(&pdev->dev, &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 const 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",
+ },
+ .probe = pm8607_regulator_probe,
+ .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 <haojian.zhuang@marvell.com>");
+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 000000000..329cdd33e
--- /dev/null
+++ b/drivers/regulator/Kconfig
@@ -0,0 +1,1013 @@
+menuconfig REGULATOR
+ 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_FIXED_VOLTAGE
+ tristate "Fixed voltage regulator support"
+ 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"
+ 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"
+ 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_88PG86X
+ tristate "Marvell 88PG86X voltage regulators"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports Marvell 88PG867 and 88PG868 voltage regulators.
+ They provide two I2C-controlled DC/DC step-down converters with
+ sleep mode and separate enable pins.
+
+config REGULATOR_88PM800
+ tristate "Marvell 88PM800 Power regulators"
+ depends on MFD_88PM800
+ help
+ This driver supports Marvell 88PM800 voltage regulator chips.
+ It delivers digitally programmable output,
+ the voltage is programmed via I2C interface.
+ It's suitable to support PXA988 chips to control VCC_MAIN and
+ various voltages.
+
+config REGULATOR_88PM8607
+ tristate "Marvell 88PM8607 Power regulators"
+ depends on MFD_88PM860X=y
+ help
+ This driver supports 88PM8607 voltage regulator chips.
+
+config REGULATOR_ACT8865
+ tristate "Active-semi act8865 voltage regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver controls a active-semi act8865 voltage output
+ regulator via I2C bus.
+
+config REGULATOR_ACT8945A
+ tristate "Active-semi ACT8945A voltage regulator"
+ depends on MFD_ACT8945A
+ help
+ This driver controls a active-semi ACT8945A voltage regulator
+ via I2C bus. The ACT8945A features three step-down DC/DC converters
+ and four low-dropout linear regulators, along with a ActivePath
+ battery charger.
+
+config REGULATOR_AD5398
+ tristate "Analog Devices AD5398/AD5821 regulators"
+ depends on I2C
+ help
+ This driver supports AD5398 and AD5821 current regulator chips.
+ If building into module, its name is ad5398.ko.
+
+config REGULATOR_ANATOP
+ tristate "Freescale i.MX on-chip ANATOP LDO regulators"
+ 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_AAT2870
+ tristate "AnalogicTech AAT2870 Regulators"
+ depends on MFD_AAT2870_CORE
+ help
+ If you have a AnalogicTech AAT2870 say Y to enable the
+ regulator driver.
+
+config REGULATOR_AB3100
+ tristate "ST-Ericsson AB3100 Regulator functions"
+ 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_ARIZONA_LDO1
+ tristate "Wolfson Arizona class devices LDO1"
+ depends on MFD_ARIZONA
+ depends on SND_SOC
+ help
+ Support for the LDO1 regulators found on Wolfson Arizona class
+ devices.
+
+config REGULATOR_ARIZONA_MICSUPP
+ tristate "Wolfson Arizona class devices MICSUPP"
+ depends on MFD_ARIZONA
+ depends on SND_SOC
+ help
+ Support for the MICSUPP regulators found on Wolfson Arizona class
+ devices.
+
+config REGULATOR_AS3711
+ tristate "AS3711 PMIC"
+ depends on MFD_AS3711
+ help
+ This driver provides support for the voltage regulators on the
+ AS3711 PMIC
+
+config REGULATOR_AS3722
+ tristate "AMS AS3722 PMIC Regulators"
+ depends on MFD_AS3722
+ help
+ This driver provides support for the voltage regulators on the
+ AS3722 PMIC. This will enable support for all the software
+ controllable DCDC/LDO regulators.
+
+config REGULATOR_AXP20X
+ tristate "X-POWERS AXP20X PMIC Regulators"
+ depends on MFD_AXP20X
+ help
+ This driver provides support for the voltage regulators on the
+ AXP20X PMIC.
+
+config REGULATOR_BCM590XX
+ tristate "Broadcom BCM590xx PMU Regulators"
+ depends on MFD_BCM590XX
+ help
+ This driver provides support for the voltage regulators on the
+ BCM590xx PMUs. This will enable support for the software
+ controllable LDO/Switching regulators.
+
+config REGULATOR_BD718XX
+ tristate "ROHM BD71837 Power Regulator"
+ depends on MFD_ROHM_BD718XX
+ help
+ This driver supports voltage regulators on ROHM BD71837 PMIC.
+ This will enable support for the software controllable buck
+ and LDO regulators.
+
+ This driver can also be built as a module. If so, the module
+ will be called bd71837-regulator.
+
+config REGULATOR_BD9571MWV
+ tristate "ROHM BD9571MWV Regulators"
+ depends on MFD_BD9571MWV
+ help
+ This driver provides support for the voltage regulators on the
+ ROHM BD9571MWV PMIC. This will enable support for the software
+ controllable regulator and voltage sampling units.
+
+ This driver can also be built as a module. If so, the module
+ will be called bd9571mwv-regulator.
+
+config REGULATOR_CPCAP
+ tristate "Motorola CPCAP regulator"
+ depends on MFD_CPCAP
+ help
+ Say y here for CPCAP regulator found on some Motorola phones
+ and tablets such as Droid 4.
+
+config REGULATOR_DA903X
+ tristate "Dialog Semiconductor DA9030/DA9034 regulators"
+ 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
+ tristate "Dialog Semiconductor DA9052/DA9053 regulators"
+ depends on PMIC_DA9052
+ help
+ This driver supports the voltage regulators of DA9052-BC and
+ DA9053-AA/Bx PMIC.
+
+config REGULATOR_DA9055
+ tristate "Dialog Semiconductor DA9055 regulators"
+ 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_DA9062
+ tristate "Dialog Semiconductor DA9061/62 regulators"
+ depends on MFD_DA9062
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ DA9061 and DA9062 PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9062-regulator.
+
+config REGULATOR_DA9063
+ tristate "Dialog Semiconductor DA9063 regulators"
+ depends on MFD_DA9063
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ DA9063 PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9063-regulator.
+
+config REGULATOR_DA9210
+ tristate "Dialog Semiconductor DA9210 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support for the Dialog Semiconductor DA9210.
+ The DA9210 is a multi-phase synchronous step down
+ converter 12A DC-DC Buck controlled through an I2C
+ interface.
+
+config REGULATOR_DA9211
+ tristate "Dialog Semiconductor DA9211/DA9212/DA9213/DA9223/DA9214/DA9224/DA9215/DA9225 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support for the Dialog Semiconductor DA9211/DA9212
+ /DA9213/DA9214/DA9215.
+ The DA9211/DA9212/DA9213/DA9214/DA9215 is a multi-phase synchronous
+ step down converter 12A or 16A DC-DC Buck controlled through an I2C
+ interface.
+
+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_FAN53555
+ tristate "Fairchild FAN53555 Regulator"
+ depends on I2C
+ select 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_GPIO
+ tristate "GPIO regulator support"
+ depends on GPIOLIB || COMPILE_TEST
+ 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_HI6421
+ tristate "HiSilicon Hi6421 PMIC voltage regulator support"
+ depends on MFD_HI6421_PMIC && OF
+ help
+ This driver provides support for the voltage regulators on the
+ HiSilicon Hi6421 PMU / Codec IC.
+ Hi6421 is a multi-function device which, on regulator part, provides
+ 21 general purpose LDOs, 3 dedicated LDOs, and 5 BUCKs. All
+ of them come with support to either ECO (idle) or sleep mode.
+
+config REGULATOR_HI6421V530
+ tristate "HiSilicon Hi6421v530 PMIC voltage regulator support"
+ depends on MFD_HI6421_PMIC && OF
+ help
+ This driver provides support for the voltage regulators on
+ HiSilicon Hi6421v530 PMU / Codec IC.
+ Hi6421v530 is a multi-function device which, on regulator part,
+ provides 5 general purpose LDOs, and all of them come with support
+ to either ECO (idle) or sleep mode.
+
+config REGULATOR_HI655X
+ tristate "Hisilicon HI655X PMIC regulators support"
+ depends on ARCH_HISI || COMPILE_TEST
+ depends on MFD_HI655X_PMIC && OF
+ help
+ This driver provides support for the voltage regulators of the
+ Hisilicon Hi655x PMIC device.
+
+config REGULATOR_ISL9305
+ tristate "Intersil ISL9305 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports ISL9305 voltage regulator chip.
+
+config REGULATOR_ISL6271A
+ tristate "Intersil ISL6271A Power regulator"
+ depends on I2C
+ help
+ This driver supports ISL6271A voltage regulator chip.
+
+config REGULATOR_LM363X
+ tristate "TI LM363X voltage regulators"
+ depends on MFD_TI_LMU
+ help
+ This driver supports LM3631 and LM3632 voltage regulators for
+ the LCD bias.
+ One boost output voltage is configurable and always on.
+ Other LDOs are used for the display module.
+
+config REGULATOR_LP3971
+ tristate "National Semiconductors LP3971 PMIC regulator driver"
+ depends on I2C
+ help
+ Say Y here to support the voltage regulators and convertors
+ on National Semiconductors LP3971 PMIC
+
+config REGULATOR_LP3972
+ tristate "National Semiconductors LP3972 PMIC regulator driver"
+ depends on I2C
+ help
+ Say Y here to support the voltage regulators and convertors
+ on National Semiconductors LP3972 PMIC
+
+config REGULATOR_LP872X
+ tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports LP8720/LP8725 PMIC
+
+config REGULATOR_LP873X
+ tristate "TI LP873X Power regulators"
+ depends on MFD_TI_LP873X && OF
+ help
+ This driver supports LP873X voltage regulator chips. LP873X
+ provides two step-down converters and two general-purpose LDO
+ voltage regulators. It supports software based voltage control
+ for different voltage domains
+
+config REGULATOR_LP8755
+ tristate "TI LP8755 High Performance PMU driver"
+ depends on I2C
+ select 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_LP87565
+ tristate "TI LP87565 Power regulators"
+ depends on MFD_TI_LP87565 && OF
+ help
+ This driver supports LP87565 voltage regulator chips. LP87565
+ provides four step-down converters. It supports software based
+ voltage control for different voltage domains
+
+config REGULATOR_LP8788
+ tristate "TI LP8788 Power Regulators"
+ depends on MFD_LP8788
+ help
+ This driver supports LP8788 voltage regulator chip.
+
+config REGULATOR_LTC3589
+ tristate "LTC3589 8-output voltage regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This enables support for the LTC3589, LTC3589-1, and LTC3589-2
+ 8-output regulators controlled via I2C.
+
+config REGULATOR_LTC3676
+ tristate "LTC3676 8-output voltage regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This enables support for the LTC3676
+ 8-output regulators controlled via I2C.
+
+config REGULATOR_MAX14577
+ tristate "Maxim 14577/77836 regulator"
+ depends on MFD_MAX14577
+ help
+ This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
+ The MAX14577 regulators include safeout LDO and charger current
+ regulator. The MAX77836 has two additional LDOs.
+
+config REGULATOR_MAX1586
+ tristate "Maxim 1586/1587 voltage regulator"
+ 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_MAX77620
+ tristate "Maxim 77620/MAX20024 voltage regulator"
+ depends on MFD_MAX77620
+ help
+ This driver controls Maxim MAX77620 voltage output regulator
+ via I2C bus. The provided regulator is suitable for Tegra
+ chip to control Step-Down DC-DC and LDOs. Say Y here to
+ enable the regulator driver.
+
+config REGULATOR_MAX8649
+ tristate "Maxim 8649 voltage regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver controls a Maxim 8649 voltage output regulator via
+ I2C bus.
+
+config REGULATOR_MAX8660
+ tristate "Maxim 8660/8661 voltage regulator"
+ 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 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
+ tristate "Maxim MAX8925 Power Management IC"
+ 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 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
+ tristate "Maxim MAX8973 voltage regulator "
+ depends on I2C
+ depends on THERMAL && THERMAL_OF
+ select 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
+ tristate "Maxim 8997/8966 regulator"
+ 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
+ tristate "Maxim 8998 voltage regulator"
+ 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
+ tristate "Maxim 77686 regulator"
+ 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_MAX77693
+ tristate "Maxim 77693/77843 regulator"
+ depends on (MFD_MAX77693 || MFD_MAX77843)
+ help
+ This driver controls a Maxim 77693/77843 regulators via I2C bus.
+ The regulators include two LDOs, 'SAFEOUT1', 'SAFEOUT2'
+ and one current regulator 'CHARGER'. This is suitable for
+ Exynos-4x12 (MAX77693) or Exynos5433 (MAX77843) SoC chips.
+
+config REGULATOR_MAX77802
+ tristate "Maxim 77802 regulator"
+ depends on MFD_MAX77686
+ help
+ This driver controls a Maxim 77802 regulator
+ via I2C bus. The provided regulator is suitable for
+ Exynos5420/Exynos5800 SoCs to control various voltages.
+ It includes support for control of voltage and ramp speed.
+
+config REGULATOR_MC13XXX_CORE
+ tristate
+
+config REGULATOR_MC13783
+ tristate "Freescale MC13783 regulator driver"
+ depends on MFD_MC13XXX
+ select REGULATOR_MC13XXX_CORE
+ help
+ Say y here to support the regulators found on the Freescale MC13783
+ PMIC.
+
+config REGULATOR_MC13892
+ tristate "Freescale MC13892 regulator driver"
+ depends on MFD_MC13XXX
+ select REGULATOR_MC13XXX_CORE
+ help
+ Say y here to support the regulators found on the Freescale MC13892
+ PMIC.
+
+config REGULATOR_MT6311
+ tristate "MediaTek MT6311 PMIC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to select this option to enable the power regulator of
+ MediaTek MT6311 PMIC.
+ This driver supports the control of different power rails of device
+ through regulator interface.
+
+config REGULATOR_MT6323
+ tristate "MediaTek MT6323 PMIC"
+ depends on MFD_MT6397
+ help
+ Say y here to select this option to enable the power regulator of
+ MediaTek MT6323 PMIC.
+ This driver supports the control of different power rails of device
+ through regulator interface.
+
+config REGULATOR_MT6380
+ tristate "MediaTek MT6380 PMIC"
+ depends on MTK_PMIC_WRAP
+ help
+ Say y here to select this option to enable the power regulator of
+ MediaTek MT6380 PMIC.
+ This driver supports the control of different power rails of device
+ through regulator interface.
+
+config REGULATOR_MT6397
+ tristate "MediaTek MT6397 PMIC"
+ depends on MFD_MT6397
+ help
+ Say y here to select this option to enable the power regulator of
+ MediaTek MT6397 PMIC.
+ This driver supports the control of different power rails of device
+ through regulator interface.
+
+config REGULATOR_PALMAS
+ tristate "TI Palmas PMIC Regulators"
+ 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_PBIAS
+ tristate "PBIAS OMAP regulator driver"
+ depends on (ARCH_OMAP || COMPILE_TEST) && MFD_SYSCON
+ help
+ Say y here to support pbias regulator for mmc1:SD card i/o
+ on OMAP SoCs.
+ This driver provides support for OMAP pbias modelled
+ regulators.
+
+config REGULATOR_PCAP
+ tristate "Motorola PCAP2 regulator driver"
+ depends on EZX_PCAP
+ help
+ This driver provides support for the voltage regulators of the
+ PCAP2 PMIC.
+
+config REGULATOR_PCF50633
+ tristate "NXP PCF50633 regulator driver"
+ depends on MFD_PCF50633
+ help
+ Say Y here to support the voltage regulators and convertors
+ on PCF50633
+
+config REGULATOR_PFUZE100
+ tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the regulators found on the Freescale
+ PFUZE100/200/3000/3001 PMIC.
+
+config REGULATOR_PV88060
+ tristate "Powerventure Semiconductor PV88060 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the voltage regulators and convertors
+ PV88060
+
+config REGULATOR_PV88080
+ tristate "Powerventure Semiconductor PV88080 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the buck convertors on PV88080
+
+config REGULATOR_PV88090
+ tristate "Powerventure Semiconductor PV88090 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the voltage regulators and convertors
+ on PV88090
+
+config REGULATOR_PWM
+ tristate "PWM voltage regulator"
+ depends on PWM
+ help
+ This driver supports PWM controlled voltage regulators. PWM
+ duty cycle can increase or decrease the voltage.
+
+config REGULATOR_QCOM_RPM
+ tristate "Qualcomm RPM regulator driver"
+ depends on MFD_QCOM_RPM
+ help
+ If you say yes to this option, support will be included for the
+ regulators exposed by the Resource Power Manager found in Qualcomm
+ 8660, 8960 and 8064 based devices.
+
+ Say M here if you want to include support for the regulators on the
+ Qualcomm RPM as a module. The module will be named
+ "qcom_rpm-regulator".
+
+config REGULATOR_QCOM_RPMH
+ tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
+ depends on QCOM_RPMH || COMPILE_TEST
+ help
+ This driver supports control of PMIC regulators via the RPMh hardware
+ block found on Qualcomm Technologies Inc. SoCs. RPMh regulator
+ control allows for voting on regulator state between multiple
+ processors within the SoC.
+
+config REGULATOR_QCOM_SMD_RPM
+ tristate "Qualcomm SMD based RPM regulator driver"
+ depends on QCOM_SMD_RPM
+ help
+ If you say yes to this option, support will be included for the
+ regulators exposed by the Resource Power Manager found in Qualcomm
+ 8974 based devices.
+
+ Say M here if you want to include support for the regulators on the
+ Qualcomm RPM as a module. The module will be named
+ "qcom_smd-regulator".
+
+config REGULATOR_QCOM_SPMI
+ tristate "Qualcomm SPMI regulator driver"
+ depends on SPMI || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ regulators found in Qualcomm SPMI PMICs.
+
+ Say M here if you want to include support for the regulators on the
+ Qualcomm SPMI PMICs as a module. The module will be named
+ "qcom_spmi-regulator".
+
+config REGULATOR_RC5T583
+ tristate "RICOH RC5T583 Power regulators"
+ 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_RK808
+ tristate "Rockchip RK805/RK808/RK818 Power regulators"
+ depends on MFD_RK808
+ help
+ Select this option to enable the power regulator of ROCKCHIP
+ PMIC RK805,RK808 and RK818.
+ 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_RN5T618
+ tristate "Ricoh RN5T567/618 voltage regulators"
+ depends on MFD_RN5T618
+ help
+ Say y here to support the regulators found on Ricoh RN5T567,
+ RN5T618 or RC5T619 PMIC.
+
+config REGULATOR_RT5033
+ tristate "Richtek RT5033 Regulators"
+ depends on MFD_RT5033
+ help
+ This adds support for voltage and current regulators in Richtek
+ RT5033 PMIC. The device supports multiple regulators like
+ current source, LDO and Buck.
+
+config REGULATOR_S2MPA01
+ tristate "Samsung S2MPA01 voltage regulator"
+ depends on MFD_SEC_CORE
+ help
+ This driver controls Samsung S2MPA01 voltage output regulator
+ via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs.
+
+config REGULATOR_S2MPS11
+ tristate "Samsung S2MPS11/13/14/15/S2MPU02 voltage regulator"
+ depends on MFD_SEC_CORE
+ help
+ This driver supports a Samsung S2MPS11/13/14/15/S2MPU02 voltage
+ output regulator via I2C bus. The chip is comprised of high efficient
+ Buck converters including Dual-Phase Buck converter, Buck-Boost
+ converter, various LDOs.
+
+config REGULATOR_S5M8767
+ tristate "Samsung S5M8767A voltage regulator"
+ 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_SC2731
+ tristate "Spreadtrum SC2731 power regulator driver"
+ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ help
+ This driver provides support for the voltage regulators on the
+ SC2731 PMIC.
+
+config REGULATOR_SKY81452
+ tristate "Skyworks Solutions SKY81452 voltage regulator"
+ depends on MFD_SKY81452
+ help
+ This driver supports Skyworks SKY81452 voltage output regulator
+ via I2C bus. SKY81452 has one voltage linear regulator can be
+ programmed from 4.5V to 20V.
+
+ This driver can also be built as a module. If so, the module
+ will be called sky81452-regulator.
+
+config REGULATOR_STM32_VREFBUF
+ tristate "STMicroelectronics STM32 VREFBUF"
+ depends on ARCH_STM32 || COMPILE_TEST
+ help
+ This driver supports STMicroelectronics STM32 VREFBUF (voltage
+ reference buffer) which can be used as voltage reference for
+ internal ADCs, DACs and also for external components through
+ dedicated Vref+ pin.
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-vrefbuf.
+
+config REGULATOR_TI_ABB
+ tristate "TI Adaptive Body Bias on-chip LDO"
+ depends on ARCH_OMAP
+ help
+ Select this option to support Texas Instruments' on-chip Adaptive Body
+ Bias (ABB) LDO regulators. It is recommended that this option be
+ enabled on required TI SoC. Certain Operating Performance Points
+ on TI SoCs may be unstable without enabling this as it provides
+ device specific optimized bias to allow/optimize functionality.
+
+config REGULATOR_STW481X_VMMC
+ bool "ST Microelectronics STW481X VMMC regulator"
+ depends on MFD_STW481X || COMPILE_TEST
+ default y if MFD_STW481X
+ help
+ This driver supports the internal VMMC regulator in the STw481x
+ PMIC chips.
+
+config REGULATOR_SY8106A
+ tristate "Silergy SY8106A regulator"
+ depends on I2C && (OF || COMPILE_TEST)
+ select REGMAP_I2C
+ help
+ This driver supports SY8106A single output regulator.
+
+config REGULATOR_TPS51632
+ tristate "TI TPS51632 Power Regulator"
+ depends on I2C
+ select 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
+ tristate "TI TPS6105X Power regulators"
+ 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
+ tristate "TI TPS6236x Power Regulator"
+ depends on I2C
+ select 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
+ tristate "TI TPS65023 Power regulators"
+ depends on I2C
+ select 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
+ tristate "TI TPS6507X Power regulators"
+ 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_TPS65086
+ tristate "TI TPS65086 Power regulators"
+ depends on MFD_TPS65086
+ help
+ This driver provides support for the voltage regulators on
+ TI TPS65086 PMICs.
+
+config REGULATOR_TPS65090
+ tristate "TI TPS65090 Power regulator"
+ depends on MFD_TPS65090
+ help
+ This driver provides support for the voltage regulators on the
+ TI TPS65090 PMIC.
+
+config REGULATOR_TPS65132
+ tristate "TI TPS65132 Dual Output Power regulators"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ This driver supports TPS65132 single inductor - dual output
+ power supply specifcally designed for display panels.
+
+config REGULATOR_TPS65217
+ tristate "TI TPS65217 Power regulators"
+ 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_TPS65218
+ tristate "TI TPS65218 Power regulators"
+ depends on MFD_TPS65218 && OF
+ help
+ This driver supports TPS65218 voltage regulator chips. TPS65218
+ provides six step-down converters and one general-purpose LDO
+ voltage regulators. It supports software based voltage control
+ for different voltage domains
+
+config REGULATOR_TPS6524X
+ tristate "TI TPS6524X Power regulators"
+ 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
+ tristate "TI TPS6586X Power regulators"
+ depends on MFD_TPS6586X
+ help
+ This driver supports TPS6586X voltage regulator chips.
+
+config REGULATOR_TPS65910
+ tristate "TI TPS65910/TPS65911 Power Regulators"
+ depends on MFD_TPS65910
+ help
+ This driver supports TPS65910/TPS65911 voltage regulator chips.
+
+config REGULATOR_TPS65912
+ tristate "TI TPS65912 Power regulator"
+ depends on MFD_TPS65912
+ help
+ This driver supports TPS65912 voltage regulator chip.
+
+config REGULATOR_TPS80031
+ tristate "TI TPS80031/TPS80032 power regulator driver"
+ 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
+ tristate "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_UNIPHIER
+ tristate "UniPhier regulator driver"
+ depends on ARCH_UNIPHIER || COMPILE_TEST
+ depends on OF && MFD_SYSCON
+ default ARCH_UNIPHIER
+ help
+ Support for regulators implemented on Socionext UniPhier SoCs.
+
+config REGULATOR_VCTRL
+ tristate "Voltage controlled regulators"
+ depends on OF
+ help
+ This driver provides support for voltage regulators whose output
+ voltage is controlled by the voltage of another regulator.
+
+config REGULATOR_VEXPRESS
+ tristate "Versatile Express regulators"
+ depends on VEXPRESS_CONFIG
+ help
+ This driver provides support for voltage regulators available
+ on the ARM Ltd's Versatile Express platform.
+
+config REGULATOR_WM831X
+ tristate "Wolfson Microelectronics WM831x PMIC regulators"
+ depends on MFD_WM831X
+ help
+ Support the voltage and current regulators of the WM831x series
+ of PMIC devices.
+
+config REGULATOR_WM8350
+ tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC"
+ depends on MFD_WM8350
+ help
+ This driver provides support for the voltage and current regulators
+ of the WM8350 AudioPlus PMIC.
+
+config REGULATOR_WM8400
+ tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC"
+ depends on MFD_WM8400
+ help
+ This driver provides support for the voltage regulators of the
+ WM8400 AudioPlus PMIC.
+
+config REGULATOR_WM8994
+ tristate "Wolfson Microelectronics WM8994 CODEC"
+ depends on MFD_WM8994
+ help
+ This driver provides support for the voltage regulators on the
+ WM8994 CODEC.
+
+endif
+
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
new file mode 100644
index 000000000..bba9c4851
--- /dev/null
+++ b/drivers/regulator/Makefile
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for regulator drivers.
+#
+
+
+obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o helpers.o devres.o
+obj-$(CONFIG_OF) += of_regulator.o
+obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
+obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
+obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+
+obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o
+obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o
+obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
+obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
+obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
+obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
+obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o
+obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o
+obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o
+obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
+obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
+obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o
+obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o
+obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
+obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
+obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
+obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
+obj-$(CONFIG_REGULATOR_BD718XX) += bd71837-regulator.o
+obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
+obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
+obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
+obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
+obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
+obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
+obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
+obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
+obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
+obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
+obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
+obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
+obj-$(CONFIG_REGULATOR_HI6421V530) += hi6421v530-regulator.o
+obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o
+obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
+obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
+obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o
+obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
+obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
+obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
+obj-$(CONFIG_REGULATOR_LP873X) += lp873x-regulator.o
+obj-$(CONFIG_REGULATOR_LP87565) += lp87565-regulator.o
+obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o
+obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
+obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
+obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
+obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
+obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
+obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
+obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
+obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
+obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
+obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8997) += max8997-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
+obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
+obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
+obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
+obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
+obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
+obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
+obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
+obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
+obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
+obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
+obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
+obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
+obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o
+obj-$(CONFIG_REGULATOR_PV88090) += pv88090-regulator.o
+obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
+obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
+obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
+obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
+obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
+obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
+obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o
+obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
+obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
+obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
+obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
+obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
+obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
+obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
+obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
+obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o
+obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65217) += tps65217-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
+obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
+obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
+obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o
+obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
+obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
+obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
+obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
+obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
+obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
+obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
+obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
+
+ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
new file mode 100644
index 000000000..afc5b5900
--- /dev/null
+++ b/drivers/regulator/aat2870-regulator.c
@@ -0,0 +1,213 @@
+/*
+ * linux/drivers/regulator/aat2870-regulator.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/aat2870.h>
+
+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 const struct regulator_ops aat2870_ldo_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_ascend,
+ .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 = dev_get_platdata(&pdev->dev);
+
+ rdev = devm_regulator_register(&pdev->dev, &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 struct platform_driver aat2870_regulator_driver = {
+ .driver = {
+ .name = "aat2870-regulator",
+ },
+ .probe = aat2870_regulator_probe,
+};
+
+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 <jinyoungp@nvidia.com>");
+MODULE_ALIAS("platform:aat2870-regulator");
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
new file mode 100644
index 000000000..de2644490
--- /dev/null
+++ b/drivers/regulator/ab3100.c
@@ -0,0 +1,745 @@
+/*
+ * 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 <mattias.wallin@stericsson.com>
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+
+/* 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,
+ &regval);
+ if (err) {
+ dev_warn(&reg->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(&reg->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(&reg->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,
+ &regval);
+ if (err) {
+ dev_err(&reg->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,
+ &regval);
+ if (err) {
+ dev_err(&reg->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, &regval);
+ if (err) {
+ dev_warn(&reg->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(&reg->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, &regval);
+ if (err) {
+ dev_warn(&reg->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(&reg->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, &regval);
+ if (err) {
+ dev_warn(&reg->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(&reg->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 = &regulator_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 = &regulator_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 = &regulator_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 = &regulator_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 = &regulator_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 = &regulator_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 = &regulator_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 = &regulator_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 = &regulator_ops_external,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK",
+ .id = AB3100_BUCK,
+ .ops = &regulator_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,
+ unsigned long 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 = devm_regulator_register(&pdev->dev, 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];
+
+ 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,
+ (unsigned long)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 = dev_get_platdata(&pdev->dev);
+ 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",
+ },
+ .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 <mattias.wallin@stericsson.com>");
+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 000000000..2ca00045e
--- /dev/null
+++ b/drivers/regulator/ab8500-ext.c
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ *
+ * This file is based on drivers/regulator/ab8500.c
+ *
+ * AB8500 external regulators
+ *
+ * ab8500-ext supports the following regulators:
+ * - VextSupply3
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/regulator/ab8500.h>
+
+static struct regulator_consumer_supply ab8500_vaux1_consumers[] = {
+ /* Main display, u8500 R3 uib */
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ /* Main display, u8500 uib and ST uib */
+ REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"),
+ /* Secondary display, ST uib */
+ REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"),
+ /* SFH7741 proximity sensor */
+ REGULATOR_SUPPLY("vcc", "gpio-keys.0"),
+ /* BH1780GLS ambient light sensor */
+ REGULATOR_SUPPLY("vcc", "2-0029"),
+ /* lsm303dlh accelerometer */
+ REGULATOR_SUPPLY("vdd", "2-0018"),
+ /* lsm303dlhc accelerometer */
+ REGULATOR_SUPPLY("vdd", "2-0019"),
+ /* lsm303dlh magnetometer */
+ REGULATOR_SUPPLY("vdd", "2-001e"),
+ /* Rohm BU21013 Touchscreen devices */
+ REGULATOR_SUPPLY("avdd", "3-005c"),
+ REGULATOR_SUPPLY("avdd", "3-005d"),
+ /* Synaptics RMI4 Touchscreen device */
+ REGULATOR_SUPPLY("vdd", "3-004b"),
+ /* L3G4200D Gyroscope device */
+ REGULATOR_SUPPLY("vdd", "2-0068"),
+ /* Ambient light sensor device */
+ REGULATOR_SUPPLY("vdd", "3-0029"),
+ /* Pressure sensor device */
+ REGULATOR_SUPPLY("vdd", "2-005c"),
+ /* Cypress TrueTouch Touchscreen device */
+ REGULATOR_SUPPLY("vcpin", "spi8.0"),
+ /* Camera device */
+ REGULATOR_SUPPLY("vaux12v5", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
+ /* On-board eMMC power */
+ REGULATOR_SUPPLY("vmmc", "sdi4"),
+ /* AB8500 audio codec */
+ REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"),
+ /* AB8500 accessory detect 1 */
+ REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"),
+ /* AB8500 Tv-out device */
+ REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"),
+ /* AV8100 HDMI device */
+ REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"),
+};
+
+static struct regulator_consumer_supply ab8500_vaux3_consumers[] = {
+ REGULATOR_SUPPLY("v-SD-STM", "stm"),
+ /* External MMC slot power */
+ REGULATOR_SUPPLY("vmmc", "sdi0"),
+};
+
+static struct regulator_consumer_supply ab8500_vtvout_consumers[] = {
+ /* TV-out DENC supply */
+ REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"),
+ /* Internal general-purpose ADC */
+ REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"),
+ /* ADC for charger */
+ REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"),
+ /* AB8500 Tv-out device */
+ REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"),
+};
+
+static struct regulator_consumer_supply ab8500_vaud_consumers[] = {
+ /* AB8500 audio-codec main supply */
+ REGULATOR_SUPPLY("vaud", "ab8500-codec.0"),
+};
+
+static struct regulator_consumer_supply ab8500_vamic1_consumers[] = {
+ /* AB8500 audio-codec Mic1 supply */
+ REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"),
+};
+
+static struct regulator_consumer_supply ab8500_vamic2_consumers[] = {
+ /* AB8500 audio-codec Mic2 supply */
+ REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"),
+};
+
+static struct regulator_consumer_supply ab8500_vdmic_consumers[] = {
+ /* AB8500 audio-codec DMic supply */
+ REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"),
+};
+
+static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
+ /* SoC core supply, no device */
+ REGULATOR_SUPPLY("v-intcore", NULL),
+ /* USB Transceiver */
+ REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"),
+ /* Handled by abx500 clk driver */
+ REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"),
+};
+
+static struct regulator_consumer_supply ab8500_vana_consumers[] = {
+ /* DB8500 DSI */
+ REGULATOR_SUPPLY("vdddsi1v2", "mcde"),
+ REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"),
+ REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"),
+ REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"),
+ REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"),
+ REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"),
+ /* DB8500 CSI */
+ REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"),
+};
+
+/* ab8500 regulator register initialization */
+static struct ab8500_regulator_reg_init ab8500_reg_init[] = {
+ /*
+ * VanaRequestCtrl = HP/LP depending on VxRequest
+ * VextSupply1RequestCtrl = HP/LP depending on VxRequest
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00),
+ /*
+ * VextSupply2RequestCtrl = HP/LP depending on VxRequest
+ * VextSupply3RequestCtrl = HP/LP depending on VxRequest
+ * Vaux1RequestCtrl = HP/LP depending on VxRequest
+ * Vaux2RequestCtrl = HP/LP depending on VxRequest
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00),
+ /*
+ * Vaux3RequestCtrl = HP/LP depending on VxRequest
+ * SwHPReq = Control through SWValid disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00),
+ /*
+ * VanaSysClkReq1HPValid = disabled
+ * Vaux1SysClkReq1HPValid = disabled
+ * Vaux2SysClkReq1HPValid = disabled
+ * Vaux3SysClkReq1HPValid = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00),
+ /*
+ * VextSupply1SysClkReq1HPValid = disabled
+ * VextSupply2SysClkReq1HPValid = disabled
+ * VextSupply3SysClkReq1HPValid = SysClkReq1 controlled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40),
+ /*
+ * VanaHwHPReq1Valid = disabled
+ * Vaux1HwHPreq1Valid = disabled
+ * Vaux2HwHPReq1Valid = disabled
+ * Vaux3HwHPReqValid = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00),
+ /*
+ * VextSupply1HwHPReq1Valid = disabled
+ * VextSupply2HwHPReq1Valid = disabled
+ * VextSupply3HwHPReq1Valid = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00),
+ /*
+ * VanaHwHPReq2Valid = disabled
+ * Vaux1HwHPReq2Valid = disabled
+ * Vaux2HwHPReq2Valid = disabled
+ * Vaux3HwHPReq2Valid = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00),
+ /*
+ * VextSupply1HwHPReq2Valid = disabled
+ * VextSupply2HwHPReq2Valid = disabled
+ * VextSupply3HwHPReq2Valid = HWReq2 controlled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04),
+ /*
+ * VanaSwHPReqValid = disabled
+ * Vaux1SwHPReqValid = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00),
+ /*
+ * Vaux2SwHPReqValid = disabled
+ * Vaux3SwHPReqValid = disabled
+ * VextSupply1SwHPReqValid = disabled
+ * VextSupply2SwHPReqValid = disabled
+ * VextSupply3SwHPReqValid = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00),
+ /*
+ * SysClkReq2Valid1 = SysClkReq2 controlled
+ * SysClkReq3Valid1 = disabled
+ * SysClkReq4Valid1 = SysClkReq4 controlled
+ * SysClkReq5Valid1 = disabled
+ * SysClkReq6Valid1 = SysClkReq6 controlled
+ * SysClkReq7Valid1 = disabled
+ * SysClkReq8Valid1 = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a),
+ /*
+ * SysClkReq2Valid2 = disabled
+ * SysClkReq3Valid2 = disabled
+ * SysClkReq4Valid2 = disabled
+ * SysClkReq5Valid2 = disabled
+ * SysClkReq6Valid2 = SysClkReq6 controlled
+ * SysClkReq7Valid2 = disabled
+ * SysClkReq8Valid2 = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20),
+ /*
+ * VTVoutEna = disabled
+ * Vintcore12Ena = disabled
+ * Vintcore12Sel = 1.25 V
+ * Vintcore12LP = inactive (HP)
+ * VTVoutLP = inactive (HP)
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10),
+ /*
+ * VaudioEna = disabled
+ * VdmicEna = disabled
+ * Vamic1Ena = disabled
+ * Vamic2Ena = disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00),
+ /*
+ * Vamic1_dzout = high-Z when Vamic1 is disabled
+ * Vamic2_dzout = high-Z when Vamic2 is disabled
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00),
+ /*
+ * VPll = Hw controlled (NOTE! PRCMU bits)
+ * VanaRegu = force off
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02),
+ /*
+ * VrefDDREna = disabled
+ * VrefDDRSleepMode = inactive (no pulldown)
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00),
+ /*
+ * VextSupply1Regu = force LP
+ * VextSupply2Regu = force OFF
+ * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP)
+ * ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0
+ * ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0
+ */
+ INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13),
+ /*
+ * Vaux1Regu = force HP
+ * Vaux2Regu = force off
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01),
+ /*
+ * Vaux3Regu = force off
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00),
+ /*
+ * Vaux1Sel = 2.8 V
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C),
+ /*
+ * Vaux2Sel = 2.9 V
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d),
+ /*
+ * Vaux3Sel = 2.91 V
+ */
+ INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07),
+ /*
+ * VextSupply12LP = disabled (no LP)
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00),
+ /*
+ * Vaux1Disch = short discharge time
+ * Vaux2Disch = short discharge time
+ * Vaux3Disch = short discharge time
+ * Vintcore12Disch = short discharge time
+ * VTVoutDisch = short discharge time
+ * VaudioDisch = short discharge time
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00),
+ /*
+ * VanaDisch = short discharge time
+ * VdmicPullDownEna = pulldown disabled when Vdmic is disabled
+ * VdmicDisch = short discharge time
+ */
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00),
+};
+
+/* AB8500 regulators */
+static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
+ /* supplies to the display/camera */
+ [AB8500_LDO_AUX1] = {
+ .supply_regulator = "ab8500-ext-supply3",
+ .constraints = {
+ .name = "V-DISPLAY",
+ .min_uV = 2800000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ .boot_on = 1, /* display is on at boot */
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers),
+ .consumer_supplies = ab8500_vaux1_consumers,
+ },
+ /* supplies to the on-board eMMC */
+ [AB8500_LDO_AUX2] = {
+ .supply_regulator = "ab8500-ext-supply3",
+ .constraints = {
+ .name = "V-eMMC1",
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers),
+ .consumer_supplies = ab8500_vaux2_consumers,
+ },
+ /* supply for VAUX3, supplies to SDcard slots */
+ [AB8500_LDO_AUX3] = {
+ .supply_regulator = "ab8500-ext-supply3",
+ .constraints = {
+ .name = "V-MMC-SD",
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers),
+ .consumer_supplies = ab8500_vaux3_consumers,
+ },
+ /* supply for tvout, gpadc, TVOUT LDO */
+ [AB8500_LDO_TVOUT] = {
+ .constraints = {
+ .name = "V-TVOUT",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers),
+ .consumer_supplies = ab8500_vtvout_consumers,
+ },
+ /* supply for ab8500-vaudio, VAUDIO LDO */
+ [AB8500_LDO_AUDIO] = {
+ .constraints = {
+ .name = "V-AUD",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers),
+ .consumer_supplies = ab8500_vaud_consumers,
+ },
+ /* supply for v-anamic1 VAMic1-LDO */
+ [AB8500_LDO_ANAMIC1] = {
+ .constraints = {
+ .name = "V-AMIC1",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers),
+ .consumer_supplies = ab8500_vamic1_consumers,
+ },
+ /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */
+ [AB8500_LDO_ANAMIC2] = {
+ .constraints = {
+ .name = "V-AMIC2",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers),
+ .consumer_supplies = ab8500_vamic2_consumers,
+ },
+ /* supply for v-dmic, VDMIC LDO */
+ [AB8500_LDO_DMIC] = {
+ .constraints = {
+ .name = "V-DMIC",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers),
+ .consumer_supplies = ab8500_vdmic_consumers,
+ },
+ /* supply for v-intcore12, VINTCORE12 LDO */
+ [AB8500_LDO_INTCORE] = {
+ .constraints = {
+ .name = "V-INTCORE",
+ .min_uV = 1250000,
+ .max_uV = 1350000,
+ .input_uV = 1800000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE |
+ REGULATOR_CHANGE_DRMS,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers),
+ .consumer_supplies = ab8500_vintcore_consumers,
+ },
+ /* supply for U8500 CSI-DSI, VANA LDO */
+ [AB8500_LDO_ANA] = {
+ .constraints = {
+ .name = "V-CSI-DSI",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers),
+ .consumer_supplies = ab8500_vana_consumers,
+ },
+};
+
+/* supply for VextSupply3 */
+static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = {
+ /* SIM supply for 3 V SIM cards */
+ REGULATOR_SUPPLY("vinvsim", "sim-detect.0"),
+};
+
+/*
+ * AB8500 external regulators
+ */
+static struct regulator_init_data ab8500_ext_regulators[] = {
+ /* fixed Vbat supplies VSMPS1_EXT_1V8 */
+ [AB8500_EXT_SUPPLY1] = {
+ .constraints = {
+ .name = "ab8500-ext-supply1",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .initial_mode = REGULATOR_MODE_IDLE,
+ .boot_on = 1,
+ .always_on = 1,
+ },
+ },
+ /* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */
+ [AB8500_EXT_SUPPLY2] = {
+ .constraints = {
+ .name = "ab8500-ext-supply2",
+ .min_uV = 1360000,
+ .max_uV = 1360000,
+ },
+ },
+ /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */
+ [AB8500_EXT_SUPPLY3] = {
+ .constraints = {
+ .name = "ab8500-ext-supply3",
+ .min_uV = 3400000,
+ .max_uV = 3400000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .boot_on = 1,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_ext_supply3_consumers),
+ .consumer_supplies = ab8500_ext_supply3_consumers,
+ },
+};
+
+static struct ab8500_regulator_platform_data ab8500_regulator_plat_data = {
+ .reg_init = ab8500_reg_init,
+ .num_reg_init = ARRAY_SIZE(ab8500_reg_init),
+ .regulator = ab8500_regulators,
+ .num_regulator = ARRAY_SIZE(ab8500_regulators),
+ .ext_regulator = ab8500_ext_regulators,
+ .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators),
+};
+
+/**
+ * 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, &regval);
+ 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_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct regulation_constraints *regu_constraints = rdev->constraints;
+
+ if (!regu_constraints) {
+ dev_err(rdev_get_dev(rdev), "No regulator constraints\n");
+ return -EINVAL;
+ }
+
+ if (regu_constraints->min_uV == min_uV &&
+ regu_constraints->max_uV == max_uV)
+ return 0;
+
+ dev_err(rdev_get_dev(rdev),
+ "Requested min %duV max %duV != constrained min %duV max %duV\n",
+ min_uV, max_uV,
+ regu_constraints->min_uV, regu_constraints->max_uV);
+
+ return -EINVAL;
+}
+
+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,
+ .set_voltage = ab8500_ext_set_voltage,
+ .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,
+ },
+};
+
+static struct of_regulator_match ab8500_ext_regulator_match[] = {
+ { .name = "ab8500_ext1", .driver_data = (void *) AB8500_EXT_SUPPLY1, },
+ { .name = "ab8500_ext2", .driver_data = (void *) AB8500_EXT_SUPPLY2, },
+ { .name = "ab8500_ext3", .driver_data = (void *) AB8500_EXT_SUPPLY3, },
+};
+
+static int ab8500_ext_regulator_probe(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_regulator_platform_data *pdata = &ab8500_regulator_plat_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct regulator_config config = { };
+ int i, err;
+
+ if (np) {
+ err = of_regulator_match(&pdev->dev, np,
+ ab8500_ext_regulator_match,
+ ARRAY_SIZE(ab8500_ext_regulator_match));
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Error parsing regulator init data: %d\n", err);
+ return err;
+ }
+ }
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\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.driver_data = info;
+ config.of_node = ab8500_ext_regulator_match[i].of_node;
+ config.init_data = (np) ?
+ ab8500_ext_regulator_match[i].init_data :
+ &pdata->ext_regulator[i];
+
+ /* register regulator with framework */
+ info->rdev = devm_regulator_register(&pdev->dev, &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);
+ return err;
+ }
+
+ dev_dbg(rdev_get_dev(info->rdev),
+ "%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+static struct platform_driver ab8500_ext_regulator_driver = {
+ .probe = ab8500_ext_regulator_probe,
+ .driver = {
+ .name = "ab8500-ext-regulator",
+ },
+};
+
+static int __init ab8500_ext_regulator_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ab8500_ext_regulator_driver);
+ if (ret)
+ pr_err("Failed to register ab8500 ext regulator: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(ab8500_ext_regulator_init);
+
+static void __exit ab8500_ext_regulator_exit(void)
+{
+ platform_driver_unregister(&ab8500_ext_regulator_driver);
+}
+module_exit(ab8500_ext_regulator_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
+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 000000000..605788204
--- /dev/null
+++ b/drivers/regulator/ab8500.c
@@ -0,0 +1,1682 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ * Daniel Willerud <daniel.willerud@stericsson.com> 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500.h>
+#include <linux/slab.h>
+
+/**
+ * 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_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 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, &regval);
+ 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, &regval);
+ 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 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 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 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,
+ .supply_name = "vin",
+ },
+ .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,
+ .supply_name = "vin",
+ },
+ .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,
+ .supply_name = "vin",
+ },
+ .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_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,
+ },
+};
+
+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],
+};
+
+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),
+};
+
+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_anamic2", .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_anamic2", .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 {
+ 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_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 {
+ 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_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 = { };
+
+ /* 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 = devm_regulator_register(&pdev->dev, &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);
+ }
+
+ 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 of_regulator_match *match;
+ int err, i;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+
+ abx500_get_regulator_info(ab8500);
+
+ 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;
+ }
+
+ match = abx500_regulator.match;
+ 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 struct platform_driver ab8500_regulator_driver = {
+ .probe = ab8500_regulator_probe,
+ .driver = {
+ .name = "ab8500-regulator",
+ },
+};
+
+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 <sundar.iyer@stericsson.com>");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
+MODULE_AUTHOR("Daniel Willerud <daniel.willerud@stericsson.com>");
+MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
+MODULE_ALIAS("platform:ab8500-regulator");
diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c
new file mode 100644
index 000000000..e0239cf3f
--- /dev/null
+++ b/drivers/regulator/act8865-regulator.c
@@ -0,0 +1,625 @@
+/*
+ * act8865-regulator.c - Voltage regulation for active-semi ACT88xx PMUs
+ *
+ * http://www.active-semi.com/products/power-management-units/act88xx/
+ *
+ * Copyright (C) 2013 Atmel Corporation
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/act8865.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+
+/*
+ * ACT8600 Global Register Map.
+ */
+#define ACT8600_SYS_MODE 0x00
+#define ACT8600_SYS_CTRL 0x01
+#define ACT8600_DCDC1_VSET 0x10
+#define ACT8600_DCDC1_CTRL 0x12
+#define ACT8600_DCDC2_VSET 0x20
+#define ACT8600_DCDC2_CTRL 0x22
+#define ACT8600_DCDC3_VSET 0x30
+#define ACT8600_DCDC3_CTRL 0x32
+#define ACT8600_SUDCDC4_VSET 0x40
+#define ACT8600_SUDCDC4_CTRL 0x41
+#define ACT8600_LDO5_VSET 0x50
+#define ACT8600_LDO5_CTRL 0x51
+#define ACT8600_LDO6_VSET 0x60
+#define ACT8600_LDO6_CTRL 0x61
+#define ACT8600_LDO7_VSET 0x70
+#define ACT8600_LDO7_CTRL 0x71
+#define ACT8600_LDO8_VSET 0x80
+#define ACT8600_LDO8_CTRL 0x81
+#define ACT8600_LDO910_CTRL 0x91
+#define ACT8600_APCH0 0xA1
+#define ACT8600_APCH1 0xA8
+#define ACT8600_APCH2 0xA9
+#define ACT8600_APCH_STAT 0xAA
+#define ACT8600_OTG0 0xB0
+#define ACT8600_OTG1 0xB2
+
+/*
+ * ACT8846 Global Register Map.
+ */
+#define ACT8846_SYS0 0x00
+#define ACT8846_SYS1 0x01
+#define ACT8846_REG1_VSET 0x10
+#define ACT8846_REG1_CTRL 0x12
+#define ACT8846_REG2_VSET0 0x20
+#define ACT8846_REG2_VSET1 0x21
+#define ACT8846_REG2_CTRL 0x22
+#define ACT8846_REG3_VSET0 0x30
+#define ACT8846_REG3_VSET1 0x31
+#define ACT8846_REG3_CTRL 0x32
+#define ACT8846_REG4_VSET0 0x40
+#define ACT8846_REG4_VSET1 0x41
+#define ACT8846_REG4_CTRL 0x42
+#define ACT8846_REG5_VSET 0x50
+#define ACT8846_REG5_CTRL 0x51
+#define ACT8846_REG6_VSET 0x58
+#define ACT8846_REG6_CTRL 0x59
+#define ACT8846_REG7_VSET 0x60
+#define ACT8846_REG7_CTRL 0x61
+#define ACT8846_REG8_VSET 0x68
+#define ACT8846_REG8_CTRL 0x69
+#define ACT8846_REG9_VSET 0x70
+#define ACT8846_REG9_CTRL 0x71
+#define ACT8846_REG10_VSET 0x80
+#define ACT8846_REG10_CTRL 0x81
+#define ACT8846_REG11_VSET 0x90
+#define ACT8846_REG11_CTRL 0x91
+#define ACT8846_REG12_VSET 0xa0
+#define ACT8846_REG12_CTRL 0xa1
+#define ACT8846_REG13_CTRL 0xb1
+#define ACT8846_GLB_OFF_CTRL 0xc3
+#define ACT8846_OFF_SYSMASK 0x18
+
+/*
+ * ACT8865 Global Register Map.
+ */
+#define ACT8865_SYS_MODE 0x00
+#define ACT8865_SYS_CTRL 0x01
+#define ACT8865_DCDC1_VSET1 0x20
+#define ACT8865_DCDC1_VSET2 0x21
+#define ACT8865_DCDC1_CTRL 0x22
+#define ACT8865_DCDC2_VSET1 0x30
+#define ACT8865_DCDC2_VSET2 0x31
+#define ACT8865_DCDC2_CTRL 0x32
+#define ACT8865_DCDC3_VSET1 0x40
+#define ACT8865_DCDC3_VSET2 0x41
+#define ACT8865_DCDC3_CTRL 0x42
+#define ACT8865_LDO1_VSET 0x50
+#define ACT8865_LDO1_CTRL 0x51
+#define ACT8865_LDO2_VSET 0x54
+#define ACT8865_LDO2_CTRL 0x55
+#define ACT8865_LDO3_VSET 0x60
+#define ACT8865_LDO3_CTRL 0x61
+#define ACT8865_LDO4_VSET 0x64
+#define ACT8865_LDO4_CTRL 0x65
+#define ACT8865_MSTROFF 0x20
+
+/*
+ * Field Definitions.
+ */
+#define ACT8865_ENA 0x80 /* ON - [7] */
+#define ACT8865_VSEL_MASK 0x3F /* VSET - [5:0] */
+
+
+#define ACT8600_LDO10_ENA 0x40 /* ON - [6] */
+#define ACT8600_SUDCDC_VSEL_MASK 0xFF /* SUDCDC VSET - [7:0] */
+
+/*
+ * ACT8865 voltage number
+ */
+#define ACT8865_VOLTAGE_NUM 64
+#define ACT8600_SUDCDC_VOLTAGE_NUM 256
+
+struct act8865 {
+ struct regmap *regmap;
+ int off_reg;
+ int off_mask;
+};
+
+static const struct regmap_range act8600_reg_ranges[] = {
+ regmap_reg_range(0x00, 0x01),
+ regmap_reg_range(0x10, 0x10),
+ regmap_reg_range(0x12, 0x12),
+ regmap_reg_range(0x20, 0x20),
+ regmap_reg_range(0x22, 0x22),
+ regmap_reg_range(0x30, 0x30),
+ regmap_reg_range(0x32, 0x32),
+ regmap_reg_range(0x40, 0x41),
+ regmap_reg_range(0x50, 0x51),
+ regmap_reg_range(0x60, 0x61),
+ regmap_reg_range(0x70, 0x71),
+ regmap_reg_range(0x80, 0x81),
+ regmap_reg_range(0x91, 0x91),
+ regmap_reg_range(0xA1, 0xA1),
+ regmap_reg_range(0xA8, 0xAA),
+ regmap_reg_range(0xB0, 0xB0),
+ regmap_reg_range(0xB2, 0xB2),
+ regmap_reg_range(0xC1, 0xC1),
+};
+
+static const struct regmap_range act8600_reg_ro_ranges[] = {
+ regmap_reg_range(0xAA, 0xAA),
+ regmap_reg_range(0xC1, 0xC1),
+};
+
+static const struct regmap_range act8600_reg_volatile_ranges[] = {
+ regmap_reg_range(0x00, 0x01),
+ regmap_reg_range(0x12, 0x12),
+ regmap_reg_range(0x22, 0x22),
+ regmap_reg_range(0x32, 0x32),
+ regmap_reg_range(0x41, 0x41),
+ regmap_reg_range(0x51, 0x51),
+ regmap_reg_range(0x61, 0x61),
+ regmap_reg_range(0x71, 0x71),
+ regmap_reg_range(0x81, 0x81),
+ regmap_reg_range(0xA8, 0xA8),
+ regmap_reg_range(0xAA, 0xAA),
+ regmap_reg_range(0xB0, 0xB0),
+ regmap_reg_range(0xC1, 0xC1),
+};
+
+static const struct regmap_access_table act8600_write_ranges_table = {
+ .yes_ranges = act8600_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(act8600_reg_ranges),
+ .no_ranges = act8600_reg_ro_ranges,
+ .n_no_ranges = ARRAY_SIZE(act8600_reg_ro_ranges),
+};
+
+static const struct regmap_access_table act8600_read_ranges_table = {
+ .yes_ranges = act8600_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(act8600_reg_ranges),
+};
+
+static const struct regmap_access_table act8600_volatile_ranges_table = {
+ .yes_ranges = act8600_reg_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(act8600_reg_volatile_ranges),
+};
+
+static const struct regmap_config act8600_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+ .wr_table = &act8600_write_ranges_table,
+ .rd_table = &act8600_read_ranges_table,
+ .volatile_table = &act8600_volatile_ranges_table,
+};
+
+static const struct regmap_config act8865_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct regulator_linear_range act8865_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000),
+ REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000),
+ REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
+};
+
+static const struct regulator_linear_range act8600_sudcdc_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(3000000, 0, 63, 0),
+ REGULATOR_LINEAR_RANGE(3000000, 64, 159, 100000),
+ REGULATOR_LINEAR_RANGE(12600000, 160, 191, 200000),
+ REGULATOR_LINEAR_RANGE(19000000, 192, 247, 400000),
+ REGULATOR_LINEAR_RANGE(41400000, 248, 255, 0),
+};
+
+static struct regulator_ops act8865_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops act8865_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define ACT88xx_REG(_name, _family, _id, _vsel_reg, _supply) \
+ [_family##_ID_##_id] = { \
+ .name = _name, \
+ .supply_name = _supply, \
+ .id = _family##_ID_##_id, \
+ .type = REGULATOR_VOLTAGE, \
+ .ops = &act8865_ops, \
+ .n_voltages = ACT8865_VOLTAGE_NUM, \
+ .linear_ranges = act8865_voltage_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(act8865_voltage_ranges), \
+ .vsel_reg = _family##_##_id##_##_vsel_reg, \
+ .vsel_mask = ACT8865_VSEL_MASK, \
+ .enable_reg = _family##_##_id##_CTRL, \
+ .enable_mask = ACT8865_ENA, \
+ .owner = THIS_MODULE, \
+ }
+
+static const struct regulator_desc act8600_regulators[] = {
+ ACT88xx_REG("DCDC1", ACT8600, DCDC1, VSET, "vp1"),
+ ACT88xx_REG("DCDC2", ACT8600, DCDC2, VSET, "vp2"),
+ ACT88xx_REG("DCDC3", ACT8600, DCDC3, VSET, "vp3"),
+ {
+ .name = "SUDCDC_REG4",
+ .id = ACT8600_ID_SUDCDC4,
+ .ops = &act8865_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = ACT8600_SUDCDC_VOLTAGE_NUM,
+ .linear_ranges = act8600_sudcdc_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(act8600_sudcdc_voltage_ranges),
+ .vsel_reg = ACT8600_SUDCDC4_VSET,
+ .vsel_mask = ACT8600_SUDCDC_VSEL_MASK,
+ .enable_reg = ACT8600_SUDCDC4_CTRL,
+ .enable_mask = ACT8865_ENA,
+ .owner = THIS_MODULE,
+ },
+ ACT88xx_REG("LDO5", ACT8600, LDO5, VSET, "inl"),
+ ACT88xx_REG("LDO6", ACT8600, LDO6, VSET, "inl"),
+ ACT88xx_REG("LDO7", ACT8600, LDO7, VSET, "inl"),
+ ACT88xx_REG("LDO8", ACT8600, LDO8, VSET, "inl"),
+ {
+ .name = "LDO_REG9",
+ .id = ACT8600_ID_LDO9,
+ .ops = &act8865_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 1,
+ .fixed_uV = 3300000,
+ .enable_reg = ACT8600_LDO910_CTRL,
+ .enable_mask = ACT8865_ENA,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO_REG10",
+ .id = ACT8600_ID_LDO10,
+ .ops = &act8865_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 1,
+ .fixed_uV = 1200000,
+ .enable_reg = ACT8600_LDO910_CTRL,
+ .enable_mask = ACT8600_LDO10_ENA,
+ .owner = THIS_MODULE,
+ },
+};
+
+static const struct regulator_desc act8846_regulators[] = {
+ ACT88xx_REG("REG1", ACT8846, REG1, VSET, "vp1"),
+ ACT88xx_REG("REG2", ACT8846, REG2, VSET0, "vp2"),
+ ACT88xx_REG("REG3", ACT8846, REG3, VSET0, "vp3"),
+ ACT88xx_REG("REG4", ACT8846, REG4, VSET0, "vp4"),
+ ACT88xx_REG("REG5", ACT8846, REG5, VSET, "inl1"),
+ ACT88xx_REG("REG6", ACT8846, REG6, VSET, "inl1"),
+ ACT88xx_REG("REG7", ACT8846, REG7, VSET, "inl1"),
+ ACT88xx_REG("REG8", ACT8846, REG8, VSET, "inl2"),
+ ACT88xx_REG("REG9", ACT8846, REG9, VSET, "inl2"),
+ ACT88xx_REG("REG10", ACT8846, REG10, VSET, "inl3"),
+ ACT88xx_REG("REG11", ACT8846, REG11, VSET, "inl3"),
+ ACT88xx_REG("REG12", ACT8846, REG12, VSET, "inl3"),
+};
+
+static const struct regulator_desc act8865_regulators[] = {
+ ACT88xx_REG("DCDC_REG1", ACT8865, DCDC1, VSET1, "vp1"),
+ ACT88xx_REG("DCDC_REG2", ACT8865, DCDC2, VSET1, "vp2"),
+ ACT88xx_REG("DCDC_REG3", ACT8865, DCDC3, VSET1, "vp3"),
+ ACT88xx_REG("LDO_REG1", ACT8865, LDO1, VSET, "inl45"),
+ ACT88xx_REG("LDO_REG2", ACT8865, LDO2, VSET, "inl45"),
+ ACT88xx_REG("LDO_REG3", ACT8865, LDO3, VSET, "inl67"),
+ ACT88xx_REG("LDO_REG4", ACT8865, LDO4, VSET, "inl67"),
+};
+
+static const struct regulator_desc act8865_alt_regulators[] = {
+ ACT88xx_REG("DCDC_REG1", ACT8865, DCDC1, VSET2, "vp1"),
+ ACT88xx_REG("DCDC_REG2", ACT8865, DCDC2, VSET2, "vp2"),
+ ACT88xx_REG("DCDC_REG3", ACT8865, DCDC3, VSET2, "vp3"),
+ ACT88xx_REG("LDO_REG1", ACT8865, LDO1, VSET, "inl45"),
+ ACT88xx_REG("LDO_REG2", ACT8865, LDO2, VSET, "inl45"),
+ ACT88xx_REG("LDO_REG3", ACT8865, LDO3, VSET, "inl67"),
+ ACT88xx_REG("LDO_REG4", ACT8865, LDO4, VSET, "inl67"),
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id act8865_dt_ids[] = {
+ { .compatible = "active-semi,act8600", .data = (void *)ACT8600 },
+ { .compatible = "active-semi,act8846", .data = (void *)ACT8846 },
+ { .compatible = "active-semi,act8865", .data = (void *)ACT8865 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, act8865_dt_ids);
+
+static struct of_regulator_match act8846_matches[] = {
+ [ACT8846_ID_REG1] = { .name = "REG1" },
+ [ACT8846_ID_REG2] = { .name = "REG2" },
+ [ACT8846_ID_REG3] = { .name = "REG3" },
+ [ACT8846_ID_REG4] = { .name = "REG4" },
+ [ACT8846_ID_REG5] = { .name = "REG5" },
+ [ACT8846_ID_REG6] = { .name = "REG6" },
+ [ACT8846_ID_REG7] = { .name = "REG7" },
+ [ACT8846_ID_REG8] = { .name = "REG8" },
+ [ACT8846_ID_REG9] = { .name = "REG9" },
+ [ACT8846_ID_REG10] = { .name = "REG10" },
+ [ACT8846_ID_REG11] = { .name = "REG11" },
+ [ACT8846_ID_REG12] = { .name = "REG12" },
+};
+
+static struct of_regulator_match act8865_matches[] = {
+ [ACT8865_ID_DCDC1] = { .name = "DCDC_REG1"},
+ [ACT8865_ID_DCDC2] = { .name = "DCDC_REG2"},
+ [ACT8865_ID_DCDC3] = { .name = "DCDC_REG3"},
+ [ACT8865_ID_LDO1] = { .name = "LDO_REG1"},
+ [ACT8865_ID_LDO2] = { .name = "LDO_REG2"},
+ [ACT8865_ID_LDO3] = { .name = "LDO_REG3"},
+ [ACT8865_ID_LDO4] = { .name = "LDO_REG4"},
+};
+
+static struct of_regulator_match act8600_matches[] = {
+ [ACT8600_ID_DCDC1] = { .name = "DCDC_REG1"},
+ [ACT8600_ID_DCDC2] = { .name = "DCDC_REG2"},
+ [ACT8600_ID_DCDC3] = { .name = "DCDC_REG3"},
+ [ACT8600_ID_SUDCDC4] = { .name = "SUDCDC_REG4"},
+ [ACT8600_ID_LDO5] = { .name = "LDO_REG5"},
+ [ACT8600_ID_LDO6] = { .name = "LDO_REG6"},
+ [ACT8600_ID_LDO7] = { .name = "LDO_REG7"},
+ [ACT8600_ID_LDO8] = { .name = "LDO_REG8"},
+ [ACT8600_ID_LDO9] = { .name = "LDO_REG9"},
+ [ACT8600_ID_LDO10] = { .name = "LDO_REG10"},
+};
+
+static int act8865_pdata_from_dt(struct device *dev,
+ struct act8865_platform_data *pdata,
+ unsigned long type)
+{
+ int matched, i, num_matches;
+ struct device_node *np;
+ struct act8865_regulator_data *regulator;
+ struct of_regulator_match *matches;
+
+ switch (type) {
+ case ACT8600:
+ matches = act8600_matches;
+ num_matches = ARRAY_SIZE(act8600_matches);
+ break;
+ case ACT8846:
+ matches = act8846_matches;
+ num_matches = ARRAY_SIZE(act8846_matches);
+ break;
+ case ACT8865:
+ matches = act8865_matches;
+ num_matches = ARRAY_SIZE(act8865_matches);
+ break;
+ default:
+ dev_err(dev, "invalid device id %lu\n", type);
+ return -EINVAL;
+ }
+
+ np = of_get_child_by_name(dev->of_node, "regulators");
+ if (!np) {
+ dev_err(dev, "missing 'regulators' subnode in DT\n");
+ return -EINVAL;
+ }
+
+ matched = of_regulator_match(dev, np, matches, num_matches);
+ of_node_put(np);
+ if (matched <= 0)
+ return matched;
+
+ pdata->regulators = devm_kcalloc(dev,
+ num_matches,
+ sizeof(struct act8865_regulator_data),
+ GFP_KERNEL);
+ if (!pdata->regulators)
+ return -ENOMEM;
+
+ pdata->num_regulators = num_matches;
+ regulator = pdata->regulators;
+
+ for (i = 0; i < num_matches; i++) {
+ regulator->id = i;
+ regulator->name = matches[i].name;
+ regulator->init_data = matches[i].init_data;
+ regulator->of_node = matches[i].of_node;
+ regulator++;
+ }
+
+ return 0;
+}
+#else
+static inline int act8865_pdata_from_dt(struct device *dev,
+ struct act8865_platform_data *pdata,
+ unsigned long type)
+{
+ return 0;
+}
+#endif
+
+static struct act8865_regulator_data *act8865_get_regulator_data(
+ int id, struct act8865_platform_data *pdata)
+{
+ int i;
+
+ if (!pdata)
+ return NULL;
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ if (pdata->regulators[i].id == id)
+ return &pdata->regulators[i];
+ }
+
+ return NULL;
+}
+
+static struct i2c_client *act8865_i2c_client;
+static void act8865_power_off(void)
+{
+ struct act8865 *act8865;
+
+ act8865 = i2c_get_clientdata(act8865_i2c_client);
+ regmap_write(act8865->regmap, act8865->off_reg, act8865->off_mask);
+ while (1);
+}
+
+static int act8865_pmic_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ const struct regulator_desc *regulators;
+ struct act8865_platform_data pdata_of, *pdata;
+ struct device *dev = &client->dev;
+ int i, ret, num_regulators;
+ struct act8865 *act8865;
+ const struct regmap_config *regmap_config;
+ unsigned long type;
+ int off_reg, off_mask;
+ int voltage_select = 0;
+
+ pdata = dev_get_platdata(dev);
+
+ if (dev->of_node && !pdata) {
+ const struct of_device_id *id;
+
+ id = of_match_device(of_match_ptr(act8865_dt_ids), dev);
+ if (!id)
+ return -ENODEV;
+
+ type = (unsigned long) id->data;
+
+ voltage_select = !!of_get_property(dev->of_node,
+ "active-semi,vsel-high",
+ NULL);
+ } else {
+ type = i2c_id->driver_data;
+ }
+
+ switch (type) {
+ case ACT8600:
+ regulators = act8600_regulators;
+ num_regulators = ARRAY_SIZE(act8600_regulators);
+ regmap_config = &act8600_regmap_config;
+ off_reg = -1;
+ off_mask = -1;
+ break;
+ case ACT8846:
+ regulators = act8846_regulators;
+ num_regulators = ARRAY_SIZE(act8846_regulators);
+ regmap_config = &act8865_regmap_config;
+ off_reg = ACT8846_GLB_OFF_CTRL;
+ off_mask = ACT8846_OFF_SYSMASK;
+ break;
+ case ACT8865:
+ if (voltage_select) {
+ regulators = act8865_alt_regulators;
+ num_regulators = ARRAY_SIZE(act8865_alt_regulators);
+ } else {
+ regulators = act8865_regulators;
+ num_regulators = ARRAY_SIZE(act8865_regulators);
+ }
+ regmap_config = &act8865_regmap_config;
+ off_reg = ACT8865_SYS_CTRL;
+ off_mask = ACT8865_MSTROFF;
+ break;
+ default:
+ dev_err(dev, "invalid device id %lu\n", type);
+ return -EINVAL;
+ }
+
+ if (dev->of_node && !pdata) {
+ ret = act8865_pdata_from_dt(dev, &pdata_of, type);
+ if (ret < 0)
+ return ret;
+
+ pdata = &pdata_of;
+ }
+
+ act8865 = devm_kzalloc(dev, sizeof(struct act8865), GFP_KERNEL);
+ if (!act8865)
+ return -ENOMEM;
+
+ act8865->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(act8865->regmap)) {
+ ret = PTR_ERR(act8865->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ if (of_device_is_system_power_controller(dev->of_node)) {
+ if (!pm_power_off && (off_reg > 0)) {
+ act8865_i2c_client = client;
+ act8865->off_reg = off_reg;
+ act8865->off_mask = off_mask;
+ pm_power_off = act8865_power_off;
+ } else {
+ dev_err(dev, "Failed to set poweroff capability, already defined\n");
+ }
+ }
+
+ /* Finally register devices */
+ for (i = 0; i < num_regulators; i++) {
+ const struct regulator_desc *desc = &regulators[i];
+ struct regulator_config config = { };
+ struct act8865_regulator_data *rdata;
+ struct regulator_dev *rdev;
+
+ config.dev = dev;
+ config.driver_data = act8865;
+ config.regmap = act8865->regmap;
+
+ rdata = act8865_get_regulator_data(desc->id, pdata);
+ if (rdata) {
+ config.init_data = rdata->init_data;
+ config.of_node = rdata->of_node;
+ }
+
+ rdev = devm_regulator_register(dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n", desc->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ i2c_set_clientdata(client, act8865);
+
+ return 0;
+}
+
+static const struct i2c_device_id act8865_ids[] = {
+ { .name = "act8600", .driver_data = ACT8600 },
+ { .name = "act8846", .driver_data = ACT8846 },
+ { .name = "act8865", .driver_data = ACT8865 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, act8865_ids);
+
+static struct i2c_driver act8865_pmic_driver = {
+ .driver = {
+ .name = "act8865",
+ },
+ .probe = act8865_pmic_probe,
+ .id_table = act8865_ids,
+};
+
+module_i2c_driver(act8865_pmic_driver);
+
+MODULE_DESCRIPTION("active-semi act88xx voltage regulator driver");
+MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c
new file mode 100644
index 000000000..43fda8b44
--- /dev/null
+++ b/drivers/regulator/act8945a-regulator.c
@@ -0,0 +1,165 @@
+/*
+ * Voltage regulation driver for active-semi ACT8945A PMIC
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Wenyou Yang <wenyou.yang@atmel.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 <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/**
+ * ACT8945A Global Register Map.
+ */
+#define ACT8945A_SYS_MODE 0x00
+#define ACT8945A_SYS_CTRL 0x01
+#define ACT8945A_DCDC1_VSET1 0x20
+#define ACT8945A_DCDC1_VSET2 0x21
+#define ACT8945A_DCDC1_CTRL 0x22
+#define ACT8945A_DCDC2_VSET1 0x30
+#define ACT8945A_DCDC2_VSET2 0x31
+#define ACT8945A_DCDC2_CTRL 0x32
+#define ACT8945A_DCDC3_VSET1 0x40
+#define ACT8945A_DCDC3_VSET2 0x41
+#define ACT8945A_DCDC3_CTRL 0x42
+#define ACT8945A_LDO1_VSET 0x50
+#define ACT8945A_LDO1_CTRL 0x51
+#define ACT8945A_LDO2_VSET 0x54
+#define ACT8945A_LDO2_CTRL 0x55
+#define ACT8945A_LDO3_VSET 0x60
+#define ACT8945A_LDO3_CTRL 0x61
+#define ACT8945A_LDO4_VSET 0x64
+#define ACT8945A_LDO4_CTRL 0x65
+
+/**
+ * Field Definitions.
+ */
+#define ACT8945A_ENA 0x80 /* ON - [7] */
+#define ACT8945A_VSEL_MASK 0x3F /* VSET - [5:0] */
+
+/**
+ * ACT8945A Voltage Number
+ */
+#define ACT8945A_VOLTAGE_NUM 64
+
+enum {
+ ACT8945A_ID_DCDC1,
+ ACT8945A_ID_DCDC2,
+ ACT8945A_ID_DCDC3,
+ ACT8945A_ID_LDO1,
+ ACT8945A_ID_LDO2,
+ ACT8945A_ID_LDO3,
+ ACT8945A_ID_LDO4,
+ ACT8945A_REG_NUM,
+};
+
+static const struct regulator_linear_range act8945a_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000),
+ REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000),
+ REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
+};
+
+static const struct regulator_ops act8945a_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply) \
+ [_family##_ID_##_id] = { \
+ .name = _name, \
+ .supply_name = _supply, \
+ .of_match = of_match_ptr("REG_"#_id), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = _family##_ID_##_id, \
+ .type = REGULATOR_VOLTAGE, \
+ .ops = &act8945a_ops, \
+ .n_voltages = ACT8945A_VOLTAGE_NUM, \
+ .linear_ranges = act8945a_voltage_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(act8945a_voltage_ranges), \
+ .vsel_reg = _family##_##_id##_##_vsel_reg, \
+ .vsel_mask = ACT8945A_VSEL_MASK, \
+ .enable_reg = _family##_##_id##_CTRL, \
+ .enable_mask = ACT8945A_ENA, \
+ .owner = THIS_MODULE, \
+ }
+
+static const struct regulator_desc act8945a_regulators[] = {
+ ACT89xx_REG("DCDC_REG1", ACT8945A, DCDC1, VSET1, "vp1"),
+ ACT89xx_REG("DCDC_REG2", ACT8945A, DCDC2, VSET1, "vp2"),
+ ACT89xx_REG("DCDC_REG3", ACT8945A, DCDC3, VSET1, "vp3"),
+ ACT89xx_REG("LDO_REG1", ACT8945A, LDO1, VSET, "inl45"),
+ ACT89xx_REG("LDO_REG2", ACT8945A, LDO2, VSET, "inl45"),
+ ACT89xx_REG("LDO_REG3", ACT8945A, LDO3, VSET, "inl67"),
+ ACT89xx_REG("LDO_REG4", ACT8945A, LDO4, VSET, "inl67"),
+};
+
+static const struct regulator_desc act8945a_alt_regulators[] = {
+ ACT89xx_REG("DCDC_REG1", ACT8945A, DCDC1, VSET2, "vp1"),
+ ACT89xx_REG("DCDC_REG2", ACT8945A, DCDC2, VSET2, "vp2"),
+ ACT89xx_REG("DCDC_REG3", ACT8945A, DCDC3, VSET2, "vp3"),
+ ACT89xx_REG("LDO_REG1", ACT8945A, LDO1, VSET, "inl45"),
+ ACT89xx_REG("LDO_REG2", ACT8945A, LDO2, VSET, "inl45"),
+ ACT89xx_REG("LDO_REG3", ACT8945A, LDO3, VSET, "inl67"),
+ ACT89xx_REG("LDO_REG4", ACT8945A, LDO4, VSET, "inl67"),
+};
+
+static int act8945a_pmic_probe(struct platform_device *pdev)
+{
+ struct regulator_config config = { };
+ const struct regulator_desc *regulators;
+ struct regulator_dev *rdev;
+ int i, num_regulators;
+ bool voltage_select;
+
+ voltage_select = of_property_read_bool(pdev->dev.parent->of_node,
+ "active-semi,vsel-high");
+
+ if (voltage_select) {
+ regulators = act8945a_alt_regulators;
+ num_regulators = ARRAY_SIZE(act8945a_alt_regulators);
+ } else {
+ regulators = act8945a_regulators;
+ num_regulators = ARRAY_SIZE(act8945a_regulators);
+ }
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = pdev->dev.parent->of_node;
+ for (i = 0; i < num_regulators; i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "failed to register %s regulator\n",
+ regulators[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver act8945a_pmic_driver = {
+ .driver = {
+ .name = "act8945a-regulator",
+ },
+ .probe = act8945a_pmic_probe,
+};
+module_platform_driver(act8945a_pmic_driver);
+
+MODULE_DESCRIPTION("Active-semi ACT8945A voltage regulator driver");
+MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c
new file mode 100644
index 000000000..11c1f880b
--- /dev/null
+++ b/drivers/regulator/ad5398.c
@@ -0,0 +1,279 @@
+/*
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#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 != 2) {
+ dev_err(&client->dev, "I2C write error\n");
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+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 const 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 = dev_get_platdata(&client->dev);
+ struct regulator_config config = { };
+ struct ad5398_chip_info *chip;
+ const struct ad5398_current_data_format *df =
+ (struct ad5398_current_data_format *)id->driver_data;
+
+ 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 = devm_regulator_register(&client->dev, &ad5398_reg,
+ &config);
+ if (IS_ERR(chip->rdev)) {
+ dev_err(&client->dev, "failed to register %s %s\n",
+ id->name, ad5398_reg.name);
+ return PTR_ERR(chip->rdev);
+ }
+
+ i2c_set_clientdata(client, chip);
+ dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name);
+ return 0;
+}
+
+static struct i2c_driver ad5398_driver = {
+ .probe = ad5398_probe,
+ .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");
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
new file mode 100644
index 000000000..d9d8155ed
--- /dev/null
+++ b/drivers/regulator/anatop-regulator.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+
+#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 */
+
+#define LDO_POWER_GATE 0x00
+#define LDO_FET_FULL_ON 0x1f
+
+struct anatop_regulator {
+ 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;
+ bool bypass;
+ int sel;
+};
+
+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_enable(struct regulator_dev *reg)
+{
+ struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
+ int sel;
+
+ sel = anatop_reg->bypass ? LDO_FET_FULL_ON : anatop_reg->sel;
+ return regulator_set_voltage_sel_regmap(reg, sel);
+}
+
+static int anatop_regmap_disable(struct regulator_dev *reg)
+{
+ return regulator_set_voltage_sel_regmap(reg, LDO_POWER_GATE);
+}
+
+static int anatop_regmap_is_enabled(struct regulator_dev *reg)
+{
+ return regulator_get_voltage_sel_regmap(reg) != LDO_POWER_GATE;
+}
+
+static int anatop_regmap_core_set_voltage_sel(struct regulator_dev *reg,
+ unsigned selector)
+{
+ struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
+ int ret;
+
+ if (anatop_reg->bypass || !anatop_regmap_is_enabled(reg)) {
+ anatop_reg->sel = selector;
+ return 0;
+ }
+
+ ret = regulator_set_voltage_sel_regmap(reg, selector);
+ if (!ret)
+ anatop_reg->sel = selector;
+ return ret;
+}
+
+static int anatop_regmap_core_get_voltage_sel(struct regulator_dev *reg)
+{
+ struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
+
+ if (anatop_reg->bypass || !anatop_regmap_is_enabled(reg))
+ return anatop_reg->sel;
+
+ return regulator_get_voltage_sel_regmap(reg);
+}
+
+static int anatop_regmap_get_bypass(struct regulator_dev *reg, bool *enable)
+{
+ struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
+ int sel;
+
+ sel = regulator_get_voltage_sel_regmap(reg);
+ if (sel == LDO_FET_FULL_ON)
+ WARN_ON(!anatop_reg->bypass);
+ else if (sel != LDO_POWER_GATE)
+ WARN_ON(anatop_reg->bypass);
+
+ *enable = anatop_reg->bypass;
+ return 0;
+}
+
+static int anatop_regmap_set_bypass(struct regulator_dev *reg, bool enable)
+{
+ struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
+ int sel;
+
+ if (enable == anatop_reg->bypass)
+ return 0;
+
+ sel = enable ? LDO_FET_FULL_ON : anatop_reg->sel;
+ anatop_reg->bypass = enable;
+
+ return regulator_set_voltage_sel_regmap(reg, sel);
+}
+
+static struct regulator_ops anatop_rops = {
+ .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,
+};
+
+static struct regulator_ops anatop_core_rops = {
+ .enable = anatop_regmap_enable,
+ .disable = anatop_regmap_disable,
+ .is_enabled = anatop_regmap_is_enabled,
+ .set_voltage_sel = anatop_regmap_core_set_voltage_sel,
+ .set_voltage_time_sel = anatop_regmap_set_voltage_time_sel,
+ .get_voltage_sel = anatop_regmap_core_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_bypass = anatop_regmap_get_bypass,
+ .set_bypass = anatop_regmap_set_bypass,
+};
+
+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;
+ u32 val;
+
+ sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
+ if (!sreg)
+ return -ENOMEM;
+
+ rdesc = &sreg->rdesc;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->owner = THIS_MODULE;
+
+ of_property_read_string(np, "regulator-name", &rdesc->name);
+ if (!rdesc->name) {
+ dev_err(dev, "failed to get a regulator-name\n");
+ return -EINVAL;
+ }
+
+ initdata = of_get_regulator_init_data(dev, np, rdesc);
+ if (!initdata)
+ return -ENOMEM;
+
+ initdata->supply_regulator = "vin";
+ sreg->initdata = initdata;
+
+ 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");
+ return ret;
+ }
+ 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");
+ return ret;
+ }
+ 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");
+ return ret;
+ }
+ 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");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "anatop-min-voltage",
+ &sreg->min_voltage);
+ if (ret) {
+ dev_err(dev, "no anatop-min-voltage property set\n");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "anatop-max-voltage",
+ &sreg->max_voltage);
+ if (ret) {
+ dev_err(dev, "no anatop-max-voltage property set\n");
+ return ret;
+ }
+
+ /* 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;
+ rdesc->min_dropout_uV = 125000;
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = sreg;
+ config.of_node = pdev->dev.of_node;
+ config.regmap = sreg->anatop;
+
+ /* Only core regulators have the ramp up delay configuration. */
+ if (sreg->control_reg && sreg->delay_bit_width) {
+ rdesc->ops = &anatop_core_rops;
+
+ ret = regmap_read(config.regmap, rdesc->vsel_reg, &val);
+ if (ret) {
+ dev_err(dev, "failed to read initial state\n");
+ return ret;
+ }
+
+ sreg->sel = (val & rdesc->vsel_mask) >> sreg->vol_bit_shift;
+ if (sreg->sel == LDO_FET_FULL_ON) {
+ sreg->sel = 0;
+ sreg->bypass = true;
+ }
+
+ /*
+ * In case vddpu was disabled by the bootloader, we need to set
+ * a sane default until imx6-cpufreq was probed and changes the
+ * voltage to the correct value. In this case we set 1.25V.
+ */
+ if (!sreg->sel && !strcmp(rdesc->name, "vddpu"))
+ sreg->sel = 22;
+
+ /* set the default voltage of the pcie phy to be 1.100v */
+ if (!sreg->sel && !strcmp(rdesc->name, "vddpcie"))
+ sreg->sel = 0x10;
+
+ if (!sreg->bypass && !sreg->sel) {
+ dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
+ return -EINVAL;
+ }
+ } else {
+ u32 enable_bit;
+
+ rdesc->ops = &anatop_rops;
+
+ if (!of_property_read_u32(np, "anatop-enable-bit",
+ &enable_bit)) {
+ anatop_rops.enable = regulator_enable_regmap;
+ anatop_rops.disable = regulator_disable_regmap;
+ anatop_rops.is_enabled = regulator_is_enabled_regmap;
+
+ rdesc->enable_reg = sreg->control_reg;
+ rdesc->enable_mask = BIT(enable_bit);
+ }
+ }
+
+ /* register regulator */
+ rdev = devm_regulator_register(dev, rdesc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n",
+ rdesc->name);
+ return PTR_ERR(rdev);
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static const struct of_device_id of_anatop_regulator_match_tbl[] = {
+ { .compatible = "fsl,anatop-regulator", },
+ { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, of_anatop_regulator_match_tbl);
+
+static struct platform_driver anatop_regulator_driver = {
+ .driver = {
+ .name = "anatop_regulator",
+ .of_match_table = of_anatop_regulator_match_tbl,
+ },
+ .probe = anatop_regulator_probe,
+};
+
+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 <Nancy.Chen@freescale.com>");
+MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>");
+MODULE_DESCRIPTION("ANATOP Regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:anatop_regulator");
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
new file mode 100644
index 000000000..e976d073f
--- /dev/null
+++ b/drivers/regulator/arizona-ldo1.c
@@ -0,0 +1,371 @@
+/*
+ * arizona-ldo1.c -- LDO1 supply for Arizona devices
+ *
+ * Copyright 2012 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+#include <linux/regulator/arizona-ldo1.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_ldo1 {
+ struct regulator_dev *regulator;
+ struct regmap *regmap;
+
+ struct regulator_consumer_supply supply;
+ struct regulator_init_data init_data;
+
+ struct gpio_desc *ena_gpiod;
+};
+
+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->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;
+
+ 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->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 const 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,
+ .ramp_delay = 24000,
+
+ .owner = THIS_MODULE,
+};
+
+static const 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,
+};
+
+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,
+ .min_uV = 900000,
+ .uV_step = 25000,
+ .n_voltages = 13,
+ .enable_time = 500,
+ .ramp_delay = 24000,
+
+ .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 const struct regulator_init_data arizona_ldo1_wm5110 = {
+ .constraints = {
+ .min_uV = 1175000,
+ .max_uV = 1200000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+};
+
+static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,
+ struct regulator_config *config,
+ const struct regulator_desc *desc,
+ bool *external_dcvdd)
+{
+ struct arizona_ldo1 *ldo1 = config->driver_data;
+ struct device_node *np = config->dev->of_node;
+ struct device_node *init_node, *dcvdd_node;
+ struct regulator_init_data *init_data;
+
+ init_node = of_get_child_by_name(np, "ldo1");
+ dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0);
+
+ if (init_node) {
+ config->of_node = init_node;
+
+ init_data = of_get_regulator_init_data(config->dev, init_node,
+ desc);
+ if (init_data) {
+ init_data->consumer_supplies = &ldo1->supply;
+ init_data->num_consumer_supplies = 1;
+
+ if (dcvdd_node && dcvdd_node != init_node)
+ *external_dcvdd = true;
+
+ pdata->init_data = init_data;
+ }
+ } else if (dcvdd_node) {
+ *external_dcvdd = true;
+ }
+
+ of_node_put(dcvdd_node);
+
+ return 0;
+}
+
+static int arizona_ldo1_common_init(struct platform_device *pdev,
+ struct arizona_ldo1 *ldo1,
+ const struct regulator_desc *desc,
+ struct arizona_ldo1_pdata *pdata,
+ bool *external_dcvdd)
+{
+ struct device *parent_dev = pdev->dev.parent;
+ struct regulator_config config = { };
+ int ret;
+
+ *external_dcvdd = false;
+
+ ldo1->supply.supply = "DCVDD";
+ ldo1->init_data.consumer_supplies = &ldo1->supply;
+ ldo1->supply.dev_name = dev_name(parent_dev);
+
+ config.dev = parent_dev;
+ config.driver_data = ldo1;
+ config.regmap = ldo1->regmap;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(parent_dev)) {
+ ret = arizona_ldo1_of_get_pdata(pdata,
+ &config, desc,
+ external_dcvdd);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ /* We assume that high output = regulator off
+ * Don't use devm, since we need to get against the parent device
+ * so clean up would happen at the wrong time
+ */
+ config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(config.ena_gpiod))
+ return PTR_ERR(config.ena_gpiod);
+
+ ldo1->ena_gpiod = config.ena_gpiod;
+
+ if (pdata->init_data)
+ config.init_data = pdata->init_data;
+ else
+ config.init_data = &ldo1->init_data;
+
+ /*
+ * LDO1 can only be used to supply DCVDD so if it has no
+ * consumers then DCVDD is supplied externally.
+ */
+ if (config.init_data->num_consumer_supplies == 0)
+ *external_dcvdd = true;
+
+ ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
+
+ of_node_put(config.of_node);
+
+ if (IS_ERR(ldo1->regulator)) {
+ if (config.ena_gpiod)
+ gpiod_put(config.ena_gpiod);
+
+ ret = PTR_ERR(ldo1->regulator);
+ dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, ldo1);
+
+ return 0;
+}
+
+static int arizona_ldo1_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct arizona_ldo1 *ldo1;
+ const struct regulator_desc *desc;
+ bool external_dcvdd;
+ int ret;
+
+ ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
+ if (!ldo1)
+ return -ENOMEM;
+
+ ldo1->regmap = arizona->regmap;
+
+ /*
+ * 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:
+ case WM8997:
+ case WM8998:
+ case WM1814:
+ desc = &arizona_ldo1_hc;
+ ldo1->init_data = arizona_ldo1_dvfs;
+ break;
+ case WM5110:
+ case WM8280:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_wm5110;
+ break;
+ default:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_default;
+ break;
+ }
+
+ ret = arizona_ldo1_common_init(pdev, ldo1, desc,
+ &arizona->pdata.ldo1,
+ &external_dcvdd);
+ if (ret == 0)
+ arizona->external_dcvdd = external_dcvdd;
+
+ return ret;
+}
+
+static int arizona_ldo1_remove(struct platform_device *pdev)
+{
+ struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);
+
+ if (ldo1->ena_gpiod)
+ gpiod_put(ldo1->ena_gpiod);
+
+ return 0;
+}
+
+static struct platform_driver arizona_ldo1_driver = {
+ .probe = arizona_ldo1_probe,
+ .remove = arizona_ldo1_remove,
+ .driver = {
+ .name = "arizona-ldo1",
+ },
+};
+
+module_platform_driver(arizona_ldo1_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+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 000000000..120de94ca
--- /dev/null
+++ b/drivers/regulator/arizona-micsupp.c
@@ -0,0 +1,337 @@
+/*
+ * arizona-micsupp.c -- Microphone supply for Arizona devices
+ *
+ * Copyright 2012 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include <linux/regulator/arizona-micsupp.h>
+
+struct arizona_micsupp {
+ struct regulator_dev *regulator;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context **dapm;
+ unsigned int enable_reg;
+ struct device *dev;
+
+ struct regulator_consumer_supply supply;
+ struct regulator_init_data init_data;
+
+ struct work_struct check_cp_work;
+};
+
+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->dapm;
+ struct snd_soc_component *component;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(micsupp->regmap, micsupp->enable_reg, &val);
+ if (ret != 0) {
+ dev_err(micsupp->dev,
+ "Failed to read CP state: %d\n", ret);
+ return;
+ }
+
+ if (dapm) {
+ component = snd_soc_dapm_to_component(dapm);
+
+ if ((val & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
+ ARIZONA_CPMIC_ENA)
+ snd_soc_component_force_enable_pin(component,
+ "MICSUPP");
+ else
+ snd_soc_component_disable_pin(component, "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 const struct regulator_ops arizona_micsupp_ops = {
+ .enable = arizona_micsupp_enable,
+ .disable = arizona_micsupp_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+
+ .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_linear_range arizona_micsupp_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1700000, 0, 0x1e, 50000),
+ REGULATOR_LINEAR_RANGE(3300000, 0x1f, 0x1f, 0),
+};
+
+static const struct regulator_desc arizona_micsupp = {
+ .name = "MICVDD",
+ .supply_name = "CPVDD",
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 32,
+ .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,
+
+ .linear_ranges = arizona_micsupp_ranges,
+ .n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ranges),
+
+ .enable_time = 3000,
+
+ .owner = THIS_MODULE,
+};
+
+static const struct regulator_linear_range arizona_micsupp_ext_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 0x14, 25000),
+ REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
+};
+
+static const struct regulator_desc arizona_micsupp_ext = {
+ .name = "MICVDD",
+ .supply_name = "CPVDD",
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 40,
+ .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,
+
+ .linear_ranges = arizona_micsupp_ext_ranges,
+ .n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges),
+
+ .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 const struct regulator_init_data arizona_micsupp_ext_default = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_BYPASS,
+ .min_uV = 900000,
+ .max_uV = 3300000,
+ },
+
+ .num_consumer_supplies = 1,
+};
+
+static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata,
+ struct regulator_config *config,
+ const struct regulator_desc *desc)
+{
+ struct arizona_micsupp *micsupp = config->driver_data;
+ struct device_node *np;
+ struct regulator_init_data *init_data;
+
+ np = of_get_child_by_name(config->dev->of_node, "micvdd");
+
+ if (np) {
+ config->of_node = np;
+
+ init_data = of_get_regulator_init_data(config->dev, np, desc);
+
+ if (init_data) {
+ init_data->consumer_supplies = &micsupp->supply;
+ init_data->num_consumer_supplies = 1;
+
+ pdata->init_data = init_data;
+ }
+ }
+
+ return 0;
+}
+
+static int arizona_micsupp_common_init(struct platform_device *pdev,
+ struct arizona_micsupp *micsupp,
+ const struct regulator_desc *desc,
+ struct arizona_micsupp_pdata *pdata)
+{
+ struct regulator_config config = { };
+ int ret;
+
+ INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp);
+
+ micsupp->init_data.consumer_supplies = &micsupp->supply;
+ micsupp->supply.supply = "MICVDD";
+ micsupp->supply.dev_name = dev_name(micsupp->dev);
+ micsupp->enable_reg = desc->enable_reg;
+
+ config.dev = micsupp->dev;
+ config.driver_data = micsupp;
+ config.regmap = micsupp->regmap;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ if (!dev_get_platdata(micsupp->dev)) {
+ ret = arizona_micsupp_of_get_pdata(pdata, &config,
+ desc);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (pdata->init_data)
+ config.init_data = pdata->init_data;
+ else
+ config.init_data = &micsupp->init_data;
+
+ /* Default to regulated mode */
+ regmap_update_bits(micsupp->regmap, micsupp->enable_reg,
+ ARIZONA_CPMIC_BYPASS, 0);
+
+ micsupp->regulator = devm_regulator_register(&pdev->dev,
+ desc,
+ &config);
+
+ of_node_put(config.of_node);
+
+ if (IS_ERR(micsupp->regulator)) {
+ ret = PTR_ERR(micsupp->regulator);
+ dev_err(micsupp->dev, "Failed to register mic supply: %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, micsupp);
+
+ return 0;
+}
+
+static int arizona_micsupp_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *desc;
+ struct arizona_micsupp *micsupp;
+
+ micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
+ if (!micsupp)
+ return -ENOMEM;
+
+ micsupp->regmap = arizona->regmap;
+ micsupp->dapm = &arizona->dapm;
+ micsupp->dev = arizona->dev;
+
+ /*
+ * 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 WM5110:
+ case WM8280:
+ desc = &arizona_micsupp_ext;
+ micsupp->init_data = arizona_micsupp_ext_default;
+ break;
+ default:
+ desc = &arizona_micsupp;
+ micsupp->init_data = arizona_micsupp_default;
+ break;
+ }
+
+ return arizona_micsupp_common_init(pdev, micsupp, desc,
+ &arizona->pdata.micvdd);
+}
+
+static struct platform_driver arizona_micsupp_driver = {
+ .probe = arizona_micsupp_probe,
+ .driver = {
+ .name = "arizona-micsupp",
+ },
+};
+
+module_platform_driver(arizona_micsupp_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+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 000000000..565a71343
--- /dev/null
+++ b/drivers/regulator/as3711-regulator.c
@@ -0,0 +1,291 @@
+/*
+ * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
+ *
+ * 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 <linux/err.h>
+#include <linux/init.h>
+#include <linux/mfd/as3711.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+struct as3711_regulator_info {
+ struct regulator_desc desc;
+};
+
+struct as3711_regulator {
+ struct as3711_regulator_info *reg_info;
+};
+
+/*
+ * 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 const 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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_mode = as3711_get_mode_sd,
+ .set_mode = as3711_set_mode_sd,
+};
+
+static const 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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static const 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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static const struct regulator_linear_range as3711_sd_ranges[] = {
+ REGULATOR_LINEAR_RANGE(612500, 0x1, 0x40, 12500),
+ REGULATOR_LINEAR_RANGE(1425000, 0x41, 0x70, 25000),
+ REGULATOR_LINEAR_RANGE(2650000, 0x71, 0x7f, 50000),
+};
+
+static const struct regulator_linear_range as3711_aldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1200000, 0, 0xf, 50000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1f, 100000),
+};
+
+static const struct regulator_linear_range as3711_dldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 0x10, 50000),
+ REGULATOR_LINEAR_RANGE(1750000, 0x20, 0x3f, 50000),
+};
+
+#define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _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, \
+ .enable_reg = AS3711_ ## _en_reg, \
+ .enable_mask = BIT(_en_bit), \
+ .linear_ranges = as3711_ ## _sfx ## _ranges, \
+ .n_linear_ranges = ARRAY_SIZE(as3711_ ## _sfx ## _ranges), \
+ }, \
+}
+
+static struct as3711_regulator_info as3711_reg_info[] = {
+ AS3711_REG(SD_1, SD_CONTROL, 0, 0x7f, sd),
+ AS3711_REG(SD_2, SD_CONTROL, 1, 0x7f, sd),
+ AS3711_REG(SD_3, SD_CONTROL, 2, 0x7f, sd),
+ AS3711_REG(SD_4, SD_CONTROL, 3, 0x7f, sd),
+ AS3711_REG(LDO_1, LDO_1_VOLTAGE, 7, 0x1f, aldo),
+ AS3711_REG(LDO_2, LDO_2_VOLTAGE, 7, 0x1f, aldo),
+ AS3711_REG(LDO_3, LDO_3_VOLTAGE, 7, 0x3f, dldo),
+ AS3711_REG(LDO_4, LDO_4_VOLTAGE, 7, 0x3f, dldo),
+ AS3711_REG(LDO_5, LDO_5_VOLTAGE, 7, 0x3f, dldo),
+ AS3711_REG(LDO_6, LDO_6_VOLTAGE, 7, 0x3f, dldo),
+ AS3711_REG(LDO_7, LDO_7_VOLTAGE, 7, 0x3f, dldo),
+ AS3711_REG(LDO_8, LDO_8_VOLTAGE, 7, 0x3f, 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_get_child_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_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_kcalloc(&pdev->dev,
+ AS3711_REGULATOR_NUM,
+ sizeof(struct as3711_regulator),
+ GFP_KERNEL);
+ if (!regs)
+ return -ENOMEM;
+
+ for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
+ reg = &regs[id];
+ reg->reg_info = ri;
+
+ config.init_data = pdata->init_data[id];
+ config.driver_data = reg;
+ config.regmap = as3711->regmap;
+ config.of_node = of_node[id];
+
+ rdev = devm_regulator_register(&pdev->dev, &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, regs);
+ return 0;
+}
+
+static struct platform_driver as3711_regulator_driver = {
+ .driver = {
+ .name = "as3711-regulator",
+ },
+ .probe = as3711_regulator_probe,
+};
+
+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 <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("AS3711 regulator driver");
+MODULE_ALIAS("platform:as3711-regulator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c
new file mode 100644
index 000000000..66337e127
--- /dev/null
+++ b/drivers/regulator/as3722-regulator.c
@@ -0,0 +1,973 @@
+/*
+ * Voltage regulator support for AMS AS3722 PMIC
+ *
+ * Copyright (C) 2013 ams
+ *
+ * Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ * Author: Laxman Dewangan <ldewangan@nvidia.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.
+ *
+ * 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 <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/as3722.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/* Regulator IDs */
+enum as3722_regulators_id {
+ AS3722_REGULATOR_ID_SD0,
+ AS3722_REGULATOR_ID_SD1,
+ AS3722_REGULATOR_ID_SD2,
+ AS3722_REGULATOR_ID_SD3,
+ AS3722_REGULATOR_ID_SD4,
+ AS3722_REGULATOR_ID_SD5,
+ AS3722_REGULATOR_ID_SD6,
+ AS3722_REGULATOR_ID_LDO0,
+ AS3722_REGULATOR_ID_LDO1,
+ AS3722_REGULATOR_ID_LDO2,
+ AS3722_REGULATOR_ID_LDO3,
+ AS3722_REGULATOR_ID_LDO4,
+ AS3722_REGULATOR_ID_LDO5,
+ AS3722_REGULATOR_ID_LDO6,
+ AS3722_REGULATOR_ID_LDO7,
+ AS3722_REGULATOR_ID_LDO9,
+ AS3722_REGULATOR_ID_LDO10,
+ AS3722_REGULATOR_ID_LDO11,
+ AS3722_REGULATOR_ID_MAX,
+};
+
+struct as3722_register_mapping {
+ u8 regulator_id;
+ const char *name;
+ const char *sname;
+ u8 vsel_reg;
+ u8 vsel_mask;
+ int n_voltages;
+ u32 enable_reg;
+ u8 enable_mask;
+ u32 control_reg;
+ u8 mode_mask;
+ u32 sleep_ctrl_reg;
+ u8 sleep_ctrl_mask;
+};
+
+struct as3722_regulator_config_data {
+ struct regulator_init_data *reg_init;
+ bool enable_tracking;
+ int ext_control;
+};
+
+struct as3722_regulators {
+ struct device *dev;
+ struct as3722 *as3722;
+ struct regulator_dev *rdevs[AS3722_REGULATOR_ID_MAX];
+ struct regulator_desc desc[AS3722_REGULATOR_ID_MAX];
+ struct as3722_regulator_config_data
+ reg_config_data[AS3722_REGULATOR_ID_MAX];
+};
+
+static const struct as3722_register_mapping as3722_reg_lookup[] = {
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD0,
+ .name = "as3722-sd0",
+ .vsel_reg = AS3722_SD0_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(0),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG,
+ .sleep_ctrl_mask = AS3722_SD0_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD0_CONTROL_REG,
+ .mode_mask = AS3722_SD0_MODE_FAST,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD1,
+ .name = "as3722-sd1",
+ .vsel_reg = AS3722_SD1_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(1),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG,
+ .sleep_ctrl_mask = AS3722_SD1_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD1_CONTROL_REG,
+ .mode_mask = AS3722_SD1_MODE_FAST,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD2,
+ .name = "as3722-sd2",
+ .sname = "vsup-sd2",
+ .vsel_reg = AS3722_SD2_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(2),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG,
+ .sleep_ctrl_mask = AS3722_SD2_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD23_CONTROL_REG,
+ .mode_mask = AS3722_SD2_MODE_FAST,
+ .n_voltages = AS3722_SD2_VSEL_MAX + 1,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD3,
+ .name = "as3722-sd3",
+ .sname = "vsup-sd3",
+ .vsel_reg = AS3722_SD3_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(3),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL1_REG,
+ .sleep_ctrl_mask = AS3722_SD3_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD23_CONTROL_REG,
+ .mode_mask = AS3722_SD3_MODE_FAST,
+ .n_voltages = AS3722_SD2_VSEL_MAX + 1,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD4,
+ .name = "as3722-sd4",
+ .sname = "vsup-sd4",
+ .vsel_reg = AS3722_SD4_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(4),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL2_REG,
+ .sleep_ctrl_mask = AS3722_SD4_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD4_CONTROL_REG,
+ .mode_mask = AS3722_SD4_MODE_FAST,
+ .n_voltages = AS3722_SD2_VSEL_MAX + 1,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD5,
+ .name = "as3722-sd5",
+ .sname = "vsup-sd5",
+ .vsel_reg = AS3722_SD5_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(5),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL2_REG,
+ .sleep_ctrl_mask = AS3722_SD5_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD5_CONTROL_REG,
+ .mode_mask = AS3722_SD5_MODE_FAST,
+ .n_voltages = AS3722_SD2_VSEL_MAX + 1,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_SD6,
+ .name = "as3722-sd6",
+ .vsel_reg = AS3722_SD6_VOLTAGE_REG,
+ .vsel_mask = AS3722_SD_VSEL_MASK,
+ .enable_reg = AS3722_SD_CONTROL_REG,
+ .enable_mask = AS3722_SDn_CTRL(6),
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL2_REG,
+ .sleep_ctrl_mask = AS3722_SD6_EXT_ENABLE_MASK,
+ .control_reg = AS3722_SD6_CONTROL_REG,
+ .mode_mask = AS3722_SD6_MODE_FAST,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO0,
+ .name = "as3722-ldo0",
+ .sname = "vin-ldo0",
+ .vsel_reg = AS3722_LDO0_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO0_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO0_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG,
+ .sleep_ctrl_mask = AS3722_LDO0_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO0_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO1,
+ .name = "as3722-ldo1",
+ .sname = "vin-ldo1-6",
+ .vsel_reg = AS3722_LDO1_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO1_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG,
+ .sleep_ctrl_mask = AS3722_LDO1_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO2,
+ .name = "as3722-ldo2",
+ .sname = "vin-ldo2-5-7",
+ .vsel_reg = AS3722_LDO2_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO2_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG,
+ .sleep_ctrl_mask = AS3722_LDO2_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO3,
+ .name = "as3722-ldo3",
+ .sname = "vin-ldo3-4",
+ .vsel_reg = AS3722_LDO3_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO3_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO3_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL3_REG,
+ .sleep_ctrl_mask = AS3722_LDO3_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO3_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO4,
+ .name = "as3722-ldo4",
+ .sname = "vin-ldo3-4",
+ .vsel_reg = AS3722_LDO4_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO4_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG,
+ .sleep_ctrl_mask = AS3722_LDO4_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO5,
+ .name = "as3722-ldo5",
+ .sname = "vin-ldo2-5-7",
+ .vsel_reg = AS3722_LDO5_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO5_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG,
+ .sleep_ctrl_mask = AS3722_LDO5_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO6,
+ .name = "as3722-ldo6",
+ .sname = "vin-ldo1-6",
+ .vsel_reg = AS3722_LDO6_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO6_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG,
+ .sleep_ctrl_mask = AS3722_LDO6_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO7,
+ .name = "as3722-ldo7",
+ .sname = "vin-ldo2-5-7",
+ .vsel_reg = AS3722_LDO7_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL0_REG,
+ .enable_mask = AS3722_LDO7_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL4_REG,
+ .sleep_ctrl_mask = AS3722_LDO7_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO9,
+ .name = "as3722-ldo9",
+ .sname = "vin-ldo9-10",
+ .vsel_reg = AS3722_LDO9_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL1_REG,
+ .enable_mask = AS3722_LDO9_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL5_REG,
+ .sleep_ctrl_mask = AS3722_LDO9_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO10,
+ .name = "as3722-ldo10",
+ .sname = "vin-ldo9-10",
+ .vsel_reg = AS3722_LDO10_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL1_REG,
+ .enable_mask = AS3722_LDO10_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL5_REG,
+ .sleep_ctrl_mask = AS3722_LDO10_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+ {
+ .regulator_id = AS3722_REGULATOR_ID_LDO11,
+ .name = "as3722-ldo11",
+ .sname = "vin-ldo11",
+ .vsel_reg = AS3722_LDO11_VOLTAGE_REG,
+ .vsel_mask = AS3722_LDO_VSEL_MASK,
+ .enable_reg = AS3722_LDOCONTROL1_REG,
+ .enable_mask = AS3722_LDO11_CTRL,
+ .sleep_ctrl_reg = AS3722_ENABLE_CTRL5_REG,
+ .sleep_ctrl_mask = AS3722_LDO11_EXT_ENABLE_MASK,
+ .n_voltages = AS3722_LDO_NUM_VOLT,
+ },
+};
+
+
+static const int as3722_ldo_current[] = { 150000, 300000 };
+static const int as3722_sd016_current[] = { 2500000, 3000000, 3500000 };
+
+static int as3722_current_to_index(int min_uA, int max_uA,
+ const int *curr_table, int n_currents)
+{
+ int i;
+
+ for (i = n_currents - 1; i >= 0; i--) {
+ if ((min_uA <= curr_table[i]) && (curr_table[i] <= max_uA))
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int as3722_ldo_get_current_limit(struct regulator_dev *rdev)
+{
+ struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev);
+ struct as3722 *as3722 = as3722_regs->as3722;
+ int id = rdev_get_id(rdev);
+ u32 val;
+ int ret;
+
+ ret = as3722_read(as3722, as3722_reg_lookup[id].vsel_reg, &val);
+ if (ret < 0) {
+ dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n",
+ as3722_reg_lookup[id].vsel_reg, ret);
+ return ret;
+ }
+ if (val & AS3722_LDO_ILIMIT_MASK)
+ return 300000;
+ return 150000;
+}
+
+static int as3722_ldo_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev);
+ struct as3722 *as3722 = as3722_regs->as3722;
+ int id = rdev_get_id(rdev);
+ int ret;
+ u32 reg = 0;
+
+ ret = as3722_current_to_index(min_uA, max_uA, as3722_ldo_current,
+ ARRAY_SIZE(as3722_ldo_current));
+ if (ret < 0) {
+ dev_err(as3722_regs->dev,
+ "Current range min:max = %d:%d does not support\n",
+ min_uA, max_uA);
+ return ret;
+ }
+ if (ret)
+ reg = AS3722_LDO_ILIMIT_BIT;
+ return as3722_update_bits(as3722, as3722_reg_lookup[id].vsel_reg,
+ AS3722_LDO_ILIMIT_MASK, reg);
+}
+
+static const struct regulator_ops as3722_ldo0_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_current_limit = as3722_ldo_get_current_limit,
+ .set_current_limit = as3722_ldo_set_current_limit,
+};
+
+static const struct regulator_ops as3722_ldo0_extcntrl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_current_limit = as3722_ldo_get_current_limit,
+ .set_current_limit = as3722_ldo_set_current_limit,
+};
+
+static int as3722_ldo3_set_tracking_mode(struct as3722_regulators *as3722_reg,
+ int id, u8 mode)
+{
+ struct as3722 *as3722 = as3722_reg->as3722;
+
+ switch (mode) {
+ case AS3722_LDO3_MODE_PMOS:
+ case AS3722_LDO3_MODE_PMOS_TRACKING:
+ case AS3722_LDO3_MODE_NMOS:
+ case AS3722_LDO3_MODE_SWITCH:
+ return as3722_update_bits(as3722,
+ as3722_reg_lookup[id].vsel_reg,
+ AS3722_LDO3_MODE_MASK, mode);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int as3722_ldo3_get_current_limit(struct regulator_dev *rdev)
+{
+ return 150000;
+}
+
+static const struct regulator_ops as3722_ldo3_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_current_limit = as3722_ldo3_get_current_limit,
+};
+
+static const struct regulator_ops as3722_ldo3_extcntrl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_current_limit = as3722_ldo3_get_current_limit,
+};
+
+static const struct regulator_ops as3722_ldo6_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .get_current_limit = as3722_ldo_get_current_limit,
+ .set_current_limit = as3722_ldo_set_current_limit,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
+};
+
+static const struct regulator_ops as3722_ldo6_extcntrl_ops = {
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .get_current_limit = as3722_ldo_get_current_limit,
+ .set_current_limit = as3722_ldo_set_current_limit,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
+};
+
+static const struct regulator_linear_range as3722_ldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x00, 0x00, 0),
+ REGULATOR_LINEAR_RANGE(825000, 0x01, 0x24, 25000),
+ REGULATOR_LINEAR_RANGE(1725000, 0x40, 0x7F, 25000),
+};
+
+static const struct regulator_ops as3722_ldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .get_current_limit = as3722_ldo_get_current_limit,
+ .set_current_limit = as3722_ldo_set_current_limit,
+};
+
+static const struct regulator_ops as3722_ldo_extcntrl_ops = {
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .get_current_limit = as3722_ldo_get_current_limit,
+ .set_current_limit = as3722_ldo_set_current_limit,
+};
+
+static unsigned int as3722_sd_get_mode(struct regulator_dev *rdev)
+{
+ struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev);
+ struct as3722 *as3722 = as3722_regs->as3722;
+ int id = rdev_get_id(rdev);
+ u32 val;
+ int ret;
+
+ if (!as3722_reg_lookup[id].control_reg)
+ return -ENOTSUPP;
+
+ ret = as3722_read(as3722, as3722_reg_lookup[id].control_reg, &val);
+ if (ret < 0) {
+ dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n",
+ as3722_reg_lookup[id].control_reg, ret);
+ return ret;
+ }
+
+ if (val & as3722_reg_lookup[id].mode_mask)
+ return REGULATOR_MODE_FAST;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int as3722_sd_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev);
+ struct as3722 *as3722 = as3722_regs->as3722;
+ u8 id = rdev_get_id(rdev);
+ u8 val = 0;
+ int ret;
+
+ if (!as3722_reg_lookup[id].control_reg)
+ return -ERANGE;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = as3722_reg_lookup[id].mode_mask;
+ case REGULATOR_MODE_NORMAL: /* fall down */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = as3722_update_bits(as3722, as3722_reg_lookup[id].control_reg,
+ as3722_reg_lookup[id].mode_mask, val);
+ if (ret < 0) {
+ dev_err(as3722_regs->dev, "Reg 0x%02x update failed: %d\n",
+ as3722_reg_lookup[id].control_reg, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int as3722_sd016_get_current_limit(struct regulator_dev *rdev)
+{
+ struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev);
+ struct as3722 *as3722 = as3722_regs->as3722;
+ int id = rdev_get_id(rdev);
+ u32 val, reg;
+ int mask;
+ int ret;
+
+ switch (id) {
+ case AS3722_REGULATOR_ID_SD0:
+ reg = AS3722_OVCURRENT_REG;
+ mask = AS3722_OVCURRENT_SD0_TRIP_MASK;
+ break;
+ case AS3722_REGULATOR_ID_SD1:
+ reg = AS3722_OVCURRENT_REG;
+ mask = AS3722_OVCURRENT_SD1_TRIP_MASK;
+ break;
+ case AS3722_REGULATOR_ID_SD6:
+ reg = AS3722_OVCURRENT_DEB_REG;
+ mask = AS3722_OVCURRENT_SD6_TRIP_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = as3722_read(as3722, reg, &val);
+ if (ret < 0) {
+ dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n",
+ reg, ret);
+ return ret;
+ }
+ val &= mask;
+ val >>= ffs(mask) - 1;
+ if (val == 3)
+ return -EINVAL;
+ return as3722_sd016_current[val];
+}
+
+static int as3722_sd016_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct as3722_regulators *as3722_regs = rdev_get_drvdata(rdev);
+ struct as3722 *as3722 = as3722_regs->as3722;
+ int id = rdev_get_id(rdev);
+ int ret;
+ int val;
+ int mask;
+ u32 reg;
+
+ ret = as3722_current_to_index(min_uA, max_uA, as3722_sd016_current,
+ ARRAY_SIZE(as3722_sd016_current));
+ if (ret < 0) {
+ dev_err(as3722_regs->dev,
+ "Current range min:max = %d:%d does not support\n",
+ min_uA, max_uA);
+ return ret;
+ }
+
+ switch (id) {
+ case AS3722_REGULATOR_ID_SD0:
+ reg = AS3722_OVCURRENT_REG;
+ mask = AS3722_OVCURRENT_SD0_TRIP_MASK;
+ break;
+ case AS3722_REGULATOR_ID_SD1:
+ reg = AS3722_OVCURRENT_REG;
+ mask = AS3722_OVCURRENT_SD1_TRIP_MASK;
+ break;
+ case AS3722_REGULATOR_ID_SD6:
+ reg = AS3722_OVCURRENT_DEB_REG;
+ mask = AS3722_OVCURRENT_SD6_TRIP_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret <<= ffs(mask) - 1;
+ val = ret & mask;
+ return as3722_update_bits(as3722, reg, mask, val);
+}
+
+static bool as3722_sd0_is_low_voltage(struct as3722_regulators *as3722_regs)
+{
+ int err;
+ unsigned val;
+
+ err = as3722_read(as3722_regs->as3722, AS3722_FUSE7_REG, &val);
+ if (err < 0) {
+ dev_err(as3722_regs->dev, "Reg 0x%02x read failed: %d\n",
+ AS3722_FUSE7_REG, err);
+ return false;
+ }
+ if (val & AS3722_FUSE7_SD0_LOW_VOLTAGE)
+ return true;
+ return false;
+}
+
+static const struct regulator_linear_range as3722_sd2345_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x00, 0x00, 0),
+ REGULATOR_LINEAR_RANGE(612500, 0x01, 0x40, 12500),
+ REGULATOR_LINEAR_RANGE(1425000, 0x41, 0x70, 25000),
+ REGULATOR_LINEAR_RANGE(2650000, 0x71, 0x7F, 50000),
+};
+
+static const struct regulator_ops as3722_sd016_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_current_limit = as3722_sd016_get_current_limit,
+ .set_current_limit = as3722_sd016_set_current_limit,
+ .get_mode = as3722_sd_get_mode,
+ .set_mode = as3722_sd_set_mode,
+};
+
+static const struct regulator_ops as3722_sd016_extcntrl_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_current_limit = as3722_sd016_get_current_limit,
+ .set_current_limit = as3722_sd016_set_current_limit,
+ .get_mode = as3722_sd_get_mode,
+ .set_mode = as3722_sd_set_mode,
+};
+
+static const struct regulator_ops as3722_sd2345_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .get_mode = as3722_sd_get_mode,
+ .set_mode = as3722_sd_set_mode,
+};
+
+static const struct regulator_ops as3722_sd2345_extcntrl_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .get_mode = as3722_sd_get_mode,
+ .set_mode = as3722_sd_set_mode,
+};
+
+static int as3722_extreg_init(struct as3722_regulators *as3722_regs, int id,
+ int ext_pwr_ctrl)
+{
+ int ret;
+ unsigned int val;
+
+ if ((ext_pwr_ctrl < AS3722_EXT_CONTROL_ENABLE1) ||
+ (ext_pwr_ctrl > AS3722_EXT_CONTROL_ENABLE3))
+ return -EINVAL;
+
+ val = ext_pwr_ctrl << (ffs(as3722_reg_lookup[id].sleep_ctrl_mask) - 1);
+ ret = as3722_update_bits(as3722_regs->as3722,
+ as3722_reg_lookup[id].sleep_ctrl_reg,
+ as3722_reg_lookup[id].sleep_ctrl_mask, val);
+ if (ret < 0)
+ dev_err(as3722_regs->dev, "Reg 0x%02x update failed: %d\n",
+ as3722_reg_lookup[id].sleep_ctrl_reg, ret);
+ return ret;
+}
+
+static struct of_regulator_match as3722_regulator_matches[] = {
+ { .name = "sd0", },
+ { .name = "sd1", },
+ { .name = "sd2", },
+ { .name = "sd3", },
+ { .name = "sd4", },
+ { .name = "sd5", },
+ { .name = "sd6", },
+ { .name = "ldo0", },
+ { .name = "ldo1", },
+ { .name = "ldo2", },
+ { .name = "ldo3", },
+ { .name = "ldo4", },
+ { .name = "ldo5", },
+ { .name = "ldo6", },
+ { .name = "ldo7", },
+ { .name = "ldo9", },
+ { .name = "ldo10", },
+ { .name = "ldo11", },
+};
+
+static int as3722_get_regulator_dt_data(struct platform_device *pdev,
+ struct as3722_regulators *as3722_regs)
+{
+ struct device_node *np;
+ struct as3722_regulator_config_data *reg_config;
+ u32 prop;
+ int id;
+ int ret;
+
+ np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
+ if (!np) {
+ dev_err(&pdev->dev, "Device is not having regulators node\n");
+ return -ENODEV;
+ }
+ pdev->dev.of_node = np;
+
+ ret = of_regulator_match(&pdev->dev, np, as3722_regulator_matches,
+ ARRAY_SIZE(as3722_regulator_matches));
+ of_node_put(np);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Parsing of regulator node failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (id = 0; id < ARRAY_SIZE(as3722_regulator_matches); ++id) {
+ struct device_node *reg_node;
+
+ reg_config = &as3722_regs->reg_config_data[id];
+ reg_config->reg_init = as3722_regulator_matches[id].init_data;
+ reg_node = as3722_regulator_matches[id].of_node;
+
+ if (!reg_config->reg_init || !reg_node)
+ continue;
+
+ ret = of_property_read_u32(reg_node, "ams,ext-control", &prop);
+ if (!ret) {
+ if (prop < 3)
+ reg_config->ext_control = prop;
+ else
+ dev_warn(&pdev->dev,
+ "ext-control have invalid option: %u\n",
+ prop);
+ }
+ reg_config->enable_tracking =
+ of_property_read_bool(reg_node, "ams,enable-tracking");
+ }
+ return 0;
+}
+
+static int as3722_regulator_probe(struct platform_device *pdev)
+{
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ struct as3722_regulators *as3722_regs;
+ struct as3722_regulator_config_data *reg_config;
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ const struct regulator_ops *ops;
+ int id;
+ int ret;
+
+ as3722_regs = devm_kzalloc(&pdev->dev, sizeof(*as3722_regs),
+ GFP_KERNEL);
+ if (!as3722_regs)
+ return -ENOMEM;
+
+ as3722_regs->dev = &pdev->dev;
+ as3722_regs->as3722 = as3722;
+ platform_set_drvdata(pdev, as3722_regs);
+
+ ret = as3722_get_regulator_dt_data(pdev, as3722_regs);
+ if (ret < 0)
+ return ret;
+
+ config.dev = &pdev->dev;
+ config.driver_data = as3722_regs;
+ config.regmap = as3722->regmap;
+
+ for (id = 0; id < AS3722_REGULATOR_ID_MAX; id++) {
+ reg_config = &as3722_regs->reg_config_data[id];
+
+ as3722_regs->desc[id].name = as3722_reg_lookup[id].name;
+ as3722_regs->desc[id].supply_name = as3722_reg_lookup[id].sname;
+ as3722_regs->desc[id].id = as3722_reg_lookup[id].regulator_id;
+ as3722_regs->desc[id].n_voltages =
+ as3722_reg_lookup[id].n_voltages;
+ as3722_regs->desc[id].type = REGULATOR_VOLTAGE;
+ as3722_regs->desc[id].owner = THIS_MODULE;
+ as3722_regs->desc[id].enable_reg =
+ as3722_reg_lookup[id].enable_reg;
+ as3722_regs->desc[id].enable_mask =
+ as3722_reg_lookup[id].enable_mask;
+ as3722_regs->desc[id].vsel_reg = as3722_reg_lookup[id].vsel_reg;
+ as3722_regs->desc[id].vsel_mask =
+ as3722_reg_lookup[id].vsel_mask;
+ switch (id) {
+ case AS3722_REGULATOR_ID_LDO0:
+ if (reg_config->ext_control)
+ ops = &as3722_ldo0_extcntrl_ops;
+ else
+ ops = &as3722_ldo0_ops;
+ as3722_regs->desc[id].min_uV = 825000;
+ as3722_regs->desc[id].uV_step = 25000;
+ as3722_regs->desc[id].linear_min_sel = 1;
+ as3722_regs->desc[id].enable_time = 500;
+ break;
+ case AS3722_REGULATOR_ID_LDO3:
+ if (reg_config->ext_control)
+ ops = &as3722_ldo3_extcntrl_ops;
+ else
+ ops = &as3722_ldo3_ops;
+ as3722_regs->desc[id].min_uV = 620000;
+ as3722_regs->desc[id].uV_step = 20000;
+ as3722_regs->desc[id].linear_min_sel = 1;
+ as3722_regs->desc[id].enable_time = 500;
+ if (reg_config->enable_tracking) {
+ ret = as3722_ldo3_set_tracking_mode(as3722_regs,
+ id, AS3722_LDO3_MODE_PMOS_TRACKING);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "LDO3 tracking failed: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ break;
+ case AS3722_REGULATOR_ID_LDO6:
+ if (reg_config->ext_control)
+ ops = &as3722_ldo6_extcntrl_ops;
+ else
+ ops = &as3722_ldo6_ops;
+ as3722_regs->desc[id].enable_time = 500;
+ as3722_regs->desc[id].bypass_reg =
+ AS3722_LDO6_VOLTAGE_REG;
+ as3722_regs->desc[id].bypass_mask =
+ AS3722_LDO_VSEL_MASK;
+ as3722_regs->desc[id].bypass_val_on =
+ AS3722_LDO6_VSEL_BYPASS;
+ as3722_regs->desc[id].bypass_val_off =
+ AS3722_LDO6_VSEL_BYPASS;
+ as3722_regs->desc[id].linear_ranges = as3722_ldo_ranges;
+ as3722_regs->desc[id].n_linear_ranges =
+ ARRAY_SIZE(as3722_ldo_ranges);
+ break;
+ case AS3722_REGULATOR_ID_SD0:
+ case AS3722_REGULATOR_ID_SD1:
+ case AS3722_REGULATOR_ID_SD6:
+ if (reg_config->ext_control)
+ ops = &as3722_sd016_extcntrl_ops;
+ else
+ ops = &as3722_sd016_ops;
+ if (id == AS3722_REGULATOR_ID_SD0 &&
+ as3722_sd0_is_low_voltage(as3722_regs)) {
+ as3722_regs->desc[id].n_voltages =
+ AS3722_SD0_VSEL_LOW_VOL_MAX + 1;
+ as3722_regs->desc[id].min_uV = 410000;
+ } else {
+ as3722_regs->desc[id].n_voltages =
+ AS3722_SD0_VSEL_MAX + 1,
+ as3722_regs->desc[id].min_uV = 610000;
+ }
+ as3722_regs->desc[id].uV_step = 10000;
+ as3722_regs->desc[id].linear_min_sel = 1;
+ as3722_regs->desc[id].enable_time = 600;
+ break;
+ case AS3722_REGULATOR_ID_SD2:
+ case AS3722_REGULATOR_ID_SD3:
+ case AS3722_REGULATOR_ID_SD4:
+ case AS3722_REGULATOR_ID_SD5:
+ if (reg_config->ext_control)
+ ops = &as3722_sd2345_extcntrl_ops;
+ else
+ ops = &as3722_sd2345_ops;
+ as3722_regs->desc[id].linear_ranges =
+ as3722_sd2345_ranges;
+ as3722_regs->desc[id].n_linear_ranges =
+ ARRAY_SIZE(as3722_sd2345_ranges);
+ break;
+ default:
+ if (reg_config->ext_control)
+ ops = &as3722_ldo_extcntrl_ops;
+ else
+ ops = &as3722_ldo_ops;
+ as3722_regs->desc[id].enable_time = 500;
+ as3722_regs->desc[id].linear_ranges = as3722_ldo_ranges;
+ as3722_regs->desc[id].n_linear_ranges =
+ ARRAY_SIZE(as3722_ldo_ranges);
+ break;
+ }
+ as3722_regs->desc[id].ops = ops;
+ config.init_data = reg_config->reg_init;
+ config.of_node = as3722_regulator_matches[id].of_node;
+ rdev = devm_regulator_register(&pdev->dev,
+ &as3722_regs->desc[id], &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&pdev->dev, "regulator %d register failed %d\n",
+ id, ret);
+ return ret;
+ }
+
+ as3722_regs->rdevs[id] = rdev;
+ if (reg_config->ext_control) {
+ ret = regulator_enable_regmap(rdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Regulator %d enable failed: %d\n",
+ id, ret);
+ return ret;
+ }
+ ret = as3722_extreg_init(as3722_regs, id,
+ reg_config->ext_control);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "AS3722 ext control failed: %d", ret);
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id of_as3722_regulator_match[] = {
+ { .compatible = "ams,as3722-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_as3722_regulator_match);
+
+static struct platform_driver as3722_regulator_driver = {
+ .driver = {
+ .name = "as3722-regulator",
+ .of_match_table = of_as3722_regulator_match,
+ },
+ .probe = as3722_regulator_probe,
+};
+
+module_platform_driver(as3722_regulator_driver);
+
+MODULE_ALIAS("platform:as3722-regulator");
+MODULE_DESCRIPTION("AS3722 regulator driver");
+MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
new file mode 100644
index 000000000..c9e3677ac
--- /dev/null
+++ b/drivers/regulator/axp20x-regulator.c
@@ -0,0 +1,860 @@
+/*
+ * AXP20x regulators driver.
+ *
+ * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define AXP20X_IO_ENABLED 0x03
+#define AXP20X_IO_DISABLED 0x07
+
+#define AXP22X_IO_ENABLED 0x03
+#define AXP22X_IO_DISABLED 0x04
+
+#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
+#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
+#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x)
+
+#define AXP20X_FREQ_DCDC_MASK 0x0f
+
+#define AXP22X_MISC_N_VBUSEN_FUNC BIT(4)
+
+#define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _enable_val, _disable_val) \
+ [_family##_##_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .enable_val = (_enable_val), \
+ .disable_val = (_disable_val), \
+ .ops = &axp20x_ops, \
+ }
+
+#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp20x_ops, \
+ }
+
+#define AXP_DESC_SW(_family, _id, _match, _supply, _ereg, _emask) \
+ [_family##_##_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
+ .owner = THIS_MODULE, \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp20x_ops_sw, \
+ }
+
+#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \
+ [_family##_##_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
+ .n_voltages = 1, \
+ .owner = THIS_MODULE, \
+ .min_uV = (_volt) * 1000, \
+ .ops = &axp20x_ops_fixed \
+ }
+
+#define AXP_DESC_RANGES(_family, _id, _match, _supply, _ranges, _n_voltages, \
+ _vreg, _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
+ .n_voltages = (_n_voltages), \
+ .owner = THIS_MODULE, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .linear_ranges = (_ranges), \
+ .n_linear_ranges = ARRAY_SIZE(_ranges), \
+ .ops = &axp20x_ops_range, \
+ }
+
+static const struct regulator_ops axp20x_ops_fixed = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static const struct regulator_ops axp20x_ops_range = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_ops axp20x_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_ops axp20x_ops_sw = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_linear_range axp20x_ldo4_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000),
+ REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0),
+ REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000),
+ REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000),
+};
+
+static const struct regulator_desc axp20x_regulators[] = {
+ AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
+ AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
+ AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
+ AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
+ AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
+ AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
+ AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
+ AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
+ AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
+ AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges,
+ 16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL,
+ 0x08),
+ AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
+ AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
+ AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+};
+
+static const struct regulator_desc axp22x_regulators[] = {
+ AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
+ AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+ AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
+ AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
+ AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
+ AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
+ AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)),
+ /* secondary switchable output of DCDC1 */
+ AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
+ BIT(7)),
+ /* LDO regulator internally chained to DCDC5 */
+ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100,
+ AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+ AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ /* Note the datasheet only guarantees reliable operation up to
+ * 3.3V, this needs to be enforced via dts provided constraints */
+ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ /* Note the datasheet only guarantees reliable operation up to
+ * 3.3V, this needs to be enforced via dts provided constraints */
+ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
+};
+
+static const struct regulator_desc axp22x_drivevbus_regulator = {
+ .name = "drivevbus",
+ .supply_name = "drivevbus",
+ .of_match = of_match_ptr("drivevbus"),
+ .regulators_node = of_match_ptr("regulators"),
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .enable_reg = AXP20X_VBUS_IPSOUT_MGMT,
+ .enable_mask = BIT(2),
+ .ops = &axp20x_ops_sw,
+};
+
+/* DCDC ranges shared with AXP813 */
+static const struct regulator_linear_range axp803_dcdc234_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x0, 0x46, 10000),
+ REGULATOR_LINEAR_RANGE(1220000, 0x47, 0x4b, 20000),
+};
+
+static const struct regulator_linear_range axp803_dcdc5_ranges[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 10000),
+ REGULATOR_LINEAR_RANGE(1140000, 0x21, 0x44, 20000),
+};
+
+static const struct regulator_linear_range axp803_dcdc6_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000),
+ REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000),
+};
+
+/* AXP806's CLDO2 and AXP809's DLDO1 shares the same range */
+static const struct regulator_linear_range axp803_dldo2_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000),
+ REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000),
+};
+
+static const struct regulator_desc axp803_regulators[] = {
+ AXP_DESC(AXP803, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges,
+ 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(1)),
+ AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges,
+ 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(2)),
+ AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges,
+ 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(3)),
+ AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges,
+ 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(4)),
+ AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges,
+ 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(5)),
+ /* secondary switchable output of DCDC1 */
+ AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
+ BIT(7)),
+ AXP_DESC(AXP803, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)),
+ AXP_DESC(AXP803, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)),
+ AXP_DESC(AXP803, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+ AXP_DESC(AXP803, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges,
+ 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
+ BIT(4)),
+ AXP_DESC(AXP803, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC(AXP803, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC(AXP803, ELDO1, "eldo1", "eldoin", 700, 1900, 50,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP803, ELDO2, "eldo2", "eldoin", 700, 1900, 50,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP803, ELDO3, "eldo3", "eldoin", 700, 1900, 50,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ AXP_DESC(AXP803, FLDO1, "fldo1", "fldoin", 700, 1450, 50,
+ AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)),
+ AXP_DESC(AXP803, FLDO2, "fldo2", "fldoin", 700, 1450, 50,
+ AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)),
+ AXP_DESC_IO(AXP803, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_IO(AXP803, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000),
+};
+
+static const struct regulator_linear_range axp806_dcdca_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000),
+ REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000),
+};
+
+static const struct regulator_linear_range axp806_dcdcd_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000),
+ REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000),
+};
+
+static const struct regulator_desc axp806_regulators[] = {
+ AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges,
+ 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1,
+ BIT(0)),
+ AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50,
+ AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges,
+ 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1,
+ BIT(2)),
+ AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges,
+ 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1,
+ BIT(3)),
+ AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100,
+ AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)),
+ AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)),
+ AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100,
+ AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100,
+ AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100,
+ AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100,
+ AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)),
+ AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100,
+ AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100,
+ AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp803_dldo2_ranges,
+ 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2,
+ BIT(5)),
+ AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100,
+ AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)),
+};
+
+static const struct regulator_linear_range axp809_dcdc4_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000),
+};
+
+static const struct regulator_desc axp809_regulators[] = {
+ AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC(AXP809, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
+ AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+ AXP_DESC(AXP809, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
+ AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", axp809_dcdc4_ranges,
+ 57, AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1,
+ BIT(4)),
+ AXP_DESC(AXP809, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
+ AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)),
+ /* secondary switchable output of DCDC1 */
+ AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
+ BIT(7)),
+ /* LDO regulator internally chained to DCDC5 */
+ AXP_DESC(AXP809, DC5LDO, "dc5ldo", NULL, 700, 1400, 100,
+ AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC(AXP809, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP809, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp803_dldo2_ranges,
+ 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
+ BIT(3)),
+ AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC(AXP809, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP809, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ /*
+ * Note the datasheet only guarantees reliable operation up to
+ * 3.3V, this needs to be enforced via dts provided constraints
+ */
+ AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ /*
+ * Note the datasheet only guarantees reliable operation up to
+ * 3.3V, this needs to be enforced via dts provided constraints
+ */
+ AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800),
+ AXP_DESC_SW(AXP809, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(6)),
+};
+
+static const struct regulator_desc axp813_regulators[] = {
+ AXP_DESC(AXP813, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges,
+ 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(1)),
+ AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges,
+ 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(2)),
+ AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges,
+ 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(3)),
+ AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges,
+ 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(4)),
+ AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges,
+ 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(5)),
+ AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", axp803_dcdc6_ranges,
+ 72, AXP813_DCDC7_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
+ BIT(6)),
+ AXP_DESC(AXP813, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)),
+ AXP_DESC(AXP813, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)),
+ AXP_DESC(AXP813, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+ AXP_DESC(AXP813, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges,
+ 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
+ BIT(4)),
+ AXP_DESC(AXP813, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC(AXP813, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC(AXP813, ELDO1, "eldo1", "eldoin", 700, 1900, 50,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP813, ELDO2, "eldo2", "eldoin", 700, 1900, 50,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP813, ELDO3, "eldo3", "eldoin", 700, 1900, 50,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ /* to do / check ... */
+ AXP_DESC(AXP813, FLDO1, "fldo1", "fldoin", 700, 1450, 50,
+ AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)),
+ AXP_DESC(AXP813, FLDO2, "fldo2", "fldoin", 700, 1450, 50,
+ AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)),
+ /*
+ * TODO: FLDO3 = {DCDC5, FLDOIN} / 2
+ *
+ * This means FLDO3 effectively switches supplies at runtime,
+ * something the regulator subsystem does not support.
+ */
+ AXP_DESC_FIXED(AXP813, RTC_LDO, "rtc-ldo", "ips", 1800),
+ AXP_DESC_IO(AXP813, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_IO(AXP813, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_SW(AXP813, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(7)),
+};
+
+static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
+{
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ unsigned int reg = AXP20X_DCDC_FREQ;
+ u32 min, max, def, step;
+
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ min = 750;
+ max = 1875;
+ def = 1500;
+ step = 75;
+ break;
+ case AXP803_ID:
+ case AXP813_ID:
+ /*
+ * AXP803/AXP813 DCDC work frequency setting has the same
+ * range and step as AXP22X, but at a different register.
+ * Fall through to the check below.
+ * (See include/linux/mfd/axp20x.h)
+ */
+ reg = AXP803_DCDC_FREQ_CTRL;
+ case AXP806_ID:
+ /*
+ * AXP806 also have DCDC work frequency setting register at a
+ * different position.
+ */
+ if (axp20x->variant == AXP806_ID)
+ reg = AXP806_DCDC_FREQ_CTRL;
+ case AXP221_ID:
+ case AXP223_ID:
+ case AXP809_ID:
+ min = 1800;
+ max = 4050;
+ def = 3000;
+ step = 150;
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "Setting DCDC frequency for unsupported AXP variant\n");
+ return -EINVAL;
+ }
+
+ if (dcdcfreq == 0)
+ dcdcfreq = def;
+
+ if (dcdcfreq < min) {
+ dcdcfreq = min;
+ dev_warn(&pdev->dev, "DCDC frequency too low. Set to %ukHz\n",
+ min);
+ }
+
+ if (dcdcfreq > max) {
+ dcdcfreq = max;
+ dev_warn(&pdev->dev, "DCDC frequency too high. Set to %ukHz\n",
+ max);
+ }
+
+ dcdcfreq = (dcdcfreq - min) / step;
+
+ return regmap_update_bits(axp20x->regmap, reg,
+ AXP20X_FREQ_DCDC_MASK, dcdcfreq);
+}
+
+static int axp20x_regulator_parse_dt(struct platform_device *pdev)
+{
+ struct device_node *np, *regulators;
+ int ret = 0;
+ u32 dcdcfreq = 0;
+
+ np = of_node_get(pdev->dev.parent->of_node);
+ if (!np)
+ return 0;
+
+ regulators = of_get_child_by_name(np, "regulators");
+ if (!regulators) {
+ dev_warn(&pdev->dev, "regulators node not found\n");
+ } else {
+ of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq);
+ ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret);
+ }
+ of_node_put(regulators);
+ }
+
+ of_node_put(np);
+ return ret;
+}
+
+static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
+{
+ struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+ unsigned int reg = AXP20X_DCDC_MODE;
+ unsigned int mask;
+
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+ return -EINVAL;
+
+ mask = AXP20X_WORKMODE_DCDC2_MASK;
+ if (id == AXP20X_DCDC3)
+ mask = AXP20X_WORKMODE_DCDC3_MASK;
+
+ workmode <<= ffs(mask) - 1;
+ break;
+
+ case AXP806_ID:
+ reg = AXP806_DCDC_MODE_CTRL2;
+ /*
+ * AXP806 DCDC regulator IDs have the same range as AXP22X.
+ * Fall through to the check below.
+ * (See include/linux/mfd/axp20x.h)
+ */
+ case AXP221_ID:
+ case AXP223_ID:
+ case AXP809_ID:
+ if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5)
+ return -EINVAL;
+
+ mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP22X_DCDC1);
+ workmode <<= id - AXP22X_DCDC1;
+ break;
+
+ case AXP803_ID:
+ if (id < AXP803_DCDC1 || id > AXP803_DCDC6)
+ return -EINVAL;
+
+ mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP803_DCDC1);
+ workmode <<= id - AXP803_DCDC1;
+ break;
+
+ case AXP813_ID:
+ if (id < AXP813_DCDC1 || id > AXP813_DCDC7)
+ return -EINVAL;
+
+ mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP813_DCDC1);
+ workmode <<= id - AXP813_DCDC1;
+ break;
+
+ default:
+ /* should not happen */
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, reg, mask, workmode);
+}
+
+/*
+ * This function checks whether a regulator is part of a poly-phase
+ * output setup based on the registers settings. Returns true if it is.
+ */
+static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id)
+{
+ u32 reg = 0;
+
+ /*
+ * Currently in our supported AXP variants, only AXP803, AXP806,
+ * and AXP813 have polyphase regulators.
+ */
+ switch (axp20x->variant) {
+ case AXP803_ID:
+ case AXP813_ID:
+ regmap_read(axp20x->regmap, AXP803_POLYPHASE_CTRL, &reg);
+
+ switch (id) {
+ case AXP803_DCDC3:
+ return !!(reg & BIT(6));
+ case AXP803_DCDC6:
+ return !!(reg & BIT(5));
+ }
+ break;
+
+ case AXP806_ID:
+ regmap_read(axp20x->regmap, AXP806_DCDC_MODE_CTRL2, &reg);
+
+ switch (id) {
+ case AXP806_DCDCB:
+ return (((reg & GENMASK(7, 6)) == BIT(6)) ||
+ ((reg & GENMASK(7, 6)) == BIT(7)));
+ case AXP806_DCDCC:
+ return ((reg & GENMASK(7, 6)) == BIT(7));
+ case AXP806_DCDCE:
+ return !!(reg & BIT(5));
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static int axp20x_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *regulators;
+ struct regulator_config config = {
+ .dev = pdev->dev.parent,
+ .regmap = axp20x->regmap,
+ .driver_data = axp20x,
+ };
+ int ret, i, nregulators;
+ u32 workmode;
+ const char *dcdc1_name = axp22x_regulators[AXP22X_DCDC1].name;
+ const char *dcdc5_name = axp22x_regulators[AXP22X_DCDC5].name;
+ bool drivevbus = false;
+
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ regulators = axp20x_regulators;
+ nregulators = AXP20X_REG_ID_MAX;
+ break;
+ case AXP221_ID:
+ case AXP223_ID:
+ regulators = axp22x_regulators;
+ nregulators = AXP22X_REG_ID_MAX;
+ drivevbus = of_property_read_bool(pdev->dev.parent->of_node,
+ "x-powers,drive-vbus-en");
+ break;
+ case AXP803_ID:
+ regulators = axp803_regulators;
+ nregulators = AXP803_REG_ID_MAX;
+ drivevbus = of_property_read_bool(pdev->dev.parent->of_node,
+ "x-powers,drive-vbus-en");
+ break;
+ case AXP806_ID:
+ regulators = axp806_regulators;
+ nregulators = AXP806_REG_ID_MAX;
+ break;
+ case AXP809_ID:
+ regulators = axp809_regulators;
+ nregulators = AXP809_REG_ID_MAX;
+ break;
+ case AXP813_ID:
+ regulators = axp813_regulators;
+ nregulators = AXP813_REG_ID_MAX;
+ drivevbus = of_property_read_bool(pdev->dev.parent->of_node,
+ "x-powers,drive-vbus-en");
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+ axp20x->variant);
+ return -EINVAL;
+ }
+
+ /* This only sets the dcdc freq. Ignore any errors */
+ axp20x_regulator_parse_dt(pdev);
+
+ for (i = 0; i < nregulators; i++) {
+ const struct regulator_desc *desc = &regulators[i];
+ struct regulator_desc *new_desc;
+
+ /*
+ * If this regulator is a slave in a poly-phase setup,
+ * skip it, as its controls are bound to the master
+ * regulator and won't work.
+ */
+ if (axp20x_is_polyphase_slave(axp20x, i))
+ continue;
+
+ /* Support for AXP813's FLDO3 is not implemented */
+ if (axp20x->variant == AXP813_ID && i == AXP813_FLDO3)
+ continue;
+
+ /*
+ * Regulators DC1SW and DC5LDO are connected internally,
+ * so we have to handle their supply names separately.
+ *
+ * We always register the regulators in proper sequence,
+ * so the supply names are correctly read. See the last
+ * part of this loop to see where we save the DT defined
+ * name.
+ */
+ if ((regulators == axp22x_regulators && i == AXP22X_DC1SW) ||
+ (regulators == axp803_regulators && i == AXP803_DC1SW) ||
+ (regulators == axp809_regulators && i == AXP809_DC1SW)) {
+ new_desc = devm_kzalloc(&pdev->dev, sizeof(*desc),
+ GFP_KERNEL);
+ if (!new_desc)
+ return -ENOMEM;
+
+ *new_desc = regulators[i];
+ new_desc->supply_name = dcdc1_name;
+ desc = new_desc;
+ }
+
+ if ((regulators == axp22x_regulators && i == AXP22X_DC5LDO) ||
+ (regulators == axp809_regulators && i == AXP809_DC5LDO)) {
+ new_desc = devm_kzalloc(&pdev->dev, sizeof(*desc),
+ GFP_KERNEL);
+ if (!new_desc)
+ return -ENOMEM;
+
+ *new_desc = regulators[i];
+ new_desc->supply_name = dcdc5_name;
+ desc = new_desc;
+ }
+
+ rdev = devm_regulator_register(&pdev->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register %s\n",
+ regulators[i].name);
+
+ return PTR_ERR(rdev);
+ }
+
+ ret = of_property_read_u32(rdev->dev.of_node,
+ "x-powers,dcdc-workmode",
+ &workmode);
+ if (!ret) {
+ if (axp20x_set_dcdc_workmode(rdev, i, workmode))
+ dev_err(&pdev->dev, "Failed to set workmode on %s\n",
+ rdev->desc->name);
+ }
+
+ /*
+ * Save AXP22X DCDC1 / DCDC5 regulator names for later.
+ */
+ if ((regulators == axp22x_regulators && i == AXP22X_DCDC1) ||
+ (regulators == axp809_regulators && i == AXP809_DCDC1))
+ of_property_read_string(rdev->dev.of_node,
+ "regulator-name",
+ &dcdc1_name);
+
+ if ((regulators == axp22x_regulators && i == AXP22X_DCDC5) ||
+ (regulators == axp809_regulators && i == AXP809_DCDC5))
+ of_property_read_string(rdev->dev.of_node,
+ "regulator-name",
+ &dcdc5_name);
+ }
+
+ if (drivevbus) {
+ /* Change N_VBUSEN sense pin to DRIVEVBUS output pin */
+ regmap_update_bits(axp20x->regmap, AXP20X_OVER_TMP,
+ AXP22X_MISC_N_VBUSEN_FUNC, 0);
+ rdev = devm_regulator_register(&pdev->dev,
+ &axp22x_drivevbus_regulator,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register drivevbus\n");
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver axp20x_regulator_driver = {
+ .probe = axp20x_regulator_probe,
+ .driver = {
+ .name = "axp20x-regulator",
+ },
+};
+
+module_platform_driver(axp20x_regulator_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC");
+MODULE_ALIAS("platform:axp20x-regulator");
diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c
new file mode 100644
index 000000000..92d6d7b10
--- /dev/null
+++ b/drivers/regulator/bcm590xx-regulator.c
@@ -0,0 +1,466 @@
+/*
+ * Broadcom BCM590xx regulator driver
+ *
+ * Copyright 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * 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 <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/bcm590xx.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/* I2C slave 0 registers */
+#define BCM590XX_RFLDOPMCTRL1 0x60
+#define BCM590XX_IOSR1PMCTRL1 0x7a
+#define BCM590XX_IOSR2PMCTRL1 0x7c
+#define BCM590XX_CSRPMCTRL1 0x7e
+#define BCM590XX_SDSR1PMCTRL1 0x82
+#define BCM590XX_SDSR2PMCTRL1 0x86
+#define BCM590XX_MSRPMCTRL1 0x8a
+#define BCM590XX_VSRPMCTRL1 0x8e
+#define BCM590XX_RFLDOCTRL 0x96
+#define BCM590XX_CSRVOUT1 0xc0
+
+/* I2C slave 1 registers */
+#define BCM590XX_GPLDO5PMCTRL1 0x16
+#define BCM590XX_GPLDO6PMCTRL1 0x18
+#define BCM590XX_GPLDO1CTRL 0x1a
+#define BCM590XX_GPLDO2CTRL 0x1b
+#define BCM590XX_GPLDO3CTRL 0x1c
+#define BCM590XX_GPLDO4CTRL 0x1d
+#define BCM590XX_GPLDO5CTRL 0x1e
+#define BCM590XX_GPLDO6CTRL 0x1f
+#define BCM590XX_OTG_CTRL 0x40
+#define BCM590XX_GPLDO1PMCTRL1 0x57
+#define BCM590XX_GPLDO2PMCTRL1 0x59
+#define BCM590XX_GPLDO3PMCTRL1 0x5b
+#define BCM590XX_GPLDO4PMCTRL1 0x5d
+
+#define BCM590XX_REG_ENABLE BIT(7)
+#define BCM590XX_VBUS_ENABLE BIT(2)
+#define BCM590XX_LDO_VSEL_MASK GENMASK(5, 3)
+#define BCM590XX_SR_VSEL_MASK GENMASK(5, 0)
+
+/*
+ * RFLDO to VSR regulators are
+ * accessed via I2C slave 0
+ */
+
+/* LDO regulator IDs */
+#define BCM590XX_REG_RFLDO 0
+#define BCM590XX_REG_CAMLDO1 1
+#define BCM590XX_REG_CAMLDO2 2
+#define BCM590XX_REG_SIMLDO1 3
+#define BCM590XX_REG_SIMLDO2 4
+#define BCM590XX_REG_SDLDO 5
+#define BCM590XX_REG_SDXLDO 6
+#define BCM590XX_REG_MMCLDO1 7
+#define BCM590XX_REG_MMCLDO2 8
+#define BCM590XX_REG_AUDLDO 9
+#define BCM590XX_REG_MICLDO 10
+#define BCM590XX_REG_USBLDO 11
+#define BCM590XX_REG_VIBLDO 12
+
+/* DCDC regulator IDs */
+#define BCM590XX_REG_CSR 13
+#define BCM590XX_REG_IOSR1 14
+#define BCM590XX_REG_IOSR2 15
+#define BCM590XX_REG_MSR 16
+#define BCM590XX_REG_SDSR1 17
+#define BCM590XX_REG_SDSR2 18
+#define BCM590XX_REG_VSR 19
+
+/*
+ * GPLDO1 to VBUS regulators are
+ * accessed via I2C slave 1
+ */
+
+#define BCM590XX_REG_GPLDO1 20
+#define BCM590XX_REG_GPLDO2 21
+#define BCM590XX_REG_GPLDO3 22
+#define BCM590XX_REG_GPLDO4 23
+#define BCM590XX_REG_GPLDO5 24
+#define BCM590XX_REG_GPLDO6 25
+#define BCM590XX_REG_VBUS 26
+
+#define BCM590XX_NUM_REGS 27
+
+#define BCM590XX_REG_IS_LDO(n) (n < BCM590XX_REG_CSR)
+#define BCM590XX_REG_IS_GPLDO(n) \
+ ((n > BCM590XX_REG_VSR) && (n < BCM590XX_REG_VBUS))
+#define BCM590XX_REG_IS_VBUS(n) (n == BCM590XX_REG_VBUS)
+
+struct bcm590xx_board {
+ struct regulator_init_data *bcm590xx_pmu_init_data[BCM590XX_NUM_REGS];
+};
+
+/* LDO group A: supported voltages in microvolts */
+static const unsigned int ldo_a_table[] = {
+ 1200000, 1800000, 2500000, 2700000, 2800000,
+ 2900000, 3000000, 3300000,
+};
+
+/* LDO group C: supported voltages in microvolts */
+static const unsigned int ldo_c_table[] = {
+ 3100000, 1800000, 2500000, 2700000, 2800000,
+ 2900000, 3000000, 3300000,
+};
+
+static const unsigned int ldo_vbus[] = {
+ 5000000,
+};
+
+/* DCDC group CSR: supported voltages in microvolts */
+static const struct regulator_linear_range dcdc_csr_ranges[] = {
+ REGULATOR_LINEAR_RANGE(860000, 2, 50, 10000),
+ REGULATOR_LINEAR_RANGE(1360000, 51, 55, 20000),
+ REGULATOR_LINEAR_RANGE(900000, 56, 63, 0),
+};
+
+/* DCDC group IOSR1: supported voltages in microvolts */
+static const struct regulator_linear_range dcdc_iosr1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(860000, 2, 51, 10000),
+ REGULATOR_LINEAR_RANGE(1500000, 52, 52, 0),
+ REGULATOR_LINEAR_RANGE(1800000, 53, 53, 0),
+ REGULATOR_LINEAR_RANGE(900000, 54, 63, 0),
+};
+
+/* DCDC group SDSR1: supported voltages in microvolts */
+static const struct regulator_linear_range dcdc_sdsr1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(860000, 2, 50, 10000),
+ REGULATOR_LINEAR_RANGE(1340000, 51, 51, 0),
+ REGULATOR_LINEAR_RANGE(900000, 52, 63, 0),
+};
+
+struct bcm590xx_info {
+ const char *name;
+ const char *vin_name;
+ u8 n_voltages;
+ const unsigned int *volt_table;
+ u8 n_linear_ranges;
+ const struct regulator_linear_range *linear_ranges;
+};
+
+#define BCM590XX_REG_TABLE(_name, _table) \
+ { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(_table), \
+ .volt_table = _table, \
+ }
+
+#define BCM590XX_REG_RANGES(_name, _ranges) \
+ { \
+ .name = #_name, \
+ .n_voltages = 64, \
+ .n_linear_ranges = ARRAY_SIZE(_ranges), \
+ .linear_ranges = _ranges, \
+ }
+
+static struct bcm590xx_info bcm590xx_regs[] = {
+ BCM590XX_REG_TABLE(rfldo, ldo_a_table),
+ BCM590XX_REG_TABLE(camldo1, ldo_c_table),
+ BCM590XX_REG_TABLE(camldo2, ldo_c_table),
+ BCM590XX_REG_TABLE(simldo1, ldo_a_table),
+ BCM590XX_REG_TABLE(simldo2, ldo_a_table),
+ BCM590XX_REG_TABLE(sdldo, ldo_c_table),
+ BCM590XX_REG_TABLE(sdxldo, ldo_a_table),
+ BCM590XX_REG_TABLE(mmcldo1, ldo_a_table),
+ BCM590XX_REG_TABLE(mmcldo2, ldo_a_table),
+ BCM590XX_REG_TABLE(audldo, ldo_a_table),
+ BCM590XX_REG_TABLE(micldo, ldo_a_table),
+ BCM590XX_REG_TABLE(usbldo, ldo_a_table),
+ BCM590XX_REG_TABLE(vibldo, ldo_c_table),
+ BCM590XX_REG_RANGES(csr, dcdc_csr_ranges),
+ BCM590XX_REG_RANGES(iosr1, dcdc_iosr1_ranges),
+ BCM590XX_REG_RANGES(iosr2, dcdc_iosr1_ranges),
+ BCM590XX_REG_RANGES(msr, dcdc_iosr1_ranges),
+ BCM590XX_REG_RANGES(sdsr1, dcdc_sdsr1_ranges),
+ BCM590XX_REG_RANGES(sdsr2, dcdc_iosr1_ranges),
+ BCM590XX_REG_RANGES(vsr, dcdc_iosr1_ranges),
+ BCM590XX_REG_TABLE(gpldo1, ldo_a_table),
+ BCM590XX_REG_TABLE(gpldo2, ldo_a_table),
+ BCM590XX_REG_TABLE(gpldo3, ldo_a_table),
+ BCM590XX_REG_TABLE(gpldo4, ldo_a_table),
+ BCM590XX_REG_TABLE(gpldo5, ldo_a_table),
+ BCM590XX_REG_TABLE(gpldo6, ldo_a_table),
+ BCM590XX_REG_TABLE(vbus, ldo_vbus),
+};
+
+struct bcm590xx_reg {
+ struct regulator_desc *desc;
+ struct bcm590xx *mfd;
+};
+
+static int bcm590xx_get_vsel_register(int id)
+{
+ if (BCM590XX_REG_IS_LDO(id))
+ return BCM590XX_RFLDOCTRL + id;
+ else if (BCM590XX_REG_IS_GPLDO(id))
+ return BCM590XX_GPLDO1CTRL + id;
+ else
+ return BCM590XX_CSRVOUT1 + (id - BCM590XX_REG_CSR) * 3;
+}
+
+static int bcm590xx_get_enable_register(int id)
+{
+ int reg = 0;
+
+ if (BCM590XX_REG_IS_LDO(id))
+ reg = BCM590XX_RFLDOPMCTRL1 + id * 2;
+ else if (BCM590XX_REG_IS_GPLDO(id))
+ reg = BCM590XX_GPLDO1PMCTRL1 + id * 2;
+ else
+ switch (id) {
+ case BCM590XX_REG_CSR:
+ reg = BCM590XX_CSRPMCTRL1;
+ break;
+ case BCM590XX_REG_IOSR1:
+ reg = BCM590XX_IOSR1PMCTRL1;
+ break;
+ case BCM590XX_REG_IOSR2:
+ reg = BCM590XX_IOSR2PMCTRL1;
+ break;
+ case BCM590XX_REG_MSR:
+ reg = BCM590XX_MSRPMCTRL1;
+ break;
+ case BCM590XX_REG_SDSR1:
+ reg = BCM590XX_SDSR1PMCTRL1;
+ break;
+ case BCM590XX_REG_SDSR2:
+ reg = BCM590XX_SDSR2PMCTRL1;
+ break;
+ case BCM590XX_REG_VBUS:
+ reg = BCM590XX_OTG_CTRL;
+ }
+
+
+ return reg;
+}
+
+static const struct regulator_ops bcm590xx_ops_ldo = {
+ .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_iterate,
+};
+
+static const struct regulator_ops bcm590xx_ops_dcdc = {
+ .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_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static const struct regulator_ops bcm590xx_ops_vbus = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+};
+
+#define BCM590XX_MATCH(_name, _id) \
+ { \
+ .name = #_name, \
+ .driver_data = (void *)&bcm590xx_regs[BCM590XX_REG_##_id], \
+ }
+
+static struct of_regulator_match bcm590xx_matches[] = {
+ BCM590XX_MATCH(rfldo, RFLDO),
+ BCM590XX_MATCH(camldo1, CAMLDO1),
+ BCM590XX_MATCH(camldo2, CAMLDO2),
+ BCM590XX_MATCH(simldo1, SIMLDO1),
+ BCM590XX_MATCH(simldo2, SIMLDO2),
+ BCM590XX_MATCH(sdldo, SDLDO),
+ BCM590XX_MATCH(sdxldo, SDXLDO),
+ BCM590XX_MATCH(mmcldo1, MMCLDO1),
+ BCM590XX_MATCH(mmcldo2, MMCLDO2),
+ BCM590XX_MATCH(audldo, AUDLDO),
+ BCM590XX_MATCH(micldo, MICLDO),
+ BCM590XX_MATCH(usbldo, USBLDO),
+ BCM590XX_MATCH(vibldo, VIBLDO),
+ BCM590XX_MATCH(csr, CSR),
+ BCM590XX_MATCH(iosr1, IOSR1),
+ BCM590XX_MATCH(iosr2, IOSR2),
+ BCM590XX_MATCH(msr, MSR),
+ BCM590XX_MATCH(sdsr1, SDSR1),
+ BCM590XX_MATCH(sdsr2, SDSR2),
+ BCM590XX_MATCH(vsr, VSR),
+ BCM590XX_MATCH(gpldo1, GPLDO1),
+ BCM590XX_MATCH(gpldo2, GPLDO2),
+ BCM590XX_MATCH(gpldo3, GPLDO3),
+ BCM590XX_MATCH(gpldo4, GPLDO4),
+ BCM590XX_MATCH(gpldo5, GPLDO5),
+ BCM590XX_MATCH(gpldo6, GPLDO6),
+ BCM590XX_MATCH(vbus, VBUS),
+};
+
+static struct bcm590xx_board *bcm590xx_parse_dt_reg_data(
+ struct platform_device *pdev,
+ struct of_regulator_match **bcm590xx_reg_matches)
+{
+ struct bcm590xx_board *data;
+ struct device_node *np = pdev->dev.parent->of_node;
+ struct device_node *regulators;
+ struct of_regulator_match *matches = bcm590xx_matches;
+ int count = ARRAY_SIZE(bcm590xx_matches);
+ int idx = 0;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "of node not found\n");
+ return NULL;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ np = of_node_get(np);
+ regulators = of_get_child_by_name(np, "regulators");
+ if (!regulators) {
+ dev_warn(&pdev->dev, "regulator node not found\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;
+ }
+
+ *bcm590xx_reg_matches = matches;
+
+ for (idx = 0; idx < count; idx++) {
+ if (!matches[idx].init_data || !matches[idx].of_node)
+ continue;
+
+ data->bcm590xx_pmu_init_data[idx] = matches[idx].init_data;
+ }
+
+ return data;
+}
+
+static int bcm590xx_probe(struct platform_device *pdev)
+{
+ struct bcm590xx *bcm590xx = dev_get_drvdata(pdev->dev.parent);
+ struct bcm590xx_board *pmu_data = NULL;
+ struct bcm590xx_reg *pmu;
+ struct regulator_config config = { };
+ struct bcm590xx_info *info;
+ struct regulator_init_data *reg_data;
+ struct regulator_dev *rdev;
+ struct of_regulator_match *bcm590xx_reg_matches = NULL;
+ int i;
+
+ pmu_data = bcm590xx_parse_dt_reg_data(pdev,
+ &bcm590xx_reg_matches);
+
+ pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL);
+ if (!pmu)
+ return -ENOMEM;
+
+ pmu->mfd = bcm590xx;
+
+ platform_set_drvdata(pdev, pmu);
+
+ pmu->desc = devm_kcalloc(&pdev->dev,
+ BCM590XX_NUM_REGS,
+ sizeof(struct regulator_desc),
+ GFP_KERNEL);
+ if (!pmu->desc)
+ return -ENOMEM;
+
+ info = bcm590xx_regs;
+
+ for (i = 0; i < BCM590XX_NUM_REGS; i++, info++) {
+ if (pmu_data)
+ reg_data = pmu_data->bcm590xx_pmu_init_data[i];
+ else
+ reg_data = NULL;
+
+ /* Register the regulators */
+ pmu->desc[i].name = info->name;
+ pmu->desc[i].supply_name = info->vin_name;
+ pmu->desc[i].id = i;
+ pmu->desc[i].volt_table = info->volt_table;
+ pmu->desc[i].n_voltages = info->n_voltages;
+ pmu->desc[i].linear_ranges = info->linear_ranges;
+ pmu->desc[i].n_linear_ranges = info->n_linear_ranges;
+
+ if ((BCM590XX_REG_IS_LDO(i)) || (BCM590XX_REG_IS_GPLDO(i))) {
+ pmu->desc[i].ops = &bcm590xx_ops_ldo;
+ pmu->desc[i].vsel_mask = BCM590XX_LDO_VSEL_MASK;
+ } else if (BCM590XX_REG_IS_VBUS(i))
+ pmu->desc[i].ops = &bcm590xx_ops_vbus;
+ else {
+ pmu->desc[i].ops = &bcm590xx_ops_dcdc;
+ pmu->desc[i].vsel_mask = BCM590XX_SR_VSEL_MASK;
+ }
+
+ if (BCM590XX_REG_IS_VBUS(i))
+ pmu->desc[i].enable_mask = BCM590XX_VBUS_ENABLE;
+ else {
+ pmu->desc[i].vsel_reg = bcm590xx_get_vsel_register(i);
+ pmu->desc[i].enable_is_inverted = true;
+ pmu->desc[i].enable_mask = BCM590XX_REG_ENABLE;
+ }
+ pmu->desc[i].enable_reg = bcm590xx_get_enable_register(i);
+ pmu->desc[i].type = REGULATOR_VOLTAGE;
+ pmu->desc[i].owner = THIS_MODULE;
+
+ config.dev = bcm590xx->dev;
+ config.init_data = reg_data;
+ config.driver_data = pmu;
+ if (BCM590XX_REG_IS_GPLDO(i) || BCM590XX_REG_IS_VBUS(i))
+ config.regmap = bcm590xx->regmap_sec;
+ else
+ config.regmap = bcm590xx->regmap_pri;
+
+ if (bcm590xx_reg_matches)
+ config.of_node = bcm590xx_reg_matches[i].of_node;
+
+ rdev = devm_regulator_register(&pdev->dev, &pmu->desc[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(bcm590xx->dev,
+ "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver bcm590xx_regulator_driver = {
+ .driver = {
+ .name = "bcm590xx-vregs",
+ },
+ .probe = bcm590xx_probe,
+};
+module_platform_driver(bcm590xx_regulator_driver);
+
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_DESCRIPTION("BCM590xx voltage regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bcm590xx-vregs");
diff --git a/drivers/regulator/bd71837-regulator.c b/drivers/regulator/bd71837-regulator.c
new file mode 100644
index 000000000..a1bd8aaf4
--- /dev/null
+++ b/drivers/regulator/bd71837-regulator.c
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// bd71837-regulator.c ROHM BD71837MWV regulator driver
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/rohm-bd718x7.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+struct bd71837_pmic {
+ struct regulator_desc descs[BD71837_REGULATOR_CNT];
+ struct bd71837 *mfd;
+ struct platform_device *pdev;
+ struct regulator_dev *rdev[BD71837_REGULATOR_CNT];
+};
+
+/*
+ * BUCK1/2/3/4
+ * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
+ * 00: 10.00mV/usec 10mV 1uS
+ * 01: 5.00mV/usec 10mV 2uS
+ * 10: 2.50mV/usec 10mV 4uS
+ * 11: 1.25mV/usec 10mV 8uS
+ */
+static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ struct bd71837_pmic *pmic = rdev_get_drvdata(rdev);
+ struct bd71837 *mfd = pmic->mfd;
+ int id = rdev->desc->id;
+ unsigned int ramp_value = BUCK_RAMPRATE_10P00MV;
+
+ dev_dbg(&pmic->pdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1,
+ ramp_delay);
+ switch (ramp_delay) {
+ case 1 ... 1250:
+ ramp_value = BUCK_RAMPRATE_1P25MV;
+ break;
+ case 1251 ... 2500:
+ ramp_value = BUCK_RAMPRATE_2P50MV;
+ break;
+ case 2501 ... 5000:
+ ramp_value = BUCK_RAMPRATE_5P00MV;
+ break;
+ case 5001 ... 10000:
+ ramp_value = BUCK_RAMPRATE_10P00MV;
+ break;
+ default:
+ ramp_value = BUCK_RAMPRATE_10P00MV;
+ dev_err(&pmic->pdev->dev,
+ "%s: ramp_delay: %d not supported, setting 10000mV//us\n",
+ rdev->desc->name, ramp_delay);
+ }
+
+ return regmap_update_bits(mfd->regmap, BD71837_REG_BUCK1_CTRL + id,
+ BUCK_RAMPRATE_MASK, ramp_value << 6);
+}
+
+/* Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed.
+ * Bucks 5 to 8 and LDOs can use PFM and must be disabled when voltage
+ * is changed. Hence we return -EBUSY for these if voltage is changed
+ * when BUCK/LDO is enabled.
+ */
+static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ if (regulator_is_enabled_regmap(rdev))
+ return -EBUSY;
+
+ return regulator_set_voltage_sel_regmap(rdev, sel);
+}
+
+static struct regulator_ops bd71837_ldo_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = bd71837_set_voltage_sel_restricted,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops bd71837_ldo_regulator_nolinear_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = bd71837_set_voltage_sel_restricted,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops bd71837_buck_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = bd71837_set_voltage_sel_restricted,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static struct regulator_ops bd71837_buck_regulator_nolinear_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = bd71837_set_voltage_sel_restricted,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static struct regulator_ops bd71837_buck1234_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = bd71837_buck1234_set_ramp_delay,
+};
+
+/*
+ * BUCK1/2/3/4
+ * 0.70 to 1.30V (10mV step)
+ */
+static const struct regulator_linear_range bd71837_buck1234_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x00, 0x3C, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x3D, 0x3F, 0),
+};
+
+/*
+ * BUCK5
+ * 0.9V to 1.35V ()
+ */
+static const struct regulator_linear_range bd71837_buck5_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
+ REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
+ REGULATOR_LINEAR_RANGE(1200000, 0x06, 0x07, 150000),
+};
+
+/*
+ * BUCK6
+ * 3.0V to 3.3V (step 100mV)
+ */
+static const struct regulator_linear_range bd71837_buck6_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
+};
+
+/*
+ * BUCK7
+ * 000 = 1.605V
+ * 001 = 1.695V
+ * 010 = 1.755V
+ * 011 = 1.8V (Initial)
+ * 100 = 1.845V
+ * 101 = 1.905V
+ * 110 = 1.95V
+ * 111 = 1.995V
+ */
+static const unsigned int buck_7_volts[] = {
+ 1605000, 1695000, 1755000, 1800000, 1845000, 1905000, 1950000, 1995000
+};
+
+/*
+ * BUCK8
+ * 0.8V to 1.40V (step 10mV)
+ */
+static const struct regulator_linear_range bd71837_buck8_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0x00, 0x3C, 10000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x3D, 0x3F, 0),
+};
+
+/*
+ * LDO1
+ * 3.0 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
+};
+
+/*
+ * LDO2
+ * 0.8 or 0.9V
+ */
+static const unsigned int ldo_2_volts[] = {
+ 900000, 800000
+};
+
+/*
+ * LDO3
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo3_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+/*
+ * LDO4
+ * 0.9 to 1.8V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo4_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
+};
+
+/*
+ * LDO5
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo5_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+/*
+ * LDO6
+ * 0.9 to 1.8V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo6_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
+};
+
+/*
+ * LDO7
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo7_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+static const struct regulator_desc bd71837_regulators[] = {
+ {
+ .name = "buck1",
+ .of_match = of_match_ptr("BUCK1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK1,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK1_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK1_VOLT_RUN,
+ .vsel_mask = BUCK1_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK1_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck2",
+ .of_match = of_match_ptr("BUCK2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK2,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK2_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK2_VOLT_RUN,
+ .vsel_mask = BUCK2_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK2_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck3",
+ .of_match = of_match_ptr("BUCK3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK3,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK3_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK3_VOLT_RUN,
+ .vsel_mask = BUCK3_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK3_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck4",
+ .of_match = of_match_ptr("BUCK4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK4,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK4_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK4_VOLT_RUN,
+ .vsel_mask = BUCK4_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK4_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck5",
+ .of_match = of_match_ptr("BUCK5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK5,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK5_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck5_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck5_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK5_VOLT,
+ .vsel_mask = BUCK5_MASK,
+ .enable_reg = BD71837_REG_BUCK5_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck6",
+ .of_match = of_match_ptr("BUCK6"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK6,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK6_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck6_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck6_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK6_VOLT,
+ .vsel_mask = BUCK6_MASK,
+ .enable_reg = BD71837_REG_BUCK6_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck7",
+ .of_match = of_match_ptr("BUCK7"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK7,
+ .ops = &bd71837_buck_regulator_nolinear_ops,
+ .type = REGULATOR_VOLTAGE,
+ .volt_table = &buck_7_volts[0],
+ .n_voltages = ARRAY_SIZE(buck_7_volts),
+ .vsel_reg = BD71837_REG_BUCK7_VOLT,
+ .vsel_mask = BUCK7_MASK,
+ .enable_reg = BD71837_REG_BUCK7_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "buck8",
+ .of_match = of_match_ptr("BUCK8"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_BUCK8,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK8_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck8_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck8_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK8_VOLT,
+ .vsel_mask = BUCK8_MASK,
+ .enable_reg = BD71837_REG_BUCK8_CTRL,
+ .enable_mask = BD71837_BUCK_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo1",
+ .of_match = of_match_ptr("LDO1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO1,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO1_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo1_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO1_VOLT,
+ .vsel_mask = LDO1_MASK,
+ .enable_reg = BD71837_REG_LDO1_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo2",
+ .of_match = of_match_ptr("LDO2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO2,
+ .ops = &bd71837_ldo_regulator_nolinear_ops,
+ .type = REGULATOR_VOLTAGE,
+ .volt_table = &ldo_2_volts[0],
+ .vsel_reg = BD71837_REG_LDO2_VOLT,
+ .vsel_mask = LDO2_MASK,
+ .n_voltages = ARRAY_SIZE(ldo_2_volts),
+ .n_voltages = BD71837_LDO2_VOLTAGE_NUM,
+ .enable_reg = BD71837_REG_LDO2_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo3",
+ .of_match = of_match_ptr("LDO3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO3,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO3_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo3_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO3_VOLT,
+ .vsel_mask = LDO3_MASK,
+ .enable_reg = BD71837_REG_LDO3_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo4",
+ .of_match = of_match_ptr("LDO4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO4,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO4_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo4_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo4_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO4_VOLT,
+ .vsel_mask = LDO4_MASK,
+ .enable_reg = BD71837_REG_LDO4_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo5",
+ .of_match = of_match_ptr("LDO5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO5,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO5_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo5_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo5_voltage_ranges),
+ /* LDO5 is supplied by buck6 */
+ .supply_name = "buck6",
+ .vsel_reg = BD71837_REG_LDO5_VOLT,
+ .vsel_mask = LDO5_MASK,
+ .enable_reg = BD71837_REG_LDO5_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo6",
+ .of_match = of_match_ptr("LDO6"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO6,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO6_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo6_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo6_voltage_ranges),
+ /* LDO6 is supplied by buck7 */
+ .supply_name = "buck7",
+ .vsel_reg = BD71837_REG_LDO6_VOLT,
+ .vsel_mask = LDO6_MASK,
+ .enable_reg = BD71837_REG_LDO6_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "ldo7",
+ .of_match = of_match_ptr("LDO7"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD71837_LDO7,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO7_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo7_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo7_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO7_VOLT,
+ .vsel_mask = LDO7_MASK,
+ .enable_reg = BD71837_REG_LDO7_VOLT,
+ .enable_mask = BD71837_LDO_EN,
+ .owner = THIS_MODULE,
+ },
+};
+
+struct reg_init {
+ unsigned int reg;
+ unsigned int mask;
+};
+
+static int bd71837_probe(struct platform_device *pdev)
+{
+ struct bd71837_pmic *pmic;
+ struct regulator_config config = { 0 };
+ struct reg_init pmic_regulator_inits[] = {
+ {
+ .reg = BD71837_REG_BUCK1_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK2_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK3_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK4_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK5_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK6_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK7_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_BUCK8_CTRL,
+ .mask = BD71837_BUCK_SEL,
+ }, {
+ .reg = BD71837_REG_LDO1_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }, {
+ .reg = BD71837_REG_LDO2_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }, {
+ .reg = BD71837_REG_LDO3_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }, {
+ .reg = BD71837_REG_LDO4_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }, {
+ .reg = BD71837_REG_LDO5_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }, {
+ .reg = BD71837_REG_LDO6_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }, {
+ .reg = BD71837_REG_LDO7_VOLT,
+ .mask = BD71837_LDO_SEL,
+ }
+ };
+
+ int i, err;
+
+ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ memcpy(pmic->descs, bd71837_regulators, sizeof(pmic->descs));
+
+ pmic->pdev = pdev;
+ pmic->mfd = dev_get_drvdata(pdev->dev.parent);
+
+ if (!pmic->mfd) {
+ dev_err(&pdev->dev, "No MFD driver data\n");
+ err = -EINVAL;
+ goto err;
+ }
+ platform_set_drvdata(pdev, pmic);
+
+ /* Register LOCK release */
+ err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK,
+ (REGLOCK_PWRSEQ | REGLOCK_VREG), 0);
+ if (err) {
+ dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err);
+ goto err;
+ } else {
+ dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n",
+ BD71837_REG_REGLOCK);
+ }
+
+ /*
+ * There is a HW quirk in BD71837. The shutdown sequence timings for
+ * bucks/LDOs which are controlled via register interface are changed.
+ * At PMIC poweroff the voltage for BUCK6/7 is cut immediately at the
+ * beginning of shut-down sequence. As bucks 6 and 7 are parent
+ * supplies for LDO5 and LDO6 - this causes LDO5/6 voltage
+ * monitoring to errorneously detect under voltage and force PMIC to
+ * emergency state instead of poweroff. In order to avoid this we
+ * disable voltage monitoring for LDO5 and LDO6
+ */
+ err = regmap_update_bits(pmic->mfd->regmap, BD718XX_REG_MVRFLTMASK2,
+ BD718XX_LDO5_VRMON80 | BD718XX_LDO6_VRMON80,
+ BD718XX_LDO5_VRMON80 | BD718XX_LDO6_VRMON80);
+ if (err) {
+ dev_err(&pmic->pdev->dev,
+ "Failed to disable voltage monitoring\n");
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) {
+
+ struct regulator_desc *desc;
+ struct regulator_dev *rdev;
+
+ desc = &pmic->descs[i];
+
+ config.dev = pdev->dev.parent;
+ config.driver_data = pmic;
+ config.regmap = pmic->mfd->regmap;
+
+ rdev = devm_regulator_register(&pdev->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(pmic->mfd->dev,
+ "failed to register %s regulator\n",
+ desc->name);
+ err = PTR_ERR(rdev);
+ goto err;
+ }
+ /* Regulator register gets the regulator constraints and
+ * applies them (set_machine_constraints). This should have
+ * turned the control register(s) to correct values and we
+ * can now switch the control from PMIC state machine to the
+ * register interface
+ */
+ err = regmap_update_bits(pmic->mfd->regmap,
+ pmic_regulator_inits[i].reg,
+ pmic_regulator_inits[i].mask,
+ 0xFFFFFFFF);
+ if (err) {
+ dev_err(&pmic->pdev->dev,
+ "Failed to write BUCK/LDO SEL bit for (%s)\n",
+ desc->name);
+ goto err;
+ }
+
+ pmic->rdev[i] = rdev;
+ }
+
+err:
+ return err;
+}
+
+static struct platform_driver bd71837_regulator = {
+ .driver = {
+ .name = "bd71837-pmic",
+ },
+ .probe = bd71837_probe,
+};
+
+module_platform_driver(bd71837_regulator);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71837 voltage regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c
new file mode 100644
index 000000000..713730757
--- /dev/null
+++ b/drivers/regulator/bd9571mwv-regulator.c
@@ -0,0 +1,365 @@
+/*
+ * ROHM BD9571MWV-M regulator driver
+ *
+ * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65086 driver
+ *
+ * NOTE: VD09 is missing
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/bd9571mwv.h>
+
+struct bd9571mwv_reg {
+ struct bd9571mwv *bd;
+
+ /* DDR Backup Power */
+ u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */
+ u8 bkup_mode_cnt_saved;
+ bool bkup_mode_enabled;
+
+ /* Power switch type */
+ bool rstbmode_level;
+ bool rstbmode_pulse;
+};
+
+enum bd9571mwv_regulators { VD09, VD18, VD25, VD33, DVFS };
+
+#define BD9571MWV_REG(_name, _of, _id, _ops, _vr, _vm, _nv, _min, _step, _lmin)\
+ { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .id = _id, \
+ .ops = &_ops, \
+ .n_voltages = _nv, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .min_uV = _min, \
+ .uV_step = _step, \
+ .linear_min_sel = _lmin, \
+ }
+
+static int bd9571mwv_avs_get_moni_state(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, BD9571MWV_AVS_SET_MONI, &val);
+ if (ret != 0)
+ return ret;
+
+ return val & BD9571MWV_AVS_SET_MONI_MASK;
+}
+
+static int bd9571mwv_avs_set_voltage_sel_regmap(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ int ret;
+
+ ret = bd9571mwv_avs_get_moni_state(rdev);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write_bits(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret),
+ rdev->desc->vsel_mask, sel);
+}
+
+static int bd9571mwv_avs_get_voltage_sel_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = bd9571mwv_avs_get_moni_state(rdev);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), &val);
+ if (ret != 0)
+ return ret;
+
+ val &= rdev->desc->vsel_mask;
+ val >>= ffs(rdev->desc->vsel_mask) - 1;
+
+ return val;
+}
+
+static int bd9571mwv_reg_set_voltage_sel_regmap(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ return regmap_write_bits(rdev->regmap, BD9571MWV_DVFS_SETVID,
+ rdev->desc->vsel_mask, sel);
+}
+
+/* Operations permitted on AVS voltage regulator */
+static struct regulator_ops avs_ops = {
+ .set_voltage_sel = bd9571mwv_avs_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = bd9571mwv_avs_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* Operations permitted on voltage regulators */
+static struct regulator_ops reg_ops = {
+ .set_voltage_sel = bd9571mwv_reg_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* Operations permitted on voltage monitors */
+static struct regulator_ops vid_ops = {
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_desc regulators[] = {
+ BD9571MWV_REG("VD09", "vd09", VD09, avs_ops, 0, 0x7f,
+ 0x6f, 600000, 10000, 0x3c),
+ BD9571MWV_REG("VD18", "vd18", VD18, vid_ops, BD9571MWV_VD18_VID, 0xf,
+ 16, 1625000, 25000, 0),
+ BD9571MWV_REG("VD25", "vd25", VD25, vid_ops, BD9571MWV_VD25_VID, 0xf,
+ 16, 2150000, 50000, 0),
+ BD9571MWV_REG("VD33", "vd33", VD33, vid_ops, BD9571MWV_VD33_VID, 0xf,
+ 11, 2800000, 100000, 0),
+ BD9571MWV_REG("DVFS", "dvfs", DVFS, reg_ops,
+ BD9571MWV_DVFS_MONIVDAC, 0x7f,
+ 0x6f, 600000, 10000, 0x3c),
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int bd9571mwv_bkup_mode_read(struct bd9571mwv *bd, unsigned int *mode)
+{
+ int ret;
+
+ ret = regmap_read(bd->regmap, BD9571MWV_BKUP_MODE_CNT, mode);
+ if (ret) {
+ dev_err(bd->dev, "failed to read backup mode (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode)
+{
+ int ret;
+
+ ret = regmap_write(bd->regmap, BD9571MWV_BKUP_MODE_CNT, mode);
+ if (ret) {
+ dev_err(bd->dev, "failed to configure backup mode 0x%x (%d)\n",
+ mode, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t backup_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", bdreg->bkup_mode_enabled ? "on" : "off");
+}
+
+static ssize_t backup_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
+ unsigned int mode;
+ int ret;
+
+ if (!count)
+ return 0;
+
+ ret = kstrtobool(buf, &bdreg->bkup_mode_enabled);
+ if (ret)
+ return ret;
+
+ if (!bdreg->rstbmode_level)
+ return count;
+
+ /*
+ * Configure DDR Backup Mode, to change the role of the accessory power
+ * switch from a power switch to a wake-up switch, or vice versa
+ */
+ ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode);
+ if (ret)
+ return ret;
+
+ mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK;
+ if (bdreg->bkup_mode_enabled)
+ mode |= bdreg->bkup_mode_cnt_keepon;
+
+ ret = bd9571mwv_bkup_mode_write(bdreg->bd, mode);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(backup_mode);
+
+static int bd9571mwv_suspend(struct device *dev)
+{
+ struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
+ unsigned int mode;
+ int ret;
+
+ if (!bdreg->bkup_mode_enabled)
+ return 0;
+
+ /* Save DDR Backup Mode */
+ ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode);
+ if (ret)
+ return ret;
+
+ bdreg->bkup_mode_cnt_saved = mode;
+
+ if (!bdreg->rstbmode_pulse)
+ return 0;
+
+ /* Enable DDR Backup Mode */
+ mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK;
+ mode |= bdreg->bkup_mode_cnt_keepon;
+
+ if (mode != bdreg->bkup_mode_cnt_saved)
+ return bd9571mwv_bkup_mode_write(bdreg->bd, mode);
+
+ return 0;
+}
+
+static int bd9571mwv_resume(struct device *dev)
+{
+ struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
+
+ if (!bdreg->bkup_mode_enabled)
+ return 0;
+
+ /* Restore DDR Backup Mode */
+ return bd9571mwv_bkup_mode_write(bdreg->bd, bdreg->bkup_mode_cnt_saved);
+}
+
+static const struct dev_pm_ops bd9571mwv_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume)
+};
+
+static int bd9571mwv_regulator_remove(struct platform_device *pdev)
+{
+ device_remove_file(&pdev->dev, &dev_attr_backup_mode);
+ return 0;
+}
+#define DEV_PM_OPS &bd9571mwv_pm
+#else
+#define DEV_PM_OPS NULL
+#define bd9571mwv_regulator_remove NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static int bd9571mwv_regulator_probe(struct platform_device *pdev)
+{
+ struct bd9571mwv *bd = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct bd9571mwv_reg *bdreg;
+ struct regulator_dev *rdev;
+ unsigned int val;
+ int i;
+
+ bdreg = devm_kzalloc(&pdev->dev, sizeof(*bdreg), GFP_KERNEL);
+ if (!bdreg)
+ return -ENOMEM;
+
+ bdreg->bd = bd;
+
+ platform_set_drvdata(pdev, bdreg);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = bd->dev->of_node;
+ config.driver_data = bd;
+ config.regmap = bd->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(bd->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ val = 0;
+ of_property_read_u32(bd->dev->of_node, "rohm,ddr-backup-power", &val);
+ if (val & ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK) {
+ dev_err(bd->dev, "invalid %s mode %u\n",
+ "rohm,ddr-backup-power", val);
+ return -EINVAL;
+ }
+ bdreg->bkup_mode_cnt_keepon = val;
+
+ bdreg->rstbmode_level = of_property_read_bool(bd->dev->of_node,
+ "rohm,rstbmode-level");
+ bdreg->rstbmode_pulse = of_property_read_bool(bd->dev->of_node,
+ "rohm,rstbmode-pulse");
+ if (bdreg->rstbmode_level && bdreg->rstbmode_pulse) {
+ dev_err(bd->dev, "only one rohm,rstbmode-* may be specified");
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_PM_SLEEP
+ if (bdreg->bkup_mode_cnt_keepon) {
+ int ret;
+
+ /*
+ * Backup mode is enabled by default in pulse mode, but needs
+ * explicit user setup in level mode.
+ */
+ bdreg->bkup_mode_enabled = bdreg->rstbmode_pulse;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_backup_mode);
+ if (ret)
+ return ret;
+ }
+#endif /* CONFIG_PM_SLEEP */
+
+ return 0;
+}
+
+static const struct platform_device_id bd9571mwv_regulator_id_table[] = {
+ { "bd9571mwv-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table);
+
+static struct platform_driver bd9571mwv_regulator_driver = {
+ .driver = {
+ .name = "bd9571mwv-regulator",
+ .pm = DEV_PM_OPS,
+ },
+ .probe = bd9571mwv_regulator_probe,
+ .remove = bd9571mwv_regulator_remove,
+ .id_table = bd9571mwv_regulator_id_table,
+};
+module_platform_driver(bd9571mwv_regulator_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>");
+MODULE_DESCRIPTION("BD9571MWV Regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
new file mode 100644
index 000000000..045075cd2
--- /dev/null
+++ b/drivers/regulator/core.c
@@ -0,0 +1,4958 @@
+/*
+ * core.c -- Voltage/Current Regulator framework.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008 SlimLogic Ltd.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/async.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/suspend.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/regulator.h>
+
+#include "dummy.h"
+#include "internal.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_map_list);
+static LIST_HEAD(regulator_ena_gpio_list);
+static LIST_HEAD(regulator_supply_alias_list);
+static bool has_full_constraints;
+
+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;
+ struct gpio_desc *gpiod;
+ 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_supply_alias
+ *
+ * Used to map lookups for a supply onto an alternative device.
+ */
+struct regulator_supply_alias {
+ struct list_head list;
+ struct device *src_dev;
+ const char *src_supply;
+ struct device *alias_dev;
+ const char *alias_supply;
+};
+
+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 int _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 void _regulator_put(struct regulator *regulator);
+
+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 "";
+}
+
+static bool have_full_constraints(void)
+{
+ return has_full_constraints || of_have_populated_dt();
+}
+
+static bool regulator_ops_is_valid(struct regulator_dev *rdev, int ops)
+{
+ if (!rdev->constraints) {
+ rdev_err(rdev, "no constraints\n");
+ return false;
+ }
+
+ if (rdev->constraints->valid_ops_mask & ops)
+ return true;
+
+ return false;
+}
+
+static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
+{
+ if (rdev && rdev->supply)
+ return rdev->supply->rdev;
+
+ return NULL;
+}
+
+/**
+ * regulator_lock_nested - lock a single regulator
+ * @rdev: regulator source
+ * @subclass: mutex subclass used for lockdep
+ *
+ * This function can be called many times by one task on
+ * a single regulator and its mutex will be locked only
+ * once. If a task, which is calling this function is other
+ * than the one, which initially locked the mutex, it will
+ * wait on mutex.
+ */
+static void regulator_lock_nested(struct regulator_dev *rdev,
+ unsigned int subclass)
+{
+ if (!mutex_trylock(&rdev->mutex)) {
+ if (rdev->mutex_owner == current) {
+ rdev->ref_cnt++;
+ return;
+ }
+ mutex_lock_nested(&rdev->mutex, subclass);
+ }
+
+ rdev->ref_cnt = 1;
+ rdev->mutex_owner = current;
+}
+
+static inline void regulator_lock(struct regulator_dev *rdev)
+{
+ regulator_lock_nested(rdev, 0);
+}
+
+/**
+ * regulator_unlock - unlock a single regulator
+ * @rdev: regulator_source
+ *
+ * This function unlocks the mutex when the
+ * reference counter reaches 0.
+ */
+static void regulator_unlock(struct regulator_dev *rdev)
+{
+ if (rdev->ref_cnt != 0) {
+ rdev->ref_cnt--;
+
+ if (!rdev->ref_cnt) {
+ rdev->mutex_owner = NULL;
+ mutex_unlock(&rdev->mutex);
+ }
+ }
+}
+
+/**
+ * regulator_lock_supply - lock a regulator and its supplies
+ * @rdev: regulator source
+ */
+static void regulator_lock_supply(struct regulator_dev *rdev)
+{
+ int i;
+
+ for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++)
+ regulator_lock_nested(rdev, i);
+}
+
+/**
+ * regulator_unlock_supply - unlock a regulator and its supplies
+ * @rdev: regulator source
+ */
+static void regulator_unlock_supply(struct regulator_dev *rdev)
+{
+ struct regulator *supply;
+
+ while (1) {
+ regulator_unlock(rdev);
+ supply = rdev->supply;
+
+ if (!rdev->supply)
+ return;
+
+ rdev = supply->rdev;
+ }
+}
+
+/**
+ * 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 %pOF failed\n",
+ prop_name, dev->of_node);
+ return NULL;
+ }
+ return regnode;
+}
+
+/* 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 (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
+ rdev_err(rdev, "voltage 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;
+}
+
+/* return 0 if the state is valid */
+static int regulator_check_states(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_MAX || state == PM_SUSPEND_TO_IDLE);
+}
+
+/* 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,
+ suspend_state_t state)
+{
+ struct regulator *regulator;
+ struct regulator_voltage *voltage;
+
+ list_for_each_entry(regulator, &rdev->consumer_list, list) {
+ voltage = &regulator->voltage[state];
+ /*
+ * Assume consumers that didn't say anything are OK
+ * with anything in the constraint range.
+ */
+ if (!voltage->min_uV && !voltage->max_uV)
+ continue;
+
+ if (*max_uV > voltage->max_uV)
+ *max_uV = voltage->max_uV;
+ if (*min_uV < voltage->min_uV)
+ *min_uV = voltage->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 (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_CURRENT)) {
+ rdev_err(rdev, "current 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,
+ unsigned 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 (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_MODE)) {
+ rdev_err(rdev, "mode 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;
+}
+
+static inline struct regulator_state *
+regulator_get_suspend_state(struct regulator_dev *rdev, suspend_state_t state)
+{
+ if (rdev->constraints == NULL)
+ return NULL;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ return &rdev->constraints->state_standby;
+ case PM_SUSPEND_MEM:
+ return &rdev->constraints->state_mem;
+ case PM_SUSPEND_MAX:
+ return &rdev->constraints->state_disk;
+ default:
+ return NULL;
+ }
+}
+
+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;
+
+ regulator_lock(rdev);
+ ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev));
+ regulator_unlock(rdev);
+
+ 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 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 DEVICE_ATTR_RO(name);
+
+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;
+
+ regulator_lock(rdev);
+ ret = regulator_print_state(buf, _regulator_is_enabled(rdev));
+ regulator_unlock(rdev);
+
+ 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;
+
+ regulator_lock(rdev);
+ list_for_each_entry(regulator, &rdev->consumer_list, list)
+ uA += regulator->uA_load;
+ regulator_unlock(rdev);
+ return sprintf(buf, "%d\n", uA);
+}
+static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL);
+
+static ssize_t 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 DEVICE_ATTR_RO(num_users);
+
+static ssize_t 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 DEVICE_ATTR_RO(type);
+
+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);
+
+/* Calculate the new optimum regulator operating mode based on the new total
+ * consumer load. All locks held by caller */
+static int drms_uA_update(struct regulator_dev *rdev)
+{
+ struct regulator *sibling;
+ int current_uA = 0, output_uV, input_uV, err;
+ unsigned int mode;
+
+ lockdep_assert_held_once(&rdev->mutex);
+
+ /*
+ * first check to see if we can set modes at all, otherwise just
+ * tell the consumer everything is OK.
+ */
+ if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS))
+ return 0;
+
+ if (!rdev->desc->ops->get_optimum_mode &&
+ !rdev->desc->ops->set_load)
+ return 0;
+
+ if (!rdev->desc->ops->set_mode &&
+ !rdev->desc->ops->set_load)
+ return -EINVAL;
+
+ /* calc total requested load */
+ list_for_each_entry(sibling, &rdev->consumer_list, list)
+ current_uA += sibling->uA_load;
+
+ current_uA += rdev->constraints->system_load;
+
+ if (rdev->desc->ops->set_load) {
+ /* set the optimum mode for our new total regulator load */
+ err = rdev->desc->ops->set_load(rdev, current_uA);
+ if (err < 0)
+ rdev_err(rdev, "failed to set load %d\n", current_uA);
+ } else {
+ /* get output voltage */
+ output_uV = _regulator_get_voltage(rdev);
+ if (output_uV <= 0) {
+ rdev_err(rdev, "invalid output voltage found\n");
+ return -EINVAL;
+ }
+
+ /* 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) {
+ rdev_err(rdev, "invalid input voltage found\n");
+ return -EINVAL;
+ }
+
+ /* 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_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n",
+ current_uA, input_uV, output_uV);
+ return err;
+ }
+
+ err = rdev->desc->ops->set_mode(rdev, mode);
+ if (err < 0)
+ rdev_err(rdev, "failed to set optimum mode %x\n", mode);
+ }
+
+ return err;
+}
+
+static int suspend_set_state(struct regulator_dev *rdev,
+ suspend_state_t state)
+{
+ int ret = 0;
+ struct regulator_state *rstate;
+
+ rstate = regulator_get_suspend_state(rdev, state);
+ if (rstate == NULL)
+ return 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 != ENABLE_IN_SUSPEND &&
+ rstate->enabled != DISABLE_IN_SUSPEND) {
+ if (rdev->desc->ops->set_suspend_voltage ||
+ rdev->desc->ops->set_suspend_mode)
+ rdev_warn(rdev, "No configuration\n");
+ return 0;
+ }
+
+ if (rstate->enabled == ENABLE_IN_SUSPEND &&
+ rdev->desc->ops->set_suspend_enable)
+ ret = rdev->desc->ops->set_suspend_enable(rdev);
+ else if (rstate->enabled == DISABLE_IN_SUSPEND &&
+ 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;
+}
+
+static void print_constraints(struct regulator_dev *rdev)
+{
+ struct regulation_constraints *constraints = rdev->constraints;
+ char buf[160] = "";
+ size_t len = sizeof(buf) - 1;
+ int count = 0;
+ int ret;
+
+ if (constraints->min_uV && constraints->max_uV) {
+ if (constraints->min_uV == constraints->max_uV)
+ count += scnprintf(buf + count, len - count, "%d mV ",
+ constraints->min_uV / 1000);
+ else
+ count += scnprintf(buf + count, len - 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 += scnprintf(buf + count, len - count,
+ "at %d mV ", ret / 1000);
+ }
+
+ if (constraints->uV_offset)
+ count += scnprintf(buf + count, len - count, "%dmV offset ",
+ constraints->uV_offset / 1000);
+
+ if (constraints->min_uA && constraints->max_uA) {
+ if (constraints->min_uA == constraints->max_uA)
+ count += scnprintf(buf + count, len - count, "%d mA ",
+ constraints->min_uA / 1000);
+ else
+ count += scnprintf(buf + count, len - 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 += scnprintf(buf + count, len - count,
+ "at %d mA ", ret / 1000);
+ }
+
+ if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
+ count += scnprintf(buf + count, len - count, "fast ");
+ if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
+ count += scnprintf(buf + count, len - count, "normal ");
+ if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
+ count += scnprintf(buf + count, len - count, "idle ");
+ if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
+ count += scnprintf(buf + count, len - count, "standby");
+
+ if (!count)
+ scnprintf(buf, len, "no parameters");
+
+ rdev_dbg(rdev, "%s\n", buf);
+
+ if ((constraints->min_uV != constraints->max_uV) &&
+ !regulator_ops_is_valid(rdev, 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)
+{
+ const 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) {
+ int target_min, target_max;
+ int current_uV = _regulator_get_voltage(rdev);
+
+ if (current_uV == -ENOTRECOVERABLE) {
+ /* This regulator can't be read and must be initted */
+ rdev_info(rdev, "Setting %d-%duV\n",
+ rdev->constraints->min_uV,
+ rdev->constraints->max_uV);
+ _regulator_do_set_voltage(rdev,
+ rdev->constraints->min_uV,
+ rdev->constraints->max_uV);
+ current_uV = _regulator_get_voltage(rdev);
+ }
+
+ if (current_uV < 0) {
+ rdev_err(rdev,
+ "failed to get the current voltage(%d)\n",
+ current_uV);
+ return current_uV;
+ }
+
+ /*
+ * If we're below the minimum voltage move up to the
+ * minimum voltage, if we're above the maximum voltage
+ * then move down to the maximum.
+ */
+ target_min = current_uV;
+ target_max = current_uV;
+
+ if (current_uV < rdev->constraints->min_uV) {
+ target_min = rdev->constraints->min_uV;
+ target_max = rdev->constraints->min_uV;
+ }
+
+ if (current_uV > rdev->constraints->max_uV) {
+ target_min = rdev->constraints->max_uV;
+ target_max = rdev->constraints->max_uV;
+ }
+
+ if (target_min != current_uV || target_max != current_uV) {
+ rdev_info(rdev, "Bringing %duV into %d-%duV\n",
+ current_uV, target_min, target_max);
+ ret = _regulator_do_set_voltage(
+ rdev, target_min, target_max);
+ if (ret < 0) {
+ rdev_err(rdev,
+ "failed to apply %d-%duV constraint(%d)\n",
+ target_min, target_max, ret);
+ 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;
+}
+
+static int machine_constraints_current(struct regulator_dev *rdev,
+ struct regulation_constraints *constraints)
+{
+ const struct regulator_ops *ops = rdev->desc->ops;
+ int ret;
+
+ if (!constraints->min_uA && !constraints->max_uA)
+ return 0;
+
+ if (constraints->min_uA > constraints->max_uA) {
+ rdev_err(rdev, "Invalid current constraints\n");
+ return -EINVAL;
+ }
+
+ if (!ops->set_current_limit || !ops->get_current_limit) {
+ rdev_warn(rdev, "Operation of current configuration missing\n");
+ return 0;
+ }
+
+ /* Set regulator current in constraints range */
+ ret = ops->set_current_limit(rdev, constraints->min_uA,
+ constraints->max_uA);
+ if (ret < 0) {
+ rdev_err(rdev, "Failed to set current constraint, %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int _regulator_do_enable(struct regulator_dev *rdev);
+
+/**
+ * set_machine_constraints - sets regulator constraints
+ * @rdev: regulator source
+ *
+ * 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)
+{
+ int ret = 0;
+ const struct regulator_ops *ops = rdev->desc->ops;
+
+ ret = machine_constraints_voltage(rdev, rdev->constraints);
+ if (ret != 0)
+ return ret;
+
+ ret = machine_constraints_current(rdev, rdev->constraints);
+ if (ret != 0)
+ return ret;
+
+ if (rdev->constraints->ilim_uA && ops->set_input_current_limit) {
+ ret = ops->set_input_current_limit(rdev,
+ rdev->constraints->ilim_uA);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set input limit\n");
+ return ret;
+ }
+ }
+
+ /* do we need to setup our suspend state */
+ if (rdev->constraints->initial_state) {
+ ret = suspend_set_state(rdev, rdev->constraints->initial_state);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set suspend state\n");
+ return ret;
+ }
+ }
+
+ if (rdev->constraints->initial_mode) {
+ if (!ops->set_mode) {
+ rdev_err(rdev, "no set_mode operation\n");
+ return -EINVAL;
+ }
+
+ ret = ops->set_mode(rdev, rdev->constraints->initial_mode);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set initial mode: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable)
+ && 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");
+ return ret;
+ }
+ }
+
+ if (rdev->constraints->pull_down && ops->set_pull_down) {
+ ret = ops->set_pull_down(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set pull down\n");
+ return ret;
+ }
+ }
+
+ if (rdev->constraints->soft_start && ops->set_soft_start) {
+ ret = ops->set_soft_start(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set soft start\n");
+ return ret;
+ }
+ }
+
+ if (rdev->constraints->over_current_protection
+ && ops->set_over_current_protection) {
+ ret = ops->set_over_current_protection(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set over current protection\n");
+ return ret;
+ }
+ }
+
+ if (rdev->constraints->active_discharge && ops->set_active_discharge) {
+ bool ad_state = (rdev->constraints->active_discharge ==
+ REGULATOR_ACTIVE_DISCHARGE_ENABLE) ? true : false;
+
+ ret = ops->set_active_discharge(rdev, ad_state);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set active discharge\n");
+ return ret;
+ }
+ }
+
+ /* 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) {
+ /* If we want to enable this regulator, make sure that we know
+ * the supplying regulator.
+ */
+ if (rdev->supply_name && !rdev->supply)
+ return -EPROBE_DEFER;
+
+ if (rdev->supply) {
+ ret = regulator_enable(rdev->supply);
+ if (ret < 0) {
+ _regulator_put(rdev->supply);
+ rdev->supply = NULL;
+ return ret;
+ }
+ }
+
+ ret = _regulator_do_enable(rdev);
+ if (ret < 0 && ret != -EINVAL) {
+ rdev_err(rdev, "failed to enable\n");
+ return ret;
+ }
+
+ if (rdev->constraints->always_on)
+ rdev->use_count++;
+ }
+
+ print_constraints(rdev);
+ return 0;
+}
+
+/**
+ * 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));
+
+ if (!try_module_get(supply_rdev->owner))
+ return -ENODEV;
+
+ 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, *new_node;
+ int has_dev;
+
+ if (supply == NULL)
+ return -EINVAL;
+
+ if (consumer_dev_name != NULL)
+ has_dev = 1;
+ else
+ has_dev = 0;
+
+ new_node = kzalloc(sizeof(struct regulator_map), GFP_KERNEL);
+ if (new_node == NULL)
+ return -ENOMEM;
+
+ new_node->regulator = rdev;
+ new_node->supply = supply;
+
+ if (has_dev) {
+ new_node->dev_name = kstrdup(consumer_dev_name, GFP_KERNEL);
+ if (new_node->dev_name == NULL) {
+ kfree(new_node);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&regulator_list_mutex);
+ list_for_each_entry(node, &regulator_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));
+ goto fail;
+ }
+
+ list_add(&new_node->list, &regulator_map_list);
+ mutex_unlock(&regulator_list_mutex);
+
+ return 0;
+
+fail:
+ mutex_unlock(&regulator_list_mutex);
+ kfree(new_node->dev_name);
+ kfree(new_node);
+ return -EBUSY;
+}
+
+static void unset_regulator_supplies(struct regulator_dev *rdev)
+{
+ struct regulator_map *node, *n;
+
+ list_for_each_entry_safe(node, n, &regulator_map_list, list) {
+ if (rdev == node->regulator) {
+ list_del(&node->list);
+ kfree(node->dev_name);
+ kfree(node);
+ }
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t constraint_flags_read_file(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const struct regulator *regulator = file->private_data;
+ const struct regulation_constraints *c = regulator->rdev->constraints;
+ char *buf;
+ ssize_t ret;
+
+ if (!c)
+ return 0;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = snprintf(buf, PAGE_SIZE,
+ "always_on: %u\n"
+ "boot_on: %u\n"
+ "apply_uV: %u\n"
+ "ramp_disable: %u\n"
+ "soft_start: %u\n"
+ "pull_down: %u\n"
+ "over_current_protection: %u\n",
+ c->always_on,
+ c->boot_on,
+ c->apply_uV,
+ c->ramp_disable,
+ c->soft_start,
+ c->pull_down,
+ c->over_current_protection);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+
+ return ret;
+}
+
+#endif
+
+static const struct file_operations constraint_flags_fops = {
+#ifdef CONFIG_DEBUG_FS
+ .open = simple_open,
+ .read = constraint_flags_read_file,
+ .llseek = default_llseek,
+#endif
+};
+
+#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;
+
+ regulator_lock(rdev);
+ regulator->rdev = rdev;
+ list_add(&regulator->list, &rdev->consumer_list);
+
+ if (dev) {
+ regulator->dev = dev;
+
+ /* Add a link to the device sysfs entry */
+ size = snprintf(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_nowarn(&rdev->dev.kobj, &dev->kobj,
+ buf);
+ if (err) {
+ rdev_dbg(rdev, "could not add device link %s err %d\n",
+ dev->kobj.name, err);
+ /* non-fatal */
+ }
+ } else {
+ regulator->supply_name = kstrdup_const(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_dbg(rdev, "Failed to create debugfs directory\n");
+ } else {
+ debugfs_create_u32("uA_load", 0444, regulator->debugfs,
+ &regulator->uA_load);
+ debugfs_create_u32("min_uV", 0444, regulator->debugfs,
+ &regulator->voltage[PM_SUSPEND_ON].min_uV);
+ debugfs_create_u32("max_uV", 0444, regulator->debugfs,
+ &regulator->voltage[PM_SUSPEND_ON].max_uV);
+ debugfs_create_file("constraint_flags", 0444,
+ regulator->debugfs, regulator,
+ &constraint_flags_fops);
+ }
+
+ /*
+ * 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_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS) &&
+ _regulator_is_enabled(rdev))
+ regulator->always_on = true;
+
+ regulator_unlock(rdev);
+ return regulator;
+overflow_err:
+ list_del(&regulator->list);
+ kfree(regulator);
+ regulator_unlock(rdev);
+ return NULL;
+}
+
+static int _regulator_get_enable_time(struct regulator_dev *rdev)
+{
+ if (rdev->constraints && rdev->constraints->enable_time)
+ return rdev->constraints->enable_time;
+ if (!rdev->desc->ops->enable_time)
+ return rdev->desc->enable_time;
+ return rdev->desc->ops->enable_time(rdev);
+}
+
+static struct regulator_supply_alias *regulator_find_supply_alias(
+ struct device *dev, const char *supply)
+{
+ struct regulator_supply_alias *map;
+
+ list_for_each_entry(map, &regulator_supply_alias_list, list)
+ if (map->src_dev == dev && strcmp(map->src_supply, supply) == 0)
+ return map;
+
+ return NULL;
+}
+
+static void regulator_supply_alias(struct device **dev, const char **supply)
+{
+ struct regulator_supply_alias *map;
+
+ map = regulator_find_supply_alias(*dev, *supply);
+ if (map) {
+ dev_dbg(*dev, "Mapping supply %s to %s,%s\n",
+ *supply, map->alias_supply,
+ dev_name(map->alias_dev));
+ *dev = map->alias_dev;
+ *supply = map->alias_supply;
+ }
+}
+
+static int regulator_match(struct device *dev, const void *data)
+{
+ struct regulator_dev *r = dev_to_rdev(dev);
+
+ return strcmp(rdev_get_name(r), data) == 0;
+}
+
+static struct regulator_dev *regulator_lookup_by_name(const char *name)
+{
+ struct device *dev;
+
+ dev = class_find_device(&regulator_class, NULL, name, regulator_match);
+
+ return dev ? dev_to_rdev(dev) : NULL;
+}
+
+/**
+ * regulator_dev_lookup - lookup a regulator device.
+ * @dev: device for regulator "consumer".
+ * @supply: Supply name or regulator ID.
+ *
+ * If successful, returns a struct regulator_dev that corresponds to the name
+ * @supply and with the embedded struct device refcount incremented by one.
+ * The refcount must be dropped by calling put_device().
+ * On failure one of the following ERR-PTR-encoded values is returned:
+ * -ENODEV if lookup fails permanently, -EPROBE_DEFER if lookup could succeed
+ * in the future.
+ */
+static struct regulator_dev *regulator_dev_lookup(struct device *dev,
+ const char *supply)
+{
+ struct regulator_dev *r = NULL;
+ struct device_node *node;
+ struct regulator_map *map;
+ const char *devname = NULL;
+
+ regulator_supply_alias(&dev, &supply);
+
+ /* first do a dt based lookup */
+ if (dev && dev->of_node) {
+ node = of_get_regulator(dev, supply);
+ if (node) {
+ r = of_find_regulator_by_node(node);
+ if (r)
+ return r;
+
+ /*
+ * We have a node, but there is no device.
+ * assume it has not registered yet.
+ */
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+ }
+
+ /* if not found, try doing it non-dt way */
+ if (dev)
+ devname = dev_name(dev);
+
+ mutex_lock(&regulator_list_mutex);
+ list_for_each_entry(map, &regulator_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 &&
+ get_device(&map->regulator->dev)) {
+ r = map->regulator;
+ break;
+ }
+ }
+ mutex_unlock(&regulator_list_mutex);
+
+ if (r)
+ return r;
+
+ r = regulator_lookup_by_name(supply);
+ if (r)
+ return r;
+
+ return ERR_PTR(-ENODEV);
+}
+
+static int regulator_resolve_supply(struct regulator_dev *rdev)
+{
+ struct regulator_dev *r;
+ struct device *dev = rdev->dev.parent;
+ int ret = 0;
+
+ /* No supply to resovle? */
+ if (!rdev->supply_name)
+ return 0;
+
+ /* Supply already resolved? (fast-path without locking contention) */
+ if (rdev->supply)
+ return 0;
+
+ r = regulator_dev_lookup(dev, rdev->supply_name);
+ if (IS_ERR(r)) {
+ ret = PTR_ERR(r);
+
+ /* Did the lookup explicitly defer for us? */
+ if (ret == -EPROBE_DEFER)
+ goto out;
+
+ if (have_full_constraints()) {
+ r = dummy_regulator_rdev;
+ get_device(&r->dev);
+ } else {
+ dev_err(dev, "Failed to resolve %s-supply for %s\n",
+ rdev->supply_name, rdev->desc->name);
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
+ }
+
+ if (r == rdev) {
+ dev_err(dev, "Supply for %s (%s) resolved to itself\n",
+ rdev->desc->name, rdev->supply_name);
+ if (!have_full_constraints()) {
+ ret = -EINVAL;
+ goto out;
+ }
+ r = dummy_regulator_rdev;
+ get_device(&r->dev);
+ }
+
+ /*
+ * If the supply's parent device is not the same as the
+ * regulator's parent device, then ensure the parent device
+ * is bound before we resolve the supply, in case the parent
+ * device get probe deferred and unregisters the supply.
+ */
+ if (r->dev.parent && r->dev.parent != rdev->dev.parent) {
+ if (!device_is_bound(r->dev.parent)) {
+ put_device(&r->dev);
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
+ }
+
+ /* Recursively resolve the supply of the supply */
+ ret = regulator_resolve_supply(r);
+ if (ret < 0) {
+ put_device(&r->dev);
+ goto out;
+ }
+
+ /*
+ * Recheck rdev->supply with rdev->mutex lock held to avoid a race
+ * between rdev->supply null check and setting rdev->supply in
+ * set_supply() from concurrent tasks.
+ */
+ regulator_lock(rdev);
+
+ /* Supply just resolved by a concurrent task? */
+ if (rdev->supply) {
+ regulator_unlock(rdev);
+ put_device(&r->dev);
+ goto out;
+ }
+
+ ret = set_supply(rdev, r);
+ if (ret < 0) {
+ regulator_unlock(rdev);
+ put_device(&r->dev);
+ goto out;
+ }
+
+ regulator_unlock(rdev);
+
+ /*
+ * In set_machine_constraints() we may have turned this regulator on
+ * but we couldn't propagate to the supply if it hadn't been resolved
+ * yet. Do it now.
+ */
+ if (rdev->use_count) {
+ ret = regulator_enable(rdev->supply);
+ if (ret < 0) {
+ _regulator_put(rdev->supply);
+ rdev->supply = NULL;
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+/* Internal regulator request function */
+struct regulator *_regulator_get(struct device *dev, const char *id,
+ enum regulator_get_type get_type)
+{
+ struct regulator_dev *rdev;
+ struct regulator *regulator;
+ const char *devname = dev ? dev_name(dev) : "deviceless";
+ int ret;
+
+ if (get_type >= MAX_GET_TYPE) {
+ dev_err(dev, "invalid type %d in %s\n", get_type, __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (id == NULL) {
+ pr_err("get() with no identifier\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rdev = regulator_dev_lookup(dev, id);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+
+ /*
+ * If regulator_dev_lookup() fails with error other
+ * than -ENODEV our job here is done, we simply return it.
+ */
+ if (ret != -ENODEV)
+ return ERR_PTR(ret);
+
+ if (!have_full_constraints()) {
+ dev_warn(dev,
+ "incomplete constraints, dummy supplies not allowed\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ switch (get_type) {
+ case NORMAL_GET:
+ /*
+ * Assume that a regulator is physically present and
+ * enabled, even if it isn't hooked up, and just
+ * provide a dummy.
+ */
+ dev_warn(dev,
+ "%s supply %s not found, using dummy regulator\n",
+ devname, id);
+ rdev = dummy_regulator_rdev;
+ get_device(&rdev->dev);
+ break;
+
+ case EXCLUSIVE_GET:
+ dev_warn(dev,
+ "dummy supplies not allowed for exclusive requests\n");
+ /* fall through */
+
+ default:
+ return ERR_PTR(-ENODEV);
+ }
+ }
+
+ if (rdev->exclusive) {
+ regulator = ERR_PTR(-EPERM);
+ put_device(&rdev->dev);
+ return regulator;
+ }
+
+ if (get_type == EXCLUSIVE_GET && rdev->open_count) {
+ regulator = ERR_PTR(-EBUSY);
+ put_device(&rdev->dev);
+ return regulator;
+ }
+
+ ret = regulator_resolve_supply(rdev);
+ if (ret < 0) {
+ regulator = ERR_PTR(ret);
+ put_device(&rdev->dev);
+ return regulator;
+ }
+
+ if (!try_module_get(rdev->owner)) {
+ regulator = ERR_PTR(-EPROBE_DEFER);
+ put_device(&rdev->dev);
+ return regulator;
+ }
+
+ regulator = create_regulator(rdev, dev, id);
+ if (regulator == NULL) {
+ regulator = ERR_PTR(-ENOMEM);
+ module_put(rdev->owner);
+ put_device(&rdev->dev);
+ return regulator;
+ }
+
+ rdev->open_count++;
+ if (get_type == EXCLUSIVE_GET) {
+ rdev->exclusive = 1;
+
+ ret = _regulator_is_enabled(rdev);
+ if (ret > 0)
+ rdev->use_count = 1;
+ else
+ rdev->use_count = 0;
+ }
+
+ device_link_add(dev, &rdev->dev, DL_FLAG_STATELESS);
+
+ 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, NORMAL_GET);
+}
+EXPORT_SYMBOL_GPL(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 regulator while 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, EXCLUSIVE_GET);
+}
+EXPORT_SYMBOL_GPL(regulator_get_exclusive);
+
+/**
+ * regulator_get_optional - obtain optional 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.
+ *
+ * This is intended for use by consumers for devices which can have
+ * some supplies unconnected in normal use, such as some MMC devices.
+ * It can allow the regulator core to provide stub supplies for other
+ * supplies requested using normal regulator_get() calls without
+ * disrupting the operation of drivers that can handle absent
+ * supplies.
+ *
+ * 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_optional(struct device *dev, const char *id)
+{
+ return _regulator_get(dev, id, OPTIONAL_GET);
+}
+EXPORT_SYMBOL_GPL(regulator_get_optional);
+
+/* regulator_list_mutex lock held by regulator_put() */
+static void _regulator_put(struct regulator *regulator)
+{
+ struct regulator_dev *rdev;
+
+ if (IS_ERR_OR_NULL(regulator))
+ return;
+
+ lockdep_assert_held_once(&regulator_list_mutex);
+
+ rdev = regulator->rdev;
+
+ debugfs_remove_recursive(regulator->debugfs);
+
+ if (regulator->dev) {
+ int count = 0;
+ struct regulator *r;
+
+ list_for_each_entry(r, &rdev->consumer_list, list)
+ if (r->dev == regulator->dev)
+ count++;
+
+ if (count == 1)
+ device_link_remove(regulator->dev, &rdev->dev);
+
+ /* remove any sysfs entries */
+ sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
+ }
+
+ regulator_lock(rdev);
+ list_del(&regulator->list);
+
+ rdev->open_count--;
+ rdev->exclusive = 0;
+ regulator_unlock(rdev);
+
+ kfree_const(regulator->supply_name);
+ kfree(regulator);
+
+ module_put(rdev->owner);
+ put_device(&rdev->dev);
+}
+
+/**
+ * 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(&regulator_list_mutex);
+ _regulator_put(regulator);
+ mutex_unlock(&regulator_list_mutex);
+}
+EXPORT_SYMBOL_GPL(regulator_put);
+
+/**
+ * regulator_register_supply_alias - Provide device alias for supply lookup
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: Supply name or regulator ID
+ * @alias_dev: device that should be used to lookup the supply
+ * @alias_id: Supply name or regulator ID that should be used to lookup the
+ * supply
+ *
+ * All lookups for id on dev will instead be conducted for alias_id on
+ * alias_dev.
+ */
+int regulator_register_supply_alias(struct device *dev, const char *id,
+ struct device *alias_dev,
+ const char *alias_id)
+{
+ struct regulator_supply_alias *map;
+
+ map = regulator_find_supply_alias(dev, id);
+ if (map)
+ return -EEXIST;
+
+ map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ map->src_dev = dev;
+ map->src_supply = id;
+ map->alias_dev = alias_dev;
+ map->alias_supply = alias_id;
+
+ list_add(&map->list, &regulator_supply_alias_list);
+
+ pr_info("Adding alias for supply %s,%s -> %s,%s\n",
+ id, dev_name(dev), alias_id, dev_name(alias_dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_register_supply_alias);
+
+/**
+ * regulator_unregister_supply_alias - Remove device alias
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: Supply name or regulator ID
+ *
+ * Remove a lookup alias if one exists for id on dev.
+ */
+void regulator_unregister_supply_alias(struct device *dev, const char *id)
+{
+ struct regulator_supply_alias *map;
+
+ map = regulator_find_supply_alias(dev, id);
+ if (map) {
+ list_del(&map->list);
+ kfree(map);
+ }
+}
+EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias);
+
+/**
+ * regulator_bulk_register_supply_alias - register multiple aliases
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: List of supply names or regulator IDs
+ * @alias_dev: device that should be used to lookup the supply
+ * @alias_id: List of supply names or regulator IDs that should be used to
+ * lookup the supply
+ * @num_id: Number of aliases to register
+ *
+ * @return 0 on success, an errno on failure.
+ *
+ * This helper function allows drivers to register several supply
+ * aliases in one operation. If any of the aliases cannot be
+ * registered any aliases that were registered will be removed
+ * before returning to the caller.
+ */
+int regulator_bulk_register_supply_alias(struct device *dev,
+ const char *const *id,
+ struct device *alias_dev,
+ const char *const *alias_id,
+ int num_id)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < num_id; ++i) {
+ ret = regulator_register_supply_alias(dev, id[i], alias_dev,
+ alias_id[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_err(dev,
+ "Failed to create supply alias %s,%s -> %s,%s\n",
+ id[i], dev_name(dev), alias_id[i], dev_name(alias_dev));
+
+ while (--i >= 0)
+ regulator_unregister_supply_alias(dev, id[i]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_register_supply_alias);
+
+/**
+ * regulator_bulk_unregister_supply_alias - unregister multiple aliases
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: List of supply names or regulator IDs
+ * @num_id: Number of aliases to unregister
+ *
+ * This helper function allows drivers to unregister several supply
+ * aliases in one operation.
+ */
+void regulator_bulk_unregister_supply_alias(struct device *dev,
+ const char *const *id,
+ int num_id)
+{
+ int i;
+
+ for (i = 0; i < num_id; ++i)
+ regulator_unregister_supply_alias(dev, id[i]);
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_unregister_supply_alias);
+
+
+/* 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;
+ struct gpio_desc *gpiod;
+ int ret;
+
+ if (config->ena_gpiod)
+ gpiod = config->ena_gpiod;
+ else
+ gpiod = gpio_to_desc(config->ena_gpio);
+
+ list_for_each_entry(pin, &regulator_ena_gpio_list, list) {
+ if (pin->gpiod == gpiod) {
+ rdev_dbg(rdev, "GPIO %d is already used\n",
+ config->ena_gpio);
+ goto update_ena_gpio_to_rdev;
+ }
+ }
+
+ if (!config->ena_gpiod) {
+ 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) {
+ if (!config->ena_gpiod)
+ gpio_free(config->ena_gpio);
+ return -ENOMEM;
+ }
+
+ pin->gpiod = gpiod;
+ pin->ena_gpio_invert = config->ena_gpio_invert;
+ list_add(&pin->list, &regulator_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, &regulator_ena_gpio_list, list) {
+ if (pin->gpiod == rdev->ena_pin->gpiod) {
+ if (pin->request_count <= 1) {
+ pin->request_count = 0;
+ gpiod_put(pin->gpiod);
+ list_del(&pin->list);
+ kfree(pin);
+ rdev->ena_pin = NULL;
+ return;
+ } 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)
+ gpiod_set_value_cansleep(pin->gpiod,
+ !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) {
+ gpiod_set_value_cansleep(pin->gpiod,
+ pin->ena_gpio_invert);
+ pin->enable_count = 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * _regulator_enable_delay - a delay helper function
+ * @delay: time to delay in microseconds
+ *
+ * Delay for the requested amount of time as per the guidelines in:
+ *
+ * Documentation/timers/timers-howto.txt
+ *
+ * The assumption here is that regulators will never be enabled in
+ * atomic context and therefore sleeping functions can be used.
+ */
+static void _regulator_enable_delay(unsigned int delay)
+{
+ unsigned int ms = delay / 1000;
+ unsigned int us = delay % 1000;
+
+ if (ms > 0) {
+ /*
+ * For small enough values, handle super-millisecond
+ * delays in the usleep_range() call below.
+ */
+ if (ms < 20)
+ us += ms * 1000;
+ else
+ msleep(ms);
+ }
+
+ /*
+ * Give the scheduler some room to coalesce with any other
+ * wakeup sources. For delays shorter than 10 us, don't even
+ * bother setting up high-resolution timers and just busy-
+ * loop.
+ */
+ if (us >= 10)
+ usleep_range(us, us + 100);
+ else
+ udelay(us);
+}
+
+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->desc->off_on_delay) {
+ /* if needed, keep a distance of off_on_delay from last time
+ * this regulator was disabled.
+ */
+ unsigned long start_jiffy = jiffies;
+ unsigned long intended, max_delay, remaining;
+
+ max_delay = usecs_to_jiffies(rdev->desc->off_on_delay);
+ intended = rdev->last_off_jiffy + max_delay;
+
+ if (time_before(start_jiffy, intended)) {
+ /* calc remaining jiffies to deal with one-time
+ * timer wrapping.
+ * in case of multiple timer wrapping, either it can be
+ * detected by out-of-range remaining, or it cannot be
+ * detected and we gets a panelty of
+ * _regulator_enable_delay().
+ */
+ remaining = intended - start_jiffy;
+ if (remaining <= max_delay)
+ _regulator_enable_delay(
+ jiffies_to_usecs(remaining));
+ }
+ }
+
+ if (rdev->ena_pin) {
+ if (!rdev->ena_gpio_state) {
+ 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));
+
+ _regulator_enable_delay(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;
+
+ lockdep_assert_held_once(&rdev->mutex);
+
+ /* check voltage and requested load before enabling */
+ if (regulator_ops_is_valid(rdev, 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_ops_is_valid(rdev,
+ REGULATOR_CHANGE_STATUS))
+ return -EPERM;
+
+ ret = _regulator_do_enable(rdev);
+ if (ret < 0)
+ return ret;
+
+ _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE,
+ NULL);
+ } 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) {
+ if (rdev->ena_gpio_state) {
+ 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;
+ }
+
+ /* cares about last_off_jiffy only if off_on_delay is required by
+ * device.
+ */
+ if (rdev->desc->off_on_delay)
+ rdev->last_off_jiffy = jiffies;
+
+ trace_regulator_disable_complete(rdev_get_name(rdev));
+
+ return 0;
+}
+
+/* locks held by regulator_disable() */
+static int _regulator_disable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+
+ lockdep_assert_held_once(&rdev->mutex);
+
+ 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_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
+ ret = _notifier_call_chain(rdev,
+ REGULATOR_EVENT_PRE_DISABLE,
+ NULL);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EINVAL;
+
+ ret = _regulator_do_disable(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to disable\n");
+ _notifier_call_chain(rdev,
+ REGULATOR_EVENT_ABORT_DISABLE,
+ NULL);
+ return ret;
+ }
+ _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
+ NULL);
+ }
+
+ rdev->use_count = 0;
+ } else if (rdev->use_count > 1) {
+ if (regulator_ops_is_valid(rdev, 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;
+
+ lockdep_assert_held_once(&rdev->mutex);
+
+ ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
+ REGULATOR_EVENT_PRE_DISABLE, NULL);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EINVAL;
+
+ ret = _regulator_do_disable(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to force disable\n");
+ _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
+ REGULATOR_EVENT_ABORT_DISABLE, NULL);
+ return ret;
+ }
+
+ _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
+ REGULATOR_EVENT_DISABLE, NULL);
+
+ return 0;
+}
+
+/**
+ * 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;
+
+ regulator_lock(rdev);
+
+ BUG_ON(!rdev->deferred_disables);
+
+ count = rdev->deferred_disables;
+ rdev->deferred_disables = 0;
+
+ /*
+ * Workqueue functions queue the new work instance while the previous
+ * work instance is being processed. Cancel the queued work instance
+ * as the work instance under processing does the job of the queued
+ * work instance.
+ */
+ cancel_delayed_work(&rdev->disable_work);
+
+ for (i = 0; i < count; i++) {
+ ret = _regulator_disable(rdev);
+ if (ret != 0)
+ rdev_err(rdev, "Deferred disable failed: %d\n", ret);
+ }
+
+ regulator_unlock(rdev);
+
+ 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;
+
+ if (regulator->always_on)
+ return 0;
+
+ if (!ms)
+ return regulator_disable(regulator);
+
+ regulator_lock(rdev);
+ rdev->deferred_disables++;
+ mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
+ msecs_to_jiffies(ms));
+ regulator_unlock(rdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_disable_deferred);
+
+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);
+}
+
+static int _regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned selector, int lock)
+{
+ const struct regulator_ops *ops = rdev->desc->ops;
+ int ret;
+
+ if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1 && !selector)
+ return rdev->desc->fixed_uV;
+
+ if (ops->list_voltage) {
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+ if (lock)
+ regulator_lock(rdev);
+ ret = ops->list_voltage(rdev, selector);
+ if (lock)
+ regulator_unlock(rdev);
+ } else if (rdev->is_switch && rdev->supply) {
+ ret = _regulator_list_voltage(rdev->supply->rdev,
+ selector, lock);
+ } else {
+ return -EINVAL;
+ }
+
+ if (ret > 0) {
+ if (ret < rdev->constraints->min_uV)
+ ret = 0;
+ else if (ret > rdev->constraints->max_uV)
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * 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(&regulator->rdev->mutex);
+ ret = _regulator_is_enabled(regulator->rdev);
+ mutex_unlock(&regulator->rdev->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_is_enabled);
+
+/**
+ * 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;
+
+ if (rdev->desc->n_voltages)
+ return rdev->desc->n_voltages;
+
+ if (!rdev->is_switch || !rdev->supply)
+ return -EINVAL;
+
+ return regulator_count_voltages(rdev->supply);
+}
+EXPORT_SYMBOL_GPL(regulator_count_voltages);
+
+/**
+ * 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)
+{
+ return _regulator_list_voltage(regulator->rdev, selector, 1);
+}
+EXPORT_SYMBOL_GPL(regulator_list_voltage);
+
+/**
+ * regulator_get_regmap - get the regulator's register map
+ * @regulator: regulator source
+ *
+ * Returns the register map for the given regulator, or an ERR_PTR value
+ * if the regulator doesn't use regmap.
+ */
+struct regmap *regulator_get_regmap(struct regulator *regulator)
+{
+ struct regmap *map = regulator->rdev->regmap;
+
+ return map ? map : ERR_PTR(-EOPNOTSUPP);
+}
+
+/**
+ * regulator_get_hardware_vsel_register - get the HW voltage selector register
+ * @regulator: regulator source
+ * @vsel_reg: voltage selector register, output parameter
+ * @vsel_mask: mask for voltage selector bitfield, output parameter
+ *
+ * Returns the hardware register offset and bitmask used for setting the
+ * regulator voltage. This might be useful when configuring voltage-scaling
+ * hardware or firmware that can make I2C requests behind the kernel's back,
+ * for example.
+ *
+ * On success, the output parameters @vsel_reg and @vsel_mask are filled in
+ * and 0 is returned, otherwise a negative errno is returned.
+ */
+int regulator_get_hardware_vsel_register(struct regulator *regulator,
+ unsigned *vsel_reg,
+ unsigned *vsel_mask)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ const struct regulator_ops *ops = rdev->desc->ops;
+
+ if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
+ return -EOPNOTSUPP;
+
+ *vsel_reg = rdev->desc->vsel_reg;
+ *vsel_mask = rdev->desc->vsel_mask;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_hardware_vsel_register);
+
+/**
+ * regulator_list_hardware_vsel - get the HW-specific register value for a selector
+ * @regulator: regulator source
+ * @selector: identify voltage to list
+ *
+ * Converts the selector to a hardware-specific voltage selector that can be
+ * directly written to the regulator registers. The address of the voltage
+ * register can be determined by calling @regulator_get_hardware_vsel_register.
+ *
+ * On error a negative errno is returned.
+ */
+int regulator_list_hardware_vsel(struct regulator *regulator,
+ unsigned selector)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ const struct regulator_ops *ops = rdev->desc->ops;
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+ if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
+ return -EOPNOTSUPP;
+
+ return selector;
+}
+EXPORT_SYMBOL_GPL(regulator_list_hardware_vsel);
+
+/**
+ * regulator_get_linear_step - return the voltage step size between VSEL values
+ * @regulator: regulator source
+ *
+ * Returns the voltage step size between VSEL values for linear
+ * regulators, or return 0 if the regulator isn't a linear regulator.
+ */
+unsigned int regulator_get_linear_step(struct regulator *regulator)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+
+ return rdev->desc->uV_step;
+}
+EXPORT_SYMBOL_GPL(regulator_get_linear_step);
+
+/**
+ * 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 (!regulator_ops_is_valid(rdev, 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);
+
+static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
+{
+ const struct regulator_desc *desc = rdev->desc;
+
+ if (desc->ops->map_voltage)
+ return desc->ops->map_voltage(rdev, min_uV, max_uV);
+
+ if (desc->ops->list_voltage == regulator_list_voltage_linear)
+ return regulator_map_voltage_linear(rdev, min_uV, max_uV);
+
+ if (desc->ops->list_voltage == regulator_list_voltage_linear_range)
+ return regulator_map_voltage_linear_range(rdev, min_uV, max_uV);
+
+ return regulator_map_voltage_iterate(rdev, min_uV, max_uV);
+}
+
+static int _regulator_call_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct pre_voltage_change_data data;
+ int ret;
+
+ data.old_uV = _regulator_get_voltage(rdev);
+ data.min_uV = min_uV;
+ data.max_uV = max_uV;
+ ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE,
+ &data);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EINVAL;
+
+ ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV, selector);
+ if (ret >= 0)
+ return ret;
+
+ _notifier_call_chain(rdev, REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE,
+ (void *)data.old_uV);
+
+ return ret;
+}
+
+static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev,
+ int uV, unsigned selector)
+{
+ struct pre_voltage_change_data data;
+ int ret;
+
+ data.old_uV = _regulator_get_voltage(rdev);
+ data.min_uV = uV;
+ data.max_uV = uV;
+ ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE,
+ &data);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EINVAL;
+
+ ret = rdev->desc->ops->set_voltage_sel(rdev, selector);
+ if (ret >= 0)
+ return ret;
+
+ _notifier_call_chain(rdev, REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE,
+ (void *)data.old_uV);
+
+ return ret;
+}
+
+static int _regulator_set_voltage_time(struct regulator_dev *rdev,
+ int old_uV, int new_uV)
+{
+ unsigned int ramp_delay = 0;
+
+ if (rdev->constraints->ramp_delay)
+ ramp_delay = rdev->constraints->ramp_delay;
+ else if (rdev->desc->ramp_delay)
+ ramp_delay = rdev->desc->ramp_delay;
+ else if (rdev->constraints->settling_time)
+ return rdev->constraints->settling_time;
+ else if (rdev->constraints->settling_time_up &&
+ (new_uV > old_uV))
+ return rdev->constraints->settling_time_up;
+ else if (rdev->constraints->settling_time_down &&
+ (new_uV < old_uV))
+ return rdev->constraints->settling_time_down;
+
+ if (ramp_delay == 0) {
+ rdev_dbg(rdev, "ramp_delay not set\n");
+ return 0;
+ }
+
+ return DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
+}
+
+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;
+ const struct regulator_ops *ops = rdev->desc->ops;
+ int old_uV = _regulator_get_voltage(rdev);
+
+ 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) &&
+ ops->set_voltage_time_sel && ops->get_voltage_sel) {
+ old_selector = ops->get_voltage_sel(rdev);
+ if (old_selector < 0)
+ return old_selector;
+ }
+
+ if (ops->set_voltage) {
+ ret = _regulator_call_set_voltage(rdev, min_uV, max_uV,
+ &selector);
+
+ if (ret >= 0) {
+ if (ops->list_voltage)
+ best_val = ops->list_voltage(rdev,
+ selector);
+ else
+ best_val = _regulator_get_voltage(rdev);
+ }
+
+ } else if (ops->set_voltage_sel) {
+ ret = regulator_map_voltage(rdev, min_uV, max_uV);
+ if (ret >= 0) {
+ best_val = ops->list_voltage(rdev, ret);
+ if (min_uV <= best_val && max_uV >= best_val) {
+ selector = ret;
+ if (old_selector == selector)
+ ret = 0;
+ else
+ ret = _regulator_call_set_voltage_sel(
+ rdev, best_val, selector);
+ } else {
+ ret = -EINVAL;
+ }
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ goto out;
+
+ if (ops->set_voltage_time_sel) {
+ /*
+ * Call set_voltage_time_sel if successfully obtained
+ * old_selector
+ */
+ if (old_selector >= 0 && old_selector != selector)
+ delay = ops->set_voltage_time_sel(rdev, old_selector,
+ selector);
+ } else {
+ if (old_uV != best_val) {
+ if (ops->set_voltage_time)
+ delay = ops->set_voltage_time(rdev, old_uV,
+ best_val);
+ else
+ delay = _regulator_set_voltage_time(rdev,
+ old_uV,
+ best_val);
+ }
+ }
+
+ if (delay < 0) {
+ rdev_warn(rdev, "failed to get delay: %d\n", delay);
+ delay = 0;
+ }
+
+ /* Insert any necessary delays */
+ if (delay >= 1000) {
+ mdelay(delay / 1000);
+ udelay(delay % 1000);
+ } else if (delay) {
+ udelay(delay);
+ }
+
+ if (best_val >= 0) {
+ unsigned long data = best_val;
+
+ _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
+ (void *)data);
+ }
+
+out:
+ trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);
+
+ return ret;
+}
+
+static int _regulator_do_set_suspend_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, suspend_state_t state)
+{
+ struct regulator_state *rstate;
+ int uV, sel;
+
+ rstate = regulator_get_suspend_state(rdev, state);
+ if (rstate == NULL)
+ return -EINVAL;
+
+ if (min_uV < rstate->min_uV)
+ min_uV = rstate->min_uV;
+ if (max_uV > rstate->max_uV)
+ max_uV = rstate->max_uV;
+
+ sel = regulator_map_voltage(rdev, min_uV, max_uV);
+ if (sel < 0)
+ return sel;
+
+ uV = rdev->desc->ops->list_voltage(rdev, sel);
+ if (uV >= min_uV && uV <= max_uV)
+ rstate->uV = uV;
+
+ return 0;
+}
+
+static int regulator_set_voltage_unlocked(struct regulator *regulator,
+ int min_uV, int max_uV,
+ suspend_state_t state)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ struct regulator_voltage *voltage = &regulator->voltage[state];
+ int ret = 0;
+ int old_min_uV, old_max_uV;
+ int current_uV;
+ int best_supply_uV = 0;
+ int supply_change_uV = 0;
+
+ /* 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 (voltage->min_uV == min_uV && voltage->max_uV == max_uV)
+ goto out;
+
+ /* If we're trying to set a range that overlaps the current voltage,
+ * return successfully even though the regulator does not support
+ * changing the voltage.
+ */
+ if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
+ current_uV = _regulator_get_voltage(rdev);
+ if (min_uV <= current_uV && current_uV <= max_uV) {
+ voltage->min_uV = min_uV;
+ voltage->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 = voltage->min_uV;
+ old_max_uV = voltage->max_uV;
+ voltage->min_uV = min_uV;
+ voltage->max_uV = max_uV;
+
+ ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state);
+ if (ret < 0)
+ goto out2;
+
+ if (rdev->supply &&
+ regulator_ops_is_valid(rdev->supply->rdev,
+ REGULATOR_CHANGE_VOLTAGE) &&
+ (rdev->desc->min_dropout_uV || !(rdev->desc->ops->get_voltage ||
+ rdev->desc->ops->get_voltage_sel))) {
+ int current_supply_uV;
+ int selector;
+
+ selector = regulator_map_voltage(rdev, min_uV, max_uV);
+ if (selector < 0) {
+ ret = selector;
+ goto out2;
+ }
+
+ best_supply_uV = _regulator_list_voltage(rdev, selector, 0);
+ if (best_supply_uV < 0) {
+ ret = best_supply_uV;
+ goto out2;
+ }
+
+ best_supply_uV += rdev->desc->min_dropout_uV;
+
+ current_supply_uV = _regulator_get_voltage(rdev->supply->rdev);
+ if (current_supply_uV < 0) {
+ ret = current_supply_uV;
+ goto out2;
+ }
+
+ supply_change_uV = best_supply_uV - current_supply_uV;
+ }
+
+ if (supply_change_uV > 0) {
+ ret = regulator_set_voltage_unlocked(rdev->supply,
+ best_supply_uV, INT_MAX, state);
+ if (ret) {
+ dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n",
+ ret);
+ goto out2;
+ }
+ }
+
+ if (state == PM_SUSPEND_ON)
+ ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+ else
+ ret = _regulator_do_set_suspend_voltage(rdev, min_uV,
+ max_uV, state);
+ if (ret < 0)
+ goto out2;
+
+ if (supply_change_uV < 0) {
+ ret = regulator_set_voltage_unlocked(rdev->supply,
+ best_supply_uV, INT_MAX, state);
+ if (ret)
+ dev_warn(&rdev->dev, "Failed to decrease supply voltage: %d\n",
+ ret);
+ /* No need to fail here */
+ ret = 0;
+ }
+
+out:
+ return ret;
+out2:
+ voltage->min_uV = old_min_uV;
+ voltage->max_uV = old_max_uV;
+
+ 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)
+{
+ int ret = 0;
+
+ regulator_lock_supply(regulator->rdev);
+
+ ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV,
+ PM_SUSPEND_ON);
+
+ regulator_unlock_supply(regulator->rdev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_voltage);
+
+static inline int regulator_suspend_toggle(struct regulator_dev *rdev,
+ suspend_state_t state, bool en)
+{
+ struct regulator_state *rstate;
+
+ rstate = regulator_get_suspend_state(rdev, state);
+ if (rstate == NULL)
+ return -EINVAL;
+
+ if (!rstate->changeable)
+ return -EPERM;
+
+ rstate->enabled = (en) ? ENABLE_IN_SUSPEND : DISABLE_IN_SUSPEND;
+
+ return 0;
+}
+
+int regulator_suspend_enable(struct regulator_dev *rdev,
+ suspend_state_t state)
+{
+ return regulator_suspend_toggle(rdev, state, true);
+}
+EXPORT_SYMBOL_GPL(regulator_suspend_enable);
+
+int regulator_suspend_disable(struct regulator_dev *rdev,
+ suspend_state_t state)
+{
+ struct regulator *regulator;
+ struct regulator_voltage *voltage;
+
+ /*
+ * if any consumer wants this regulator device keeping on in
+ * suspend states, don't set it as disabled.
+ */
+ list_for_each_entry(regulator, &rdev->consumer_list, list) {
+ voltage = &regulator->voltage[state];
+ if (voltage->min_uV || voltage->max_uV)
+ return 0;
+ }
+
+ return regulator_suspend_toggle(rdev, state, false);
+}
+EXPORT_SYMBOL_GPL(regulator_suspend_disable);
+
+static int _regulator_set_suspend_voltage(struct regulator *regulator,
+ int min_uV, int max_uV,
+ suspend_state_t state)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ struct regulator_state *rstate;
+
+ rstate = regulator_get_suspend_state(rdev, state);
+ if (rstate == NULL)
+ return -EINVAL;
+
+ if (rstate->min_uV == rstate->max_uV) {
+ rdev_err(rdev, "The suspend voltage can't be changed!\n");
+ return -EPERM;
+ }
+
+ return regulator_set_voltage_unlocked(regulator, min_uV, max_uV, state);
+}
+
+int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV,
+ int max_uV, suspend_state_t state)
+{
+ int ret = 0;
+
+ /* PM_SUSPEND_ON is handled by regulator_set_voltage() */
+ if (regulator_check_states(state) || state == PM_SUSPEND_ON)
+ return -EINVAL;
+
+ regulator_lock_supply(regulator->rdev);
+
+ ret = _regulator_set_suspend_voltage(regulator, min_uV,
+ max_uV, state);
+
+ regulator_unlock_supply(regulator->rdev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_suspend_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;
+ const struct regulator_ops *ops = rdev->desc->ops;
+ int old_sel = -1;
+ int new_sel = -1;
+ int voltage;
+ int i;
+
+ if (ops->set_voltage_time)
+ return ops->set_voltage_time(rdev, old_uV, new_uV);
+ else if (!ops->set_voltage_time_sel)
+ return _regulator_set_voltage_time(rdev, old_uV, new_uV);
+
+ /* Currently requires operations to do this */
+ if (!ops->list_voltage || !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)
+{
+ int old_volt, new_volt;
+
+ /* 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);
+
+ if (rdev->desc->ops->set_voltage_time)
+ return rdev->desc->ops->set_voltage_time(rdev, old_volt,
+ new_volt);
+ else
+ return _regulator_set_voltage_time(rdev, old_volt, new_volt);
+}
+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;
+ struct regulator_voltage *voltage = &regulator->voltage[PM_SUSPEND_ON];
+ int ret, min_uV, max_uV;
+
+ regulator_lock(rdev);
+
+ 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 (!voltage->min_uV && !voltage->max_uV) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ min_uV = voltage->min_uV;
+ max_uV = voltage->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, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+
+out:
+ regulator_unlock(rdev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_sync_voltage);
+
+static int _regulator_get_voltage(struct regulator_dev *rdev)
+{
+ int sel, ret;
+ bool bypassed;
+
+ if (rdev->desc->ops->get_bypass) {
+ ret = rdev->desc->ops->get_bypass(rdev, &bypassed);
+ if (ret < 0)
+ return ret;
+ if (bypassed) {
+ /* if bypassed the regulator must have a supply */
+ if (!rdev->supply) {
+ rdev_err(rdev,
+ "bypassed regulator has no supply!\n");
+ return -EPROBE_DEFER;
+ }
+
+ return _regulator_get_voltage(rdev->supply->rdev);
+ }
+ }
+
+ 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 if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
+ ret = rdev->desc->fixed_uV;
+ } else if (rdev->supply) {
+ ret = _regulator_get_voltage(rdev->supply->rdev);
+ } else if (rdev->supply_name) {
+ return -EPROBE_DEFER;
+ } 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;
+
+ regulator_lock_supply(regulator->rdev);
+
+ ret = _regulator_get_voltage(regulator->rdev);
+
+ regulator_unlock_supply(regulator->rdev);
+
+ 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;
+
+ regulator_lock(rdev);
+
+ /* 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:
+ regulator_unlock(rdev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_current_limit);
+
+static int _regulator_get_current_limit(struct regulator_dev *rdev)
+{
+ int ret;
+
+ regulator_lock(rdev);
+
+ /* sanity check */
+ if (!rdev->desc->ops->get_current_limit) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rdev->desc->ops->get_current_limit(rdev);
+out:
+ regulator_unlock(rdev);
+ 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;
+
+ regulator_lock(rdev);
+
+ /* 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:
+ regulator_unlock(rdev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_mode);
+
+static unsigned int _regulator_get_mode(struct regulator_dev *rdev)
+{
+ int ret;
+
+ regulator_lock(rdev);
+
+ /* sanity check */
+ if (!rdev->desc->ops->get_mode) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rdev->desc->ops->get_mode(rdev);
+out:
+ regulator_unlock(rdev);
+ 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);
+
+static int _regulator_get_error_flags(struct regulator_dev *rdev,
+ unsigned int *flags)
+{
+ int ret;
+
+ regulator_lock(rdev);
+
+ /* sanity check */
+ if (!rdev->desc->ops->get_error_flags) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rdev->desc->ops->get_error_flags(rdev, flags);
+out:
+ regulator_unlock(rdev);
+ return ret;
+}
+
+/**
+ * regulator_get_error_flags - get regulator error information
+ * @regulator: regulator source
+ * @flags: pointer to store error flags
+ *
+ * Get the current regulator error information.
+ */
+int regulator_get_error_flags(struct regulator *regulator,
+ unsigned int *flags)
+{
+ return _regulator_get_error_flags(regulator->rdev, flags);
+}
+EXPORT_SYMBOL_GPL(regulator_get_error_flags);
+
+/**
+ * regulator_set_load - set regulator load
+ * @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.
+ *
+ * On error a negative errno is returned.
+ */
+int regulator_set_load(struct regulator *regulator, int uA_load)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret;
+
+ regulator_lock(rdev);
+ regulator->uA_load = uA_load;
+ ret = drms_uA_update(rdev);
+ regulator_unlock(rdev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_load);
+
+/**
+ * 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 (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS))
+ return 0;
+
+ regulator_lock(rdev);
+
+ 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;
+
+ regulator_unlock(rdev);
+
+ 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(&regulator->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(&regulator->rdev->notifier,
+ nb);
+}
+EXPORT_SYMBOL_GPL(regulator_unregister_notifier);
+
+/* notify regulator consumers and downstream regulator consumers.
+ * Note mutex must be held by caller.
+ */
+static int _notifier_call_chain(struct regulator_dev *rdev,
+ unsigned long event, void *data)
+{
+ /* call rdev chain first */
+ return 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);
+
+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 re-enable %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 = 0;
+
+ for (i = 0; i < num_consumers; i++) {
+ consumers[i].ret =
+ regulator_force_disable(consumers[i].consumer);
+
+ /* Store first error for reporting */
+ if (consumers[i].ret && !ret)
+ ret = consumers[i].ret;
+ }
+
+ 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)
+{
+ lockdep_assert_held_once(&rdev->mutex);
+
+ _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);
+
+static struct attribute *regulator_dev_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_num_users.attr,
+ &dev_attr_type.attr,
+ &dev_attr_microvolts.attr,
+ &dev_attr_microamps.attr,
+ &dev_attr_opmode.attr,
+ &dev_attr_state.attr,
+ &dev_attr_status.attr,
+ &dev_attr_bypass.attr,
+ &dev_attr_requested_microamps.attr,
+ &dev_attr_min_microvolts.attr,
+ &dev_attr_max_microvolts.attr,
+ &dev_attr_min_microamps.attr,
+ &dev_attr_max_microamps.attr,
+ &dev_attr_suspend_standby_state.attr,
+ &dev_attr_suspend_mem_state.attr,
+ &dev_attr_suspend_disk_state.attr,
+ &dev_attr_suspend_standby_microvolts.attr,
+ &dev_attr_suspend_mem_microvolts.attr,
+ &dev_attr_suspend_disk_microvolts.attr,
+ &dev_attr_suspend_standby_mode.attr,
+ &dev_attr_suspend_mem_mode.attr,
+ &dev_attr_suspend_disk_mode.attr,
+ NULL
+};
+
+/*
+ * To avoid cluttering sysfs (and memory) with useless state, only
+ * create attributes that can be meaningfully displayed.
+ */
+static umode_t regulator_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ const struct regulator_ops *ops = rdev->desc->ops;
+ umode_t mode = attr->mode;
+
+ /* these three are always present */
+ if (attr == &dev_attr_name.attr ||
+ attr == &dev_attr_num_users.attr ||
+ attr == &dev_attr_type.attr)
+ return mode;
+
+ /* some attributes need specific methods to be displayed */
+ if (attr == &dev_attr_microvolts.attr) {
+ 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) ||
+ (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1))
+ return mode;
+ return 0;
+ }
+
+ if (attr == &dev_attr_microamps.attr)
+ return ops->get_current_limit ? mode : 0;
+
+ if (attr == &dev_attr_opmode.attr)
+ return ops->get_mode ? mode : 0;
+
+ if (attr == &dev_attr_state.attr)
+ return (rdev->ena_pin || ops->is_enabled) ? mode : 0;
+
+ if (attr == &dev_attr_status.attr)
+ return ops->get_status ? mode : 0;
+
+ if (attr == &dev_attr_bypass.attr)
+ return ops->get_bypass ? mode : 0;
+
+ /* some attributes are type-specific */
+ if (attr == &dev_attr_requested_microamps.attr)
+ return rdev->desc->type == REGULATOR_CURRENT ? mode : 0;
+
+ /* constraints need specific supporting methods */
+ if (attr == &dev_attr_min_microvolts.attr ||
+ attr == &dev_attr_max_microvolts.attr)
+ return (ops->set_voltage || ops->set_voltage_sel) ? mode : 0;
+
+ if (attr == &dev_attr_min_microamps.attr ||
+ attr == &dev_attr_max_microamps.attr)
+ return ops->set_current_limit ? mode : 0;
+
+ if (attr == &dev_attr_suspend_standby_state.attr ||
+ attr == &dev_attr_suspend_mem_state.attr ||
+ attr == &dev_attr_suspend_disk_state.attr)
+ return mode;
+
+ if (attr == &dev_attr_suspend_standby_microvolts.attr ||
+ attr == &dev_attr_suspend_mem_microvolts.attr ||
+ attr == &dev_attr_suspend_disk_microvolts.attr)
+ return ops->set_suspend_voltage ? mode : 0;
+
+ if (attr == &dev_attr_suspend_standby_mode.attr ||
+ attr == &dev_attr_suspend_mem_mode.attr ||
+ attr == &dev_attr_suspend_disk_mode.attr)
+ return ops->set_suspend_mode ? mode : 0;
+
+ return mode;
+}
+
+static const struct attribute_group regulator_dev_group = {
+ .attrs = regulator_dev_attrs,
+ .is_visible = regulator_attr_is_visible,
+};
+
+static const struct attribute_group *regulator_dev_groups[] = {
+ &regulator_dev_group,
+ NULL
+};
+
+static void regulator_dev_release(struct device *dev)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+
+ kfree(rdev->constraints);
+ of_node_put(rdev->dev.of_node);
+ kfree(rdev);
+}
+
+static void rdev_init_debugfs(struct regulator_dev *rdev)
+{
+ struct device *parent = rdev->dev.parent;
+ const char *rname = rdev_get_name(rdev);
+ char name[NAME_MAX];
+
+ /* Avoid duplicate debugfs directory names */
+ if (parent && rname == rdev->desc->name) {
+ snprintf(name, sizeof(name), "%s-%s", dev_name(parent),
+ rname);
+ rname = name;
+ }
+
+ rdev->debugfs = debugfs_create_dir(rname, 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);
+}
+
+static int regulator_register_resolve_supply(struct device *dev, void *data)
+{
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+
+ if (regulator_resolve_supply(rdev))
+ rdev_dbg(rdev, "unable to resolve supply\n");
+
+ return 0;
+}
+
+static int regulator_fill_coupling_array(struct regulator_dev *rdev)
+{
+ struct coupling_desc *c_desc = &rdev->coupling_desc;
+ int n_coupled = c_desc->n_coupled;
+ struct regulator_dev *c_rdev;
+ int i;
+
+ for (i = 1; i < n_coupled; i++) {
+ /* already resolved */
+ if (c_desc->coupled_rdevs[i])
+ continue;
+
+ c_rdev = of_parse_coupled_regulator(rdev, i - 1);
+
+ if (c_rdev) {
+ c_desc->coupled_rdevs[i] = c_rdev;
+ c_desc->n_resolved++;
+ }
+ }
+
+ if (rdev->coupling_desc.n_resolved < n_coupled)
+ return -1;
+ else
+ return 0;
+}
+
+static int regulator_register_fill_coupling_array(struct device *dev,
+ void *data)
+{
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+
+ if (!IS_ENABLED(CONFIG_OF))
+ return 0;
+
+ if (regulator_fill_coupling_array(rdev))
+ rdev_dbg(rdev, "unable to resolve coupling\n");
+
+ return 0;
+}
+
+static int regulator_resolve_coupling(struct regulator_dev *rdev)
+{
+ int n_phandles;
+
+ if (!IS_ENABLED(CONFIG_OF))
+ n_phandles = 0;
+ else
+ n_phandles = of_get_n_coupled(rdev);
+
+ if (n_phandles + 1 > MAX_COUPLED) {
+ rdev_err(rdev, "too many regulators coupled\n");
+ return -EPERM;
+ }
+
+ /*
+ * Every regulator should always have coupling descriptor filled with
+ * at least pointer to itself.
+ */
+ rdev->coupling_desc.coupled_rdevs[0] = rdev;
+ rdev->coupling_desc.n_coupled = n_phandles + 1;
+ rdev->coupling_desc.n_resolved++;
+
+ /* regulator isn't coupled */
+ if (n_phandles == 0)
+ return 0;
+
+ /* regulator, which can't change its voltage, can't be coupled */
+ if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
+ rdev_err(rdev, "voltage operation not allowed\n");
+ return -EPERM;
+ }
+
+ if (rdev->constraints->max_spread <= 0) {
+ rdev_err(rdev, "wrong max_spread value\n");
+ return -EPERM;
+ }
+
+ if (!of_check_coupling_data(rdev))
+ return -EPERM;
+
+ /*
+ * After everything has been checked, try to fill rdevs array
+ * with pointers to regulators parsed from device tree. If some
+ * regulators are not registered yet, retry in late init call
+ */
+ regulator_fill_coupling_array(rdev);
+
+ return 0;
+}
+
+/**
+ * regulator_register - register regulator
+ * @regulator_desc: regulator to register
+ * @cfg: 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 *cfg)
+{
+ const struct regulator_init_data *init_data;
+ struct regulator_config *config = NULL;
+ static atomic_t regulator_no = ATOMIC_INIT(-1);
+ struct regulator_dev *rdev;
+ struct device *dev;
+ int ret, i;
+
+ if (regulator_desc == NULL || cfg == NULL)
+ return ERR_PTR(-EINVAL);
+
+ dev = cfg->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);
+ }
+
+ rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
+ if (rdev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * Duplicate the config so the driver could override it after
+ * parsing init data.
+ */
+ config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL);
+ if (config == NULL) {
+ kfree(rdev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init_data = regulator_of_get_init_data(dev, regulator_desc, config,
+ &rdev->dev.of_node);
+ if (!init_data) {
+ init_data = config->init_data;
+ rdev->dev.of_node = of_node_get(config->of_node);
+ }
+
+ 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;
+ }
+
+ if (config->ena_gpiod ||
+ ((config->ena_gpio || config->ena_gpio_initialized) &&
+ gpio_is_valid(config->ena_gpio))) {
+ mutex_lock(&regulator_list_mutex);
+ ret = regulator_ena_gpio_request(rdev, config);
+ mutex_unlock(&regulator_list_mutex);
+ if (ret != 0) {
+ rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
+ config->ena_gpio, ret);
+ goto clean;
+ }
+ }
+
+ /* register with sysfs */
+ rdev->dev.class = &regulator_class;
+ rdev->dev.parent = dev;
+ dev_set_name(&rdev->dev, "regulator.%lu",
+ (unsigned long) atomic_inc_return(&regulator_no));
+
+ /* set regulator constraints */
+ if (init_data)
+ rdev->constraints = kmemdup(&init_data->constraints,
+ sizeof(*rdev->constraints),
+ GFP_KERNEL);
+ else
+ rdev->constraints = kzalloc(sizeof(*rdev->constraints),
+ GFP_KERNEL);
+ if (!rdev->constraints) {
+ ret = -ENOMEM;
+ goto wash;
+ }
+
+ if (init_data && init_data->supply_regulator)
+ rdev->supply_name = init_data->supply_regulator;
+ else if (regulator_desc->supply_name)
+ rdev->supply_name = regulator_desc->supply_name;
+
+ ret = set_machine_constraints(rdev);
+ if (ret == -EPROBE_DEFER) {
+ /* Regulator might be in bypass mode and so needs its supply
+ * to set the constraints */
+ /* FIXME: this currently triggers a chicken-and-egg problem
+ * when creating -SUPPLY symlink in sysfs to a regulator
+ * that is just being created */
+ ret = regulator_resolve_supply(rdev);
+ if (!ret)
+ ret = set_machine_constraints(rdev);
+ else
+ rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
+ ERR_PTR(ret));
+ }
+ if (ret < 0)
+ goto wash;
+
+ mutex_lock(&regulator_list_mutex);
+ ret = regulator_resolve_coupling(rdev);
+ mutex_unlock(&regulator_list_mutex);
+
+ if (ret != 0)
+ goto wash;
+
+ /* 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;
+ }
+ }
+ }
+
+ if (!rdev->desc->ops->get_voltage &&
+ !rdev->desc->ops->list_voltage &&
+ !rdev->desc->fixed_uV)
+ rdev->is_switch = true;
+
+ dev_set_drvdata(&rdev->dev, rdev);
+ ret = device_register(&rdev->dev);
+ if (ret != 0) {
+ put_device(&rdev->dev);
+ goto unset_supplies;
+ }
+
+ rdev_init_debugfs(rdev);
+
+ /* try to resolve regulators supply since a new one was registered */
+ class_for_each_device(&regulator_class, NULL, NULL,
+ regulator_register_resolve_supply);
+ kfree(config);
+ return rdev;
+
+unset_supplies:
+ mutex_lock(&regulator_list_mutex);
+ unset_regulator_supplies(rdev);
+ mutex_unlock(&regulator_list_mutex);
+wash:
+ kfree(rdev->constraints);
+ mutex_lock(&regulator_list_mutex);
+ regulator_ena_gpio_free(rdev);
+ mutex_unlock(&regulator_list_mutex);
+clean:
+ kfree(rdev);
+ kfree(config);
+ return ERR_PTR(ret);
+}
+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) {
+ while (rdev->use_count--)
+ regulator_disable(rdev->supply);
+ regulator_put(rdev->supply);
+ }
+ mutex_lock(&regulator_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);
+ regulator_ena_gpio_free(rdev);
+ mutex_unlock(&regulator_list_mutex);
+ device_unregister(&rdev->dev);
+}
+EXPORT_SYMBOL_GPL(regulator_unregister);
+
+#ifdef CONFIG_SUSPEND
+static int _regulator_suspend(struct device *dev, void *data)
+{
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ suspend_state_t *state = data;
+ int ret;
+
+ regulator_lock(rdev);
+ ret = suspend_set_state(rdev, *state);
+ regulator_unlock(rdev);
+
+ return ret;
+}
+
+/**
+ * regulator_suspend - prepare regulators for system wide suspend
+ * @state: system suspend state
+ *
+ * Configure each regulator with it's suspend operating parameters for state.
+ */
+static int regulator_suspend(struct device *dev)
+{
+ suspend_state_t state = pm_suspend_target_state;
+
+ return class_for_each_device(&regulator_class, NULL, &state,
+ _regulator_suspend);
+}
+
+static int _regulator_resume(struct device *dev, void *data)
+{
+ int ret = 0;
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ suspend_state_t *state = data;
+ struct regulator_state *rstate;
+
+ rstate = regulator_get_suspend_state(rdev, *state);
+ if (rstate == NULL)
+ return 0;
+
+ regulator_lock(rdev);
+
+ if (rdev->desc->ops->resume &&
+ (rstate->enabled == ENABLE_IN_SUSPEND ||
+ rstate->enabled == DISABLE_IN_SUSPEND))
+ ret = rdev->desc->ops->resume(rdev);
+
+ regulator_unlock(rdev);
+
+ return ret;
+}
+
+static int regulator_resume(struct device *dev)
+{
+ suspend_state_t state = pm_suspend_target_state;
+
+ return class_for_each_device(&regulator_class, NULL, &state,
+ _regulator_resume);
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define regulator_suspend NULL
+#define regulator_resume NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
+ .suspend = regulator_suspend,
+ .resume = regulator_resume,
+};
+#endif
+
+struct class regulator_class = {
+ .name = "regulator",
+ .dev_release = regulator_dev_release,
+ .dev_groups = regulator_dev_groups,
+#ifdef CONFIG_PM
+ .pm = &regulator_pm_ops,
+#endif
+};
+/**
+ * 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);
+
+/**
+ * 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 int supply_map_show(struct seq_file *sf, void *data)
+{
+ struct regulator_map *map;
+
+ list_for_each_entry(map, &regulator_map_list, list) {
+ seq_printf(sf, "%s -> %s.%s\n",
+ rdev_get_name(map->regulator), map->dev_name,
+ map->supply);
+ }
+
+ return 0;
+}
+
+static int supply_map_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, supply_map_show, inode->i_private);
+}
+#endif
+
+static const struct file_operations supply_map_fops = {
+#ifdef CONFIG_DEBUG_FS
+ .open = supply_map_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+#endif
+};
+
+#ifdef CONFIG_DEBUG_FS
+struct summary_data {
+ struct seq_file *s;
+ struct regulator_dev *parent;
+ int level;
+};
+
+static void regulator_summary_show_subtree(struct seq_file *s,
+ struct regulator_dev *rdev,
+ int level);
+
+static int regulator_summary_show_children(struct device *dev, void *data)
+{
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ struct summary_data *summary_data = data;
+
+ if (rdev->supply && rdev->supply->rdev == summary_data->parent)
+ regulator_summary_show_subtree(summary_data->s, rdev,
+ summary_data->level + 1);
+
+ return 0;
+}
+
+static void regulator_summary_show_subtree(struct seq_file *s,
+ struct regulator_dev *rdev,
+ int level)
+{
+ struct regulation_constraints *c;
+ struct regulator *consumer;
+ struct summary_data summary_data;
+
+ if (!rdev)
+ return;
+
+ seq_printf(s, "%*s%-*s %3d %4d %6d ",
+ level * 3 + 1, "",
+ 30 - level * 3, rdev_get_name(rdev),
+ rdev->use_count, rdev->open_count, rdev->bypass_count);
+
+ seq_printf(s, "%5dmV ", _regulator_get_voltage(rdev) / 1000);
+ seq_printf(s, "%5dmA ", _regulator_get_current_limit(rdev) / 1000);
+
+ c = rdev->constraints;
+ if (c) {
+ switch (rdev->desc->type) {
+ case REGULATOR_VOLTAGE:
+ seq_printf(s, "%5dmV %5dmV ",
+ c->min_uV / 1000, c->max_uV / 1000);
+ break;
+ case REGULATOR_CURRENT:
+ seq_printf(s, "%5dmA %5dmA ",
+ c->min_uA / 1000, c->max_uA / 1000);
+ break;
+ }
+ }
+
+ seq_puts(s, "\n");
+
+ list_for_each_entry(consumer, &rdev->consumer_list, list) {
+ if (consumer->dev && consumer->dev->class == &regulator_class)
+ continue;
+
+ seq_printf(s, "%*s%-*s ",
+ (level + 1) * 3 + 1, "",
+ 30 - (level + 1) * 3,
+ consumer->dev ? dev_name(consumer->dev) : "deviceless");
+
+ switch (rdev->desc->type) {
+ case REGULATOR_VOLTAGE:
+ seq_printf(s, "%37dmV %5dmV",
+ consumer->voltage[PM_SUSPEND_ON].min_uV / 1000,
+ consumer->voltage[PM_SUSPEND_ON].max_uV / 1000);
+ break;
+ case REGULATOR_CURRENT:
+ break;
+ }
+
+ seq_puts(s, "\n");
+ }
+
+ summary_data.s = s;
+ summary_data.level = level;
+ summary_data.parent = rdev;
+
+ class_for_each_device(&regulator_class, NULL, &summary_data,
+ regulator_summary_show_children);
+}
+
+static int regulator_summary_show_roots(struct device *dev, void *data)
+{
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ struct seq_file *s = data;
+
+ if (!rdev->supply)
+ regulator_summary_show_subtree(s, rdev, 0);
+
+ return 0;
+}
+
+static int regulator_summary_show(struct seq_file *s, void *data)
+{
+ seq_puts(s, " regulator use open bypass voltage current min max\n");
+ seq_puts(s, "-------------------------------------------------------------------------------\n");
+
+ class_for_each_device(&regulator_class, NULL, s,
+ regulator_summary_show_roots);
+
+ return 0;
+}
+
+static int regulator_summary_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, regulator_summary_show, inode->i_private);
+}
+#endif
+
+static const struct file_operations regulator_summary_fops = {
+#ifdef CONFIG_DEBUG_FS
+ .open = regulator_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+#endif
+};
+
+static int __init regulator_init(void)
+{
+ int ret;
+
+ ret = class_register(&regulator_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);
+
+ debugfs_create_file("regulator_summary", 0444, debugfs_root,
+ NULL, &regulator_summary_fops);
+
+ regulator_dummy_init();
+
+ return ret;
+}
+
+/* init early to allow our consumers to complete system booting */
+core_initcall(regulator_init);
+
+static int regulator_late_cleanup(struct device *dev, void *data)
+{
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ const struct regulator_ops *ops = rdev->desc->ops;
+ struct regulation_constraints *c = rdev->constraints;
+ int enabled, ret;
+
+ if (c && c->always_on)
+ return 0;
+
+ if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS))
+ return 0;
+
+ regulator_lock(rdev);
+
+ 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 (have_full_constraints()) {
+ /* We log since this may kill the system if it goes
+ * wrong. */
+ rdev_info(rdev, "disabling\n");
+ ret = _regulator_do_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:
+ regulator_unlock(rdev);
+
+ return 0;
+}
+
+static void regulator_init_complete_work_function(struct work_struct *work)
+{
+ /*
+ * Regulators may had failed to resolve their input supplies
+ * when were registered, either because the input supply was
+ * not registered yet or because its parent device was not
+ * bound yet. So attempt to resolve the input supplies for
+ * pending regulators before trying to disable unused ones.
+ */
+ class_for_each_device(&regulator_class, NULL, NULL,
+ regulator_register_resolve_supply);
+
+ /* If we have a full configuration then disable any regulators
+ * we have permission to change the status for and which are
+ * not in use or always_on. This is effectively the default
+ * for DT and ACPI as they have full constraints.
+ */
+ class_for_each_device(&regulator_class, NULL, NULL,
+ regulator_late_cleanup);
+}
+
+static DECLARE_DELAYED_WORK(regulator_init_complete_work,
+ regulator_init_complete_work_function);
+
+static int __init regulator_init_complete(void)
+{
+ /*
+ * 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;
+
+ /*
+ * We punt completion for an arbitrary amount of time since
+ * systems like distros will load many drivers from userspace
+ * so consumers might not always be ready yet, this is
+ * particularly an issue with laptops where this might bounce
+ * the display off then on. Ideally we'd get a notification
+ * from userspace when this happens but we don't so just wait
+ * a bit and hope we waited long enough. It'd be better if
+ * we'd only do this on systems that need it, and a kernel
+ * command line option might be useful.
+ */
+ schedule_delayed_work(&regulator_init_complete_work,
+ msecs_to_jiffies(30000));
+
+ class_for_each_device(&regulator_class, NULL, NULL,
+ regulator_register_fill_coupling_array);
+
+ return 0;
+}
+late_initcall_sync(regulator_init_complete);
diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c
new file mode 100644
index 000000000..213145793
--- /dev/null
+++ b/drivers/regulator/cpcap-regulator.c
@@ -0,0 +1,582 @@
+/*
+ * Motorola CPCAP PMIC regulator driver
+ *
+ * Based on cpcap-regulator.c from Motorola Linux kernel tree
+ * Copyright (C) 2009-2011 Motorola, Inc.
+ *
+ * Rewritten for mainline kernel to use device tree and regmap
+ * Copyright (C) 2017 Tony Lindgren <tony@atomide.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 <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/motorola-cpcap.h>
+
+/*
+ * Resource assignment register bits. These seem to control the state
+ * idle modes adn are used at least for omap4.
+ */
+
+/* CPCAP_REG_ASSIGN2 bits - Resource Assignment 2 */
+#define CPCAP_BIT_VSDIO_SEL BIT(15)
+#define CPCAP_BIT_VDIG_SEL BIT(14)
+#define CPCAP_BIT_VCAM_SEL BIT(13)
+#define CPCAP_BIT_SW6_SEL BIT(12)
+#define CPCAP_BIT_SW5_SEL BIT(11)
+#define CPCAP_BIT_SW4_SEL BIT(10)
+#define CPCAP_BIT_SW3_SEL BIT(9)
+#define CPCAP_BIT_SW2_SEL BIT(8)
+#define CPCAP_BIT_SW1_SEL BIT(7)
+
+/* CPCAP_REG_ASSIGN3 bits - Resource Assignment 3 */
+#define CPCAP_BIT_VUSBINT2_SEL BIT(15)
+#define CPCAP_BIT_VUSBINT1_SEL BIT(14)
+#define CPCAP_BIT_VVIB_SEL BIT(13)
+#define CPCAP_BIT_VWLAN1_SEL BIT(12)
+#define CPCAP_BIT_VRF1_SEL BIT(11)
+#define CPCAP_BIT_VHVIO_SEL BIT(10)
+#define CPCAP_BIT_VDAC_SEL BIT(9)
+#define CPCAP_BIT_VUSB_SEL BIT(8)
+#define CPCAP_BIT_VSIM_SEL BIT(7)
+#define CPCAP_BIT_VRFREF_SEL BIT(6)
+#define CPCAP_BIT_VPLL_SEL BIT(5)
+#define CPCAP_BIT_VFUSE_SEL BIT(4)
+#define CPCAP_BIT_VCSI_SEL BIT(3)
+#define CPCAP_BIT_SPARE_14_2 BIT(2)
+#define CPCAP_BIT_VWLAN2_SEL BIT(1)
+#define CPCAP_BIT_VRF2_SEL BIT(0)
+
+/* CPCAP_REG_ASSIGN4 bits - Resource Assignment 4 */
+#define CPCAP_BIT_VAUDIO_SEL BIT(0)
+
+/*
+ * Enable register bits. At least CPCAP_BIT_AUDIO_LOW_PWR is generic,
+ * and not limited to audio regulator. Let's use the Motorola kernel
+ * naming for now until we have a better understanding of the other
+ * enable register bits. No idea why BIT(3) is not defined.
+ */
+#define CPCAP_BIT_AUDIO_LOW_PWR BIT(6)
+#define CPCAP_BIT_AUD_LOWPWR_SPEED BIT(5)
+#define CPCAP_BIT_VAUDIOPRISTBY BIT(4)
+#define CPCAP_BIT_VAUDIO_MODE1 BIT(2)
+#define CPCAP_BIT_VAUDIO_MODE0 BIT(1)
+#define CPCAP_BIT_V_AUDIO_EN BIT(0)
+
+#define CPCAP_BIT_AUDIO_NORMAL_MODE 0x00
+
+/*
+ * Off mode configuration bit. Used currently only by SW5 on omap4. There's
+ * the following comment in Motorola Linux kernel tree for it:
+ *
+ * When set in the regulator mode, the regulator assignment will be changed
+ * to secondary when the regulator is disabled. The mode will be set back to
+ * primary when the regulator is turned on.
+ */
+#define CPCAP_REG_OFF_MODE_SEC BIT(15)
+
+/**
+ * SoC specific configuraion for CPCAP regulator. There are at least three
+ * different SoCs each with their own parameters: omap3, omap4 and tegra2.
+ *
+ * The assign_reg and assign_mask seem to allow toggling between primary
+ * and secondary mode that at least omap4 uses for off mode.
+ */
+struct cpcap_regulator {
+ struct regulator_desc rdesc;
+ const u16 assign_reg;
+ const u16 assign_mask;
+ const u16 vsel_shift;
+};
+
+#define CPCAP_REG(_ID, reg, assignment_reg, assignment_mask, val_tbl, \
+ mode_mask, volt_mask, volt_shft, \
+ mode_val, off_val, volt_trans_time) { \
+ .rdesc = { \
+ .name = #_ID, \
+ .of_match = of_match_ptr(#_ID), \
+ .ops = &cpcap_regulator_ops, \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = CPCAP_##_ID, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(val_tbl), \
+ .volt_table = (val_tbl), \
+ .vsel_reg = (reg), \
+ .vsel_mask = (volt_mask), \
+ .enable_reg = (reg), \
+ .enable_mask = (mode_mask), \
+ .enable_val = (mode_val), \
+ .disable_val = (off_val), \
+ .ramp_delay = (volt_trans_time), \
+ .of_map_mode = cpcap_map_mode, \
+ }, \
+ .assign_reg = (assignment_reg), \
+ .assign_mask = (assignment_mask), \
+ .vsel_shift = (volt_shft), \
+}
+
+struct cpcap_ddata {
+ struct regmap *reg;
+ struct device *dev;
+ const struct cpcap_regulator *soc;
+};
+
+enum cpcap_regulator_id {
+ CPCAP_SW1,
+ CPCAP_SW2,
+ CPCAP_SW3,
+ CPCAP_SW4,
+ CPCAP_SW5,
+ CPCAP_SW6,
+ CPCAP_VCAM,
+ CPCAP_VCSI,
+ CPCAP_VDAC,
+ CPCAP_VDIG,
+ CPCAP_VFUSE,
+ CPCAP_VHVIO,
+ CPCAP_VSDIO,
+ CPCAP_VPLL,
+ CPCAP_VRF1,
+ CPCAP_VRF2,
+ CPCAP_VRFREF,
+ CPCAP_VWLAN1,
+ CPCAP_VWLAN2,
+ CPCAP_VSIM,
+ CPCAP_VSIMCARD,
+ CPCAP_VVIB,
+ CPCAP_VUSB,
+ CPCAP_VAUDIO,
+ CPCAP_NR_REGULATORS,
+};
+
+/*
+ * We need to also configure regulator idle mode for SoC off mode if
+ * CPCAP_REG_OFF_MODE_SEC is set.
+ */
+static int cpcap_regulator_enable(struct regulator_dev *rdev)
+{
+ struct cpcap_regulator *regulator = rdev_get_drvdata(rdev);
+ int error, ignore;
+
+ error = regulator_enable_regmap(rdev);
+ if (error)
+ return error;
+
+ if (rdev->desc->enable_val & CPCAP_REG_OFF_MODE_SEC) {
+ error = regmap_update_bits(rdev->regmap, regulator->assign_reg,
+ regulator->assign_mask,
+ regulator->assign_mask);
+ if (error)
+ ignore = regulator_disable_regmap(rdev);
+ }
+
+ return error;
+}
+
+/*
+ * We need to also configure regulator idle mode for SoC off mode if
+ * CPCAP_REG_OFF_MODE_SEC is set.
+ */
+static int cpcap_regulator_disable(struct regulator_dev *rdev)
+{
+ struct cpcap_regulator *regulator = rdev_get_drvdata(rdev);
+ int error, ignore;
+
+ if (rdev->desc->enable_val & CPCAP_REG_OFF_MODE_SEC) {
+ error = regmap_update_bits(rdev->regmap, regulator->assign_reg,
+ regulator->assign_mask, 0);
+ if (error)
+ return error;
+ }
+
+ error = regulator_disable_regmap(rdev);
+ if (error && (rdev->desc->enable_val & CPCAP_REG_OFF_MODE_SEC)) {
+ ignore = regmap_update_bits(rdev->regmap, regulator->assign_reg,
+ regulator->assign_mask,
+ regulator->assign_mask);
+ }
+
+ return error;
+}
+
+static unsigned int cpcap_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case CPCAP_BIT_AUDIO_NORMAL_MODE:
+ return REGULATOR_MODE_NORMAL;
+ case CPCAP_BIT_AUDIO_LOW_PWR:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+static unsigned int cpcap_regulator_get_mode(struct regulator_dev *rdev)
+{
+ int value;
+
+ regmap_read(rdev->regmap, rdev->desc->enable_reg, &value);
+
+ if (value & CPCAP_BIT_AUDIO_LOW_PWR)
+ return REGULATOR_MODE_STANDBY;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int cpcap_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int value;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ value = CPCAP_BIT_AUDIO_NORMAL_MODE;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ value = CPCAP_BIT_AUDIO_LOW_PWR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ CPCAP_BIT_AUDIO_LOW_PWR, value);
+}
+
+static struct regulator_ops cpcap_regulator_ops = {
+ .enable = cpcap_regulator_enable,
+ .disable = cpcap_regulator_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_mode = cpcap_regulator_get_mode,
+ .set_mode = cpcap_regulator_set_mode,
+};
+
+static const unsigned int unknown_val_tbl[] = { 0, };
+static const unsigned int sw2_sw4_val_tbl[] = { 612500, 625000, 637500,
+ 650000, 662500, 675000,
+ 687500, 700000, 712500,
+ 725000, 737500, 750000,
+ 762500, 775000, 787500,
+ 800000, 812500, 825000,
+ 837500, 850000, 862500,
+ 875000, 887500, 900000,
+ 912500, 925000, 937500,
+ 950000, 962500, 975000,
+ 987500, 1000000, 1012500,
+ 1025000, 1037500, 1050000,
+ 1062500, 1075000, 1087500,
+ 1100000, 1112500, 1125000,
+ 1137500, 1150000, 1162500,
+ 1175000, 1187500, 1200000,
+ 1212500, 1225000, 1237500,
+ 1250000, 1262500, 1275000,
+ 1287500, 1300000, 1312500,
+ 1325000, 1337500, 1350000,
+ 1362500, 1375000, 1387500,
+ 1400000, 1412500, 1425000,
+ 1437500, 1450000, 1462500, };
+static const unsigned int sw5_val_tbl[] = { 0, 5050000, };
+static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000,
+ 2900000, };
+static const unsigned int vcsi_val_tbl[] = { 1200000, 1800000, };
+static const unsigned int vdac_val_tbl[] = { 1200000, 1500000, 1800000,
+ 2500000,};
+static const unsigned int vdig_val_tbl[] = { 1200000, 1350000, 1500000,
+ 1875000, };
+static const unsigned int vfuse_val_tbl[] = { 1500000, 1600000, 1700000,
+ 1800000, 1900000, 2000000,
+ 2100000, 2200000, 2300000,
+ 2400000, 2500000, 2600000,
+ 2700000, 3150000, };
+static const unsigned int vhvio_val_tbl[] = { 2775000, };
+static const unsigned int vsdio_val_tbl[] = { 1500000, 1600000, 1800000,
+ 2600000, 2700000, 2800000,
+ 2900000, 3000000, };
+static const unsigned int vpll_val_tbl[] = { 1200000, 1300000, 1400000,
+ 1800000, };
+/* Quirk: 2775000 is before 2500000 for vrf1 regulator */
+static const unsigned int vrf1_val_tbl[] = { 2775000, 2500000, };
+static const unsigned int vrf2_val_tbl[] = { 0, 2775000, };
+static const unsigned int vrfref_val_tbl[] = { 2500000, 2775000, };
+static const unsigned int vwlan1_val_tbl[] = { 1800000, 1900000, };
+static const unsigned int vwlan2_val_tbl[] = { 2775000, 3000000, 3300000,
+ 3300000, };
+static const unsigned int vsim_val_tbl[] = { 1800000, 2900000, };
+static const unsigned int vsimcard_val_tbl[] = { 1800000, 2900000, };
+static const unsigned int vvib_val_tbl[] = { 1300000, 1800000, 2000000,
+ 3000000, };
+static const unsigned int vusb_val_tbl[] = { 0, 3300000, };
+static const unsigned int vaudio_val_tbl[] = { 0, 2775000, };
+
+/**
+ * SoC specific configuration for omap4. The data below is comes from Motorola
+ * Linux kernel tree. It's basically the values of cpcap_regltr_data,
+ * cpcap_regulator_mode_values and cpcap_regulator_off_mode_values, see
+ * CPCAP_REG macro above.
+ *
+ * SW1 to SW4 and SW6 seems to be unused for mapphone. Note that VSIM and
+ * VSIMCARD have a shared resource assignment bit.
+ */
+static struct cpcap_regulator omap4_regulators[] = {
+ CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW1_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW2_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW3_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW4_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW5_SEL, sw5_val_tbl,
+ 0x28, 0, 0, 0x20 | CPCAP_REG_OFF_MODE_SEC, 0, 0),
+ CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW6_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VCAM_SEL, vcam_val_tbl,
+ 0x87, 0x30, 4, 0x3, 0, 420),
+ CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VCSI_SEL, vcsi_val_tbl,
+ 0x47, 0x10, 4, 0x43, 0x41, 350),
+ CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VDAC_SEL, vdac_val_tbl,
+ 0x87, 0x30, 4, 0x3, 0, 420),
+ CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VDIG_SEL, vdig_val_tbl,
+ 0x87, 0x30, 4, 0x82, 0, 420),
+ CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl,
+ 0x80, 0xf, 0, 0x80, 0, 420),
+ CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl,
+ 0x17, 0, 0, 0, 0x12, 0),
+ CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl,
+ 0x87, 0x38, 3, 0x82, 0, 420),
+ CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VPLL_SEL, vpll_val_tbl,
+ 0x43, 0x18, 3, 0x2, 0, 420),
+ CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRF1_SEL, vrf1_val_tbl,
+ 0xac, 0x2, 1, 0x4, 0, 10),
+ CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRF2_SEL, vrf2_val_tbl,
+ 0x23, 0x8, 3, 0, 0, 10),
+ CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl,
+ 0x23, 0x8, 3, 0, 0, 420),
+ CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl,
+ 0x47, 0x10, 4, 0, 0, 420),
+ CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl,
+ 0x20c, 0xc0, 6, 0x20c, 0, 420),
+ CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+ 0xffff, vsim_val_tbl,
+ 0x23, 0x8, 3, 0x3, 0, 420),
+ CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+ 0xffff, vsimcard_val_tbl,
+ 0x1e80, 0x8, 3, 0x1e00, 0, 420),
+ CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VVIB_SEL, vvib_val_tbl,
+ 0x1, 0xc, 2, 0x1, 0, 500),
+ CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VUSB_SEL, vusb_val_tbl,
+ 0x11c, 0x40, 6, 0xc, 0, 0),
+ CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4,
+ CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl,
+ 0x16, 0x1, 0, 0x4, 0, 0),
+ { /* sentinel */ },
+};
+
+static struct cpcap_regulator xoom_regulators[] = {
+ CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW1_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW2_SEL, sw2_sw4_val_tbl,
+ 0xf00, 0x7f, 0, 0x800, 0, 120),
+ CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW3_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW4_SEL, sw2_sw4_val_tbl,
+ 0xf00, 0x7f, 0, 0x900, 0, 100),
+ CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW5_SEL, sw5_val_tbl,
+ 0x2a, 0, 0, 0x22, 0, 0),
+ CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_SW6_SEL, unknown_val_tbl,
+ 0, 0, 0, 0, 0, 0),
+ CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VCAM_SEL, vcam_val_tbl,
+ 0x87, 0x30, 4, 0x7, 0, 420),
+ CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VCSI_SEL, vcsi_val_tbl,
+ 0x47, 0x10, 4, 0x7, 0, 350),
+ CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VDAC_SEL, vdac_val_tbl,
+ 0x87, 0x30, 4, 0x3, 0, 420),
+ CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VDIG_SEL, vdig_val_tbl,
+ 0x87, 0x30, 4, 0x5, 0, 420),
+ CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl,
+ 0x80, 0xf, 0, 0x80, 0, 420),
+ CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl,
+ 0x17, 0, 0, 0x2, 0, 0),
+ CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2,
+ CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl,
+ 0x87, 0x38, 3, 0x2, 0, 420),
+ CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VPLL_SEL, vpll_val_tbl,
+ 0x43, 0x18, 3, 0x1, 0, 420),
+ CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRF1_SEL, vrf1_val_tbl,
+ 0xac, 0x2, 1, 0xc, 0, 10),
+ CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRF2_SEL, vrf2_val_tbl,
+ 0x23, 0x8, 3, 0x3, 0, 10),
+ CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl,
+ 0x23, 0x8, 3, 0x3, 0, 420),
+ CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl,
+ 0x47, 0x10, 4, 0x5, 0, 420),
+ CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl,
+ 0x20c, 0xc0, 6, 0x8, 0, 420),
+ CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+ 0xffff, vsim_val_tbl,
+ 0x23, 0x8, 3, 0x3, 0, 420),
+ CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+ 0xffff, vsimcard_val_tbl,
+ 0x1e80, 0x8, 3, 0x1e00, 0, 420),
+ CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VVIB_SEL, vvib_val_tbl,
+ 0x1, 0xc, 2, 0, 0x1, 500),
+ CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3,
+ CPCAP_BIT_VUSB_SEL, vusb_val_tbl,
+ 0x11c, 0x40, 6, 0xc, 0, 0),
+ CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4,
+ CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl,
+ 0x16, 0x1, 0, 0x4, 0, 0),
+ { /* sentinel */ },
+};
+
+static const struct of_device_id cpcap_regulator_id_table[] = {
+ {
+ .compatible = "motorola,cpcap-regulator",
+ },
+ {
+ .compatible = "motorola,mapphone-cpcap-regulator",
+ .data = omap4_regulators,
+ },
+ {
+ .compatible = "motorola,xoom-cpcap-regulator",
+ .data = xoom_regulators,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table);
+
+static int cpcap_regulator_probe(struct platform_device *pdev)
+{
+ struct cpcap_ddata *ddata;
+ const struct of_device_id *match;
+ struct regulator_config config;
+ struct regulator_init_data init_data;
+ int i;
+
+ match = of_match_device(of_match_ptr(cpcap_regulator_id_table),
+ &pdev->dev);
+ if (!match)
+ return -EINVAL;
+
+ if (!match->data) {
+ dev_err(&pdev->dev, "no configuration data found\n");
+
+ return -ENODEV;
+ }
+
+ ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ ddata->reg = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!ddata->reg)
+ return -ENODEV;
+
+ ddata->dev = &pdev->dev;
+ ddata->soc = match->data;
+ platform_set_drvdata(pdev, ddata);
+
+ memset(&config, 0, sizeof(config));
+ memset(&init_data, 0, sizeof(init_data));
+ config.dev = &pdev->dev;
+ config.regmap = ddata->reg;
+ config.init_data = &init_data;
+
+ for (i = 0; i < CPCAP_NR_REGULATORS; i++) {
+ const struct cpcap_regulator *regulator = &ddata->soc[i];
+ struct regulator_dev *rdev;
+
+ if (!regulator->rdesc.name)
+ break;
+
+ if (regulator->rdesc.volt_table == unknown_val_tbl)
+ continue;
+
+ config.driver_data = (void *)regulator;
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulator->rdesc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ regulator->rdesc.name);
+
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver cpcap_regulator_driver = {
+ .probe = cpcap_regulator_probe,
+ .driver = {
+ .name = "cpcap-regulator",
+ .of_match_table = of_match_ptr(cpcap_regulator_id_table),
+ },
+};
+
+module_platform_driver(cpcap_regulator_driver);
+
+MODULE_ALIAS("platform:cpcap-regulator");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("CPCAP regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
new file mode 100644
index 000000000..33e8f3b8d
--- /dev/null
+++ b/drivers/regulator/da903x.c
@@ -0,0 +1,498 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/da903x.h>
+
+/* 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, &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 const struct regulator_linear_range da9034_ldo12_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 50000),
+ REGULATOR_LINEAR_RANGE(2700000, 8, 15, 50000),
+};
+
+static const 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 const 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 const 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 const 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 const struct regulator_ops da9034_regulator_ldo12_ops = {
+ .set_voltage_sel = da903x_set_voltage_sel,
+ .get_voltage_sel = da903x_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .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;
+ ri->desc.linear_ranges = da9034_ldo12_ranges;
+ ri->desc.n_linear_ranges = ARRAY_SIZE(da9034_ldo12_ranges);
+ }
+
+ 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 = dev_get_platdata(&pdev->dev);
+ config.driver_data = ri;
+
+ rdev = devm_regulator_register(&pdev->dev, &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 struct platform_driver da903x_regulator_driver = {
+ .driver = {
+ .name = "da903x-regulator",
+ },
+ .probe = da903x_regulator_probe,
+};
+
+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 <eric.miao@marvell.com>"
+ "Mike Rapoport <mike@compulab.co.il>");
+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 000000000..bd91c95f7
--- /dev/null
+++ b/drivers/regulator/da9052-regulator.c
@@ -0,0 +1,488 @@
+/*
+* da9052-regulator.c: Regulator driver for DA9052
+*
+* Copyright(c) 2011 Dialog Semiconductor Ltd.
+*
+* Author: David Dajun Chen <dchen@diasemi.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+#endif
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/pdata.h>
+
+/* 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;
+ unsigned char activate_bit;
+};
+
+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 int da9052_regulator_set_voltage_sel(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 ret;
+
+ ret = da9052_reg_update(regulator->da9052, rdev->desc->vsel_reg,
+ rdev->desc->vsel_mask, selector);
+ if (ret < 0)
+ return ret;
+
+ /* Some LDOs and DCDCs are DVC controlled which requires enabling of
+ * the activate bit to implment the changes on the output.
+ */
+ switch (id) {
+ case DA9052_ID_BUCK1:
+ case DA9052_ID_BUCK2:
+ case DA9052_ID_BUCK3:
+ case DA9052_ID_LDO2:
+ case DA9052_ID_LDO3:
+ ret = da9052_reg_update(regulator->da9052, DA9052_SUPPLY_REG,
+ info->activate_bit, info->activate_bit);
+ break;
+ }
+
+ return ret;
+}
+
+static int da9052_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_sel,
+ unsigned int new_sel)
+{
+ struct da9052_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9052_regulator_info *info = regulator->info;
+ int id = rdev_get_id(rdev);
+ int ret = 0;
+
+ /* The DVC controlled LDOs and DCDCs ramp with 6.25mV/µs after enabling
+ * the activate bit.
+ */
+ switch (id) {
+ case DA9052_ID_BUCK1:
+ case DA9052_ID_BUCK2:
+ case DA9052_ID_BUCK3:
+ case DA9052_ID_LDO2:
+ case DA9052_ID_LDO3:
+ ret = DIV_ROUND_UP(abs(new_sel - old_sel) * info->step_uV,
+ 6250);
+ break;
+ }
+
+ return ret;
+}
+
+static const 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 = da9052_regulator_set_voltage_sel,
+ .set_voltage_time_sel = da9052_regulator_set_voltage_time_sel,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+};
+
+static const 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 = da9052_regulator_set_voltage_sel,
+ .set_voltage_time_sel = da9052_regulator_set_voltage_time_sel,
+ .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,\
+ .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
+ .enable_mask = 1 << (ebits),\
+ },\
+ .min_uV = (min) * 1000,\
+ .max_uV = (max) * 1000,\
+ .step_uV = (step) * 1000,\
+ .activate_bit = (abits),\
+}
+
+#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,\
+ .enable_reg = DA9052_BUCKCORE_REG + DA9052_ID_##_id, \
+ .enable_mask = 1 << (ebits),\
+ },\
+ .min_uV = (min) * 1000,\
+ .max_uV = (max) * 1000,\
+ .step_uV = (step) * 1000,\
+ .activate_bit = (abits),\
+}
+
+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, 950, 2525, 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, 950, 2525, 6, 6, DA9052_SUPPLY_VBMEMGO),
+ DA9052_DCDC(BUCK4, 25, 950, 2525, 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:
+ case DA9053_BC:
+ 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)
+{
+ const struct mfd_cell *cell = mfd_get_cell(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 = dev_get_platdata(da9052->dev);
+ regulator->da9052 = da9052;
+
+ regulator->info = find_regulator_info(regulator->da9052->chip_id,
+ cell->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[cell->id];
+ } else {
+#ifdef CONFIG_OF
+ struct device_node *nproot = da9052->dev->of_node;
+ struct device_node *np;
+
+ if (!nproot)
+ return -ENODEV;
+
+ nproot = of_get_child_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,
+ &regulator->info->reg_desc);
+ config.of_node = np;
+ break;
+ }
+ }
+ of_node_put(nproot);
+#endif
+ }
+
+ regulator->rdev = devm_regulator_register(&pdev->dev,
+ &regulator->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 struct platform_driver da9052_regulator_driver = {
+ .probe = da9052_regulator_probe,
+ .driver = {
+ .name = "da9052-regulator",
+ },
+};
+
+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 <dchen@diasemi.com>");
+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 000000000..f40c3b864
--- /dev/null
+++ b/drivers/regulator/da9055-regulator.c
@@ -0,0 +1,686 @@
+/*
+* Regulator driver for DA9055 PMIC
+*
+* Copyright(c) 2012 Dialog Semiconductor Ltd.
+*
+* Author: David Dajun Chen <dchen@diasemi.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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+#include <linux/mfd/da9055/pdata.h>
+
+#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 const 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 const 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)
+ return 0;
+
+ if (pdata->gpio_ren && pdata->gpio_ren[id]) {
+ char name[18];
+ int gpio_mux = pdata->gpio_ren[id];
+
+ config->ena_gpiod = pdata->ena_gpiods[id];
+ 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;
+}
+
+#ifdef CONFIG_OF
+static struct of_regulator_match da9055_reg_matches[] = {
+ { .name = "BUCK1", },
+ { .name = "BUCK2", },
+ { .name = "LDO1", },
+ { .name = "LDO2", },
+ { .name = "LDO3", },
+ { .name = "LDO4", },
+ { .name = "LDO5", },
+ { .name = "LDO6", },
+};
+
+static int da9055_regulator_dt_init(struct platform_device *pdev,
+ struct da9055_regulator *regulator,
+ struct regulator_config *config,
+ int regid)
+{
+ struct device_node *nproot, *np;
+ int ret;
+
+ nproot = of_node_get(pdev->dev.parent->of_node);
+ if (!nproot)
+ return -ENODEV;
+
+ np = of_get_child_by_name(nproot, "regulators");
+ if (!np)
+ return -ENODEV;
+
+ ret = of_regulator_match(&pdev->dev, np, &da9055_reg_matches[regid], 1);
+ of_node_put(nproot);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error matching regulator: %d\n", ret);
+ return ret;
+ }
+
+ config->init_data = da9055_reg_matches[regid].init_data;
+ config->of_node = da9055_reg_matches[regid].of_node;
+
+ if (!config->of_node)
+ return -ENODEV;
+
+ return 0;
+}
+#else
+static inline int da9055_regulator_dt_init(struct platform_device *pdev,
+ struct da9055_regulator *regulator,
+ struct regulator_config *config,
+ int regid)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
+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 = dev_get_platdata(da9055->dev);
+ int ret, irq;
+
+ 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];
+ } else {
+ ret = da9055_regulator_dt_init(pdev, regulator, &config,
+ pdev->id);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = da9055_gpio_init(regulator, &config, pdata, pdev->id);
+ if (ret < 0)
+ return ret;
+
+ regulator->rdev = devm_regulator_register(&pdev->dev,
+ &regulator->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);
+ }
+
+ /* 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");
+ if (irq < 0)
+ return 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);
+ return ret;
+ }
+ }
+ }
+
+ platform_set_drvdata(pdev, regulator);
+
+ return 0;
+}
+
+static struct platform_driver da9055_regulator_driver = {
+ .probe = da9055_regulator_probe,
+ .driver = {
+ .name = "da9055-regulator",
+ },
+};
+
+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 <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9055 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-regulator");
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
new file mode 100644
index 000000000..34a70d9dc
--- /dev/null
+++ b/drivers/regulator/da9062-regulator.c
@@ -0,0 +1,1120 @@
+/*
+ * Regulator device driver for DA9061 and DA9062.
+ * Copyright (C) 2015-2017 Dialog Semiconductor
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Regulator IDs */
+enum {
+ DA9061_ID_BUCK1,
+ DA9061_ID_BUCK2,
+ DA9061_ID_BUCK3,
+ DA9061_ID_LDO1,
+ DA9061_ID_LDO2,
+ DA9061_ID_LDO3,
+ DA9061_ID_LDO4,
+ DA9061_MAX_REGULATORS,
+};
+
+enum {
+ DA9062_ID_BUCK1,
+ DA9062_ID_BUCK2,
+ DA9062_ID_BUCK3,
+ DA9062_ID_BUCK4,
+ DA9062_ID_LDO1,
+ DA9062_ID_LDO2,
+ DA9062_ID_LDO3,
+ DA9062_ID_LDO4,
+ DA9062_MAX_REGULATORS,
+};
+
+/* Regulator capabilities and registers description */
+struct da9062_regulator_info {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned int n_current_limits;
+ const int *current_limits;
+ /* Main register fields */
+ struct reg_field mode;
+ struct reg_field suspend;
+ struct reg_field sleep;
+ struct reg_field suspend_sleep;
+ unsigned int suspend_vsel_reg;
+ struct reg_field ilimit;
+ /* Event detection bit */
+ struct reg_field oc_event;
+};
+
+/* Single regulator settings */
+struct da9062_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct da9062 *hw;
+ const struct da9062_regulator_info *info;
+
+ struct regmap_field *mode;
+ struct regmap_field *suspend;
+ struct regmap_field *sleep;
+ struct regmap_field *suspend_sleep;
+ struct regmap_field *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9062_regulators {
+ int irq_ldo_lim;
+ unsigned n_regulators;
+ /* Array size to be defined during init. Keep at end. */
+ struct da9062_regulator regulator[0];
+};
+
+/* BUCK modes */
+enum {
+ BUCK_MODE_MANUAL, /* 0 */
+ BUCK_MODE_SLEEP, /* 1 */
+ BUCK_MODE_SYNC, /* 2 */
+ BUCK_MODE_AUTO /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA)
+ * - DA9061_ID_[BUCK1|BUCK3]
+ * - DA9062_ID_[BUCK1|BUCK2|BUCK4]
+ * Entry indexes corresponds to register values.
+ */
+static const int da9062_buck_a_limits[] = {
+ 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
+ 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA)
+ * - DA9061_ID_BUCK2
+ * - DA9062_ID_BUCK3
+ * Entry indexes corresponds to register values.
+ */
+static const int da9062_buck_b_limits[] = {
+ 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+ 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+static int da9062_set_current_limit(struct regulator_dev *rdev,
+ int min_ua, int max_ua)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int n, tval;
+
+ for (n = 0; n < rinfo->n_current_limits; n++) {
+ tval = rinfo->current_limits[n];
+ if (tval >= min_ua && tval <= max_ua)
+ return regmap_field_write(regl->ilimit, n);
+ }
+
+ return -EINVAL;
+}
+
+static int da9062_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_field_read(regl->ilimit, &sel);
+ if (ret < 0)
+ return ret;
+
+ if (sel >= rinfo->n_current_limits)
+ sel = rinfo->n_current_limits - 1;
+
+ return rinfo->current_limits[sel];
+}
+
+static int da9062_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9062_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ unsigned int val, mode = 0;
+ int ret;
+
+ ret = regmap_field_read(regl->mode, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ default:
+ case BUCK_MODE_MANUAL:
+ mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+ /* Sleep flag bit decides the mode */
+ break;
+ case BUCK_MODE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ case BUCK_MODE_SYNC:
+ return REGULATOR_MODE_FAST;
+ case BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ }
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ mode &= REGULATOR_MODE_STANDBY;
+ else
+ mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+ return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9062_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9062_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ int ret, val;
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9062_buck_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_buck_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_ldo_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_ldo_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int ret, sel;
+
+ sel = regulator_map_voltage_linear(rdev, uv, uv);
+ if (sel < 0)
+ return sel;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+ rdev->desc->vsel_mask, sel);
+
+ return ret;
+}
+
+static int da9062_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9062_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9062_buck_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static const struct regulator_ops da9062_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9062_set_current_limit,
+ .get_current_limit = da9062_get_current_limit,
+ .set_mode = da9062_buck_set_mode,
+ .get_mode = da9062_buck_get_mode,
+ .get_status = da9062_buck_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_buck_set_suspend_mode,
+};
+
+static const struct regulator_ops da9062_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = da9062_ldo_set_mode,
+ .get_mode = da9062_ldo_get_mode,
+ .get_status = da9062_ldo_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_ldo_set_suspend_mode,
+};
+
+/* DA9061 Regulator information */
+static const struct da9062_regulator_info local_da9061_regulator_info[] = {
+ {
+ .desc.id = DA9061_ID_BUCK1,
+ .desc.name = "DA9061 BUCK1",
+ .desc.of_match = of_match_ptr("buck1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK1_CONT,
+ .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK1_A,
+ .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+ .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9061_ID_BUCK2,
+ .desc.name = "DA9061 BUCK2",
+ .desc.of_match = of_match_ptr("buck2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (800) * 1000,
+ .desc.uV_step = (20) * 1000,
+ .desc.n_voltages = ((3340) - (800))/(20) + 1,
+ .current_limits = da9062_buck_b_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+ .desc.enable_reg = DA9062AA_BUCK3_CONT,
+ .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK3_A,
+ .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+ .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9061_ID_BUCK3,
+ .desc.name = "DA9061 BUCK3",
+ .desc.of_match = of_match_ptr("buck3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (530) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1800) - (530))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK4_CONT,
+ .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK4_A,
+ .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+ .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9061_ID_LDO1,
+ .desc.name = "DA9061 LDO1",
+ .desc.of_match = of_match_ptr("ldo1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO1_CONT,
+ .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO1_A,
+ .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO1_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9061_ID_LDO2,
+ .desc.name = "DA9061 LDO2",
+ .desc.of_match = of_match_ptr("ldo2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (600))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO2_CONT,
+ .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO2_A,
+ .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO2_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9061_ID_LDO3,
+ .desc.name = "DA9061 LDO3",
+ .desc.of_match = of_match_ptr("ldo3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO3_CONT,
+ .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO3_A,
+ .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO3_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9061_ID_LDO4,
+ .desc.name = "DA9061 LDO4",
+ .desc.of_match = of_match_ptr("ldo4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO4_CONT,
+ .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO4_A,
+ .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO4_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+ },
+};
+
+/* DA9062 Regulator information */
+static const struct da9062_regulator_info local_da9062_regulator_info[] = {
+ {
+ .desc.id = DA9062_ID_BUCK1,
+ .desc.name = "DA9062 BUCK1",
+ .desc.of_match = of_match_ptr("buck1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK1_CONT,
+ .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK1_A,
+ .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+ .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK2,
+ .desc.name = "DA9062 BUCK2",
+ .desc.of_match = of_match_ptr("buck2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK2_CONT,
+ .desc.enable_mask = DA9062AA_BUCK2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK2_A,
+ .desc.vsel_mask = DA9062AA_VBUCK2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK2_A,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK2_B,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK2_B,
+ .mode = REG_FIELD(DA9062AA_BUCK2_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK2_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK2_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK3,
+ .desc.name = "DA9062 BUCK3",
+ .desc.of_match = of_match_ptr("buck3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (800) * 1000,
+ .desc.uV_step = (20) * 1000,
+ .desc.n_voltages = ((3340) - (800))/(20) + 1,
+ .current_limits = da9062_buck_b_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+ .desc.enable_reg = DA9062AA_BUCK3_CONT,
+ .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK3_A,
+ .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+ .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK4,
+ .desc.name = "DA9062 BUCK4",
+ .desc.of_match = of_match_ptr("buck4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (530) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1800) - (530))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK4_CONT,
+ .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK4_A,
+ .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+ .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO1,
+ .desc.name = "DA9062 LDO1",
+ .desc.of_match = of_match_ptr("ldo1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO1_CONT,
+ .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO1_A,
+ .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO1_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO2,
+ .desc.name = "DA9062 LDO2",
+ .desc.of_match = of_match_ptr("ldo2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (600))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO2_CONT,
+ .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO2_A,
+ .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO2_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO3,
+ .desc.name = "DA9062 LDO3",
+ .desc.of_match = of_match_ptr("ldo3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO3_CONT,
+ .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO3_A,
+ .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO3_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO4,
+ .desc.name = "DA9062 LDO4",
+ .desc.of_match = of_match_ptr("ldo4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO4_CONT,
+ .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO4_A,
+ .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO4_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+ },
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
+{
+ struct da9062_regulators *regulators = data;
+ struct da9062 *hw = regulators->regulator[0].hw;
+ struct da9062_regulator *regl;
+ int handled = IRQ_NONE;
+ int bits, i, ret;
+
+ ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Failed to read LDO overcurrent indicator\n");
+ goto ldo_lim_error;
+ }
+
+ for (i = regulators->n_regulators - 1; i >= 0; i--) {
+ regl = &regulators->regulator[i];
+ if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
+ continue;
+
+ if (BIT(regl->info->oc_event.lsb) & bits) {
+ regulator_notifier_call_chain(regl->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+ handled = IRQ_HANDLED;
+ }
+ }
+
+ldo_lim_error:
+ return handled;
+}
+
+static int da9062_regulator_probe(struct platform_device *pdev)
+{
+ struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+ struct da9062_regulators *regulators;
+ struct da9062_regulator *regl;
+ struct regulator_config config = { };
+ const struct da9062_regulator_info *rinfo;
+ int irq, n, ret;
+ size_t size;
+ int max_regulators;
+
+ switch (chip->chip_type) {
+ case COMPAT_TYPE_DA9061:
+ max_regulators = DA9061_MAX_REGULATORS;
+ rinfo = local_da9061_regulator_info;
+ break;
+ case COMPAT_TYPE_DA9062:
+ max_regulators = DA9062_MAX_REGULATORS;
+ rinfo = local_da9062_regulator_info;
+ break;
+ default:
+ dev_err(chip->dev, "Unrecognised chip type\n");
+ return -ENODEV;
+ }
+
+ /* Allocate memory required by usable regulators */
+ size = sizeof(struct da9062_regulators) +
+ max_regulators * sizeof(struct da9062_regulator);
+ regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (!regulators)
+ return -ENOMEM;
+
+ regulators->n_regulators = max_regulators;
+ platform_set_drvdata(pdev, regulators);
+
+ n = 0;
+ while (n < regulators->n_regulators) {
+ /* Initialise regulator structure */
+ regl = &regulators->regulator[n];
+ regl->hw = chip;
+ regl->info = &rinfo[n];
+ regl->desc = regl->info->desc;
+ regl->desc.type = REGULATOR_VOLTAGE;
+ regl->desc.owner = THIS_MODULE;
+
+ if (regl->info->mode.reg)
+ regl->mode = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->mode);
+ if (regl->info->suspend.reg)
+ regl->suspend = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend);
+ if (regl->info->sleep.reg)
+ regl->sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->sleep);
+ if (regl->info->suspend_sleep.reg)
+ regl->suspend_sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend_sleep);
+ if (regl->info->ilimit.reg)
+ regl->ilimit = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->ilimit);
+
+ /* Register regulator */
+ memset(&config, 0, sizeof(config));
+ config.dev = chip->dev;
+ config.driver_data = regl;
+ config.regmap = chip->regmap;
+
+ regl->rdev = devm_regulator_register(&pdev->dev, &regl->desc,
+ &config);
+ if (IS_ERR(regl->rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %s regulator\n",
+ regl->desc.name);
+ return PTR_ERR(regl->rdev);
+ }
+
+ n++;
+ }
+
+ /* LDOs overcurrent event support */
+ irq = platform_get_irq_byname(pdev, "LDO_LIM");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ return irq;
+ }
+ regulators->irq_ldo_lim = irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, da9062_ldo_lim_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "LDO_LIM", regulators);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request LDO_LIM IRQ.\n");
+ regulators->irq_ldo_lim = -ENXIO;
+ }
+
+ return 0;
+}
+
+static struct platform_driver da9062_regulator_driver = {
+ .driver = {
+ .name = "da9062-regulators",
+ },
+ .probe = da9062_regulator_probe,
+};
+
+static int __init da9062_regulator_init(void)
+{
+ return platform_driver_register(&da9062_regulator_driver);
+}
+subsys_initcall(da9062_regulator_init);
+
+static void __exit da9062_regulator_cleanup(void)
+{
+ platform_driver_unregister(&da9062_regulator_driver);
+}
+module_exit(da9062_regulator_cleanup);
+
+/* Module information */
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("REGULATOR device driver for Dialog DA9062 and DA9061");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9062-regulators");
diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c
new file mode 100644
index 000000000..8cbcd2a3e
--- /dev/null
+++ b/drivers/regulator/da9063-regulator.c
@@ -0,0 +1,917 @@
+
+/*
+ * Regulator driver for DA9063 PMIC series
+ *
+ * Copyright 2012 Dialog Semiconductors Ltd.
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+
+
+/* Definition for registering regmap bit fields using a mask */
+#define BFIELD(_reg, _mask) \
+ REG_FIELD(_reg, __builtin_ffs((int)_mask) - 1, \
+ sizeof(unsigned int) * 8 - __builtin_clz((_mask)) - 1)
+
+/* Regulator capabilities and registers description */
+struct da9063_regulator_info {
+ struct regulator_desc desc;
+
+ /* Current limiting */
+ unsigned n_current_limits;
+ const int *current_limits;
+
+ /* DA9063 main register fields */
+ struct reg_field mode; /* buck mode of operation */
+ struct reg_field suspend;
+ struct reg_field sleep;
+ struct reg_field suspend_sleep;
+ unsigned int suspend_vsel_reg;
+ struct reg_field ilimit;
+
+ /* DA9063 event detection bit */
+ struct reg_field oc_event;
+};
+
+/* Macros for LDO */
+#define DA9063_LDO(chip, regl_name, min_mV, step_mV, max_mV) \
+ .desc.id = chip##_ID_##regl_name, \
+ .desc.name = __stringify(chip##_##regl_name), \
+ .desc.ops = &da9063_ldo_ops, \
+ .desc.min_uV = (min_mV) * 1000, \
+ .desc.uV_step = (step_mV) * 1000, \
+ .desc.n_voltages = (((max_mV) - (min_mV))/(step_mV) + 1 \
+ + (DA9063_V##regl_name##_BIAS)), \
+ .desc.enable_reg = DA9063_REG_##regl_name##_CONT, \
+ .desc.enable_mask = DA9063_LDO_EN, \
+ .desc.vsel_reg = DA9063_REG_V##regl_name##_A, \
+ .desc.vsel_mask = DA9063_V##regl_name##_MASK, \
+ .desc.linear_min_sel = DA9063_V##regl_name##_BIAS, \
+ .sleep = BFIELD(DA9063_REG_V##regl_name##_A, DA9063_LDO_SL), \
+ .suspend_sleep = BFIELD(DA9063_REG_V##regl_name##_B, DA9063_LDO_SL), \
+ .suspend_vsel_reg = DA9063_REG_V##regl_name##_B
+
+/* Macros for voltage DC/DC converters (BUCKs) */
+#define DA9063_BUCK(chip, regl_name, min_mV, step_mV, max_mV, limits_array) \
+ .desc.id = chip##_ID_##regl_name, \
+ .desc.name = __stringify(chip##_##regl_name), \
+ .desc.ops = &da9063_buck_ops, \
+ .desc.min_uV = (min_mV) * 1000, \
+ .desc.uV_step = (step_mV) * 1000, \
+ .desc.n_voltages = ((max_mV) - (min_mV))/(step_mV) + 1, \
+ .current_limits = limits_array, \
+ .n_current_limits = ARRAY_SIZE(limits_array)
+
+#define DA9063_BUCK_COMMON_FIELDS(regl_name) \
+ .desc.enable_reg = DA9063_REG_##regl_name##_CONT, \
+ .desc.enable_mask = DA9063_BUCK_EN, \
+ .desc.vsel_reg = DA9063_REG_V##regl_name##_A, \
+ .desc.vsel_mask = DA9063_VBUCK_MASK, \
+ .desc.linear_min_sel = DA9063_VBUCK_BIAS, \
+ .sleep = BFIELD(DA9063_REG_V##regl_name##_A, DA9063_BUCK_SL), \
+ .suspend_sleep = BFIELD(DA9063_REG_V##regl_name##_B, DA9063_BUCK_SL), \
+ .suspend_vsel_reg = DA9063_REG_V##regl_name##_B, \
+ .mode = BFIELD(DA9063_REG_##regl_name##_CFG, DA9063_BUCK_MODE_MASK)
+
+/* Defines asignment of regulators info table to chip model */
+struct da9063_dev_model {
+ const struct da9063_regulator_info *regulator_info;
+ unsigned n_regulators;
+ enum da9063_type type;
+};
+
+/* Single regulator settings */
+struct da9063_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct da9063 *hw;
+ const struct da9063_regulator_info *info;
+
+ struct regmap_field *mode;
+ struct regmap_field *suspend;
+ struct regmap_field *sleep;
+ struct regmap_field *suspend_sleep;
+ struct regmap_field *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9063_regulators {
+ unsigned n_regulators;
+ /* Array size to be defined during init. Keep at end. */
+ struct da9063_regulator regulator[0];
+};
+
+/* BUCK modes for DA9063 */
+enum {
+ BUCK_MODE_MANUAL, /* 0 */
+ BUCK_MODE_SLEEP, /* 1 */
+ BUCK_MODE_SYNC, /* 2 */
+ BUCK_MODE_AUTO /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) for BCORE1, BCORE2, BPRO.
+ Entry indexes corresponds to register values. */
+static const int da9063_buck_a_limits[] = {
+ 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
+ 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BMEM, BIO, BPERI.
+ Entry indexes corresponds to register values. */
+static const int da9063_buck_b_limits[] = {
+ 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+ 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+/* Current limits array (in uA) for merged BCORE1 and BCORE2.
+ Entry indexes corresponds to register values. */
+static const int da9063_bcores_merged_limits[] = {
+ 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000,
+ 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, 3800000, 4000000
+};
+
+/* Current limits array (in uA) for merged BMEM and BIO.
+ Entry indexes corresponds to register values. */
+static const int da9063_bmem_bio_merged_limits[] = {
+ 3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000,
+ 4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000
+};
+
+static int da9063_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9063_regulator_info *rinfo = regl->info;
+ int n, tval;
+
+ for (n = 0; n < rinfo->n_current_limits; n++) {
+ tval = rinfo->current_limits[n];
+ if (tval >= min_uA && tval <= max_uA)
+ return regmap_field_write(regl->ilimit, n);
+ }
+
+ return -EINVAL;
+}
+
+static int da9063_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9063_regulator_info *rinfo = regl->info;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_field_read(regl->ilimit, &sel);
+ if (ret < 0)
+ return ret;
+
+ if (sel >= rinfo->n_current_limits)
+ sel = rinfo->n_current_limits - 1;
+
+ return rinfo->current_limits[sel];
+}
+
+static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9063_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ unsigned int val, mode = 0;
+ int ret;
+
+ ret = regmap_field_read(regl->mode, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ default:
+ case BUCK_MODE_MANUAL:
+ mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+ /* Sleep flag bit decides the mode */
+ break;
+ case BUCK_MODE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ case BUCK_MODE_SYNC:
+ return REGULATOR_MODE_FAST;
+ case BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ }
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ mode &= REGULATOR_MODE_STANDBY;
+ else
+ mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+ return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9063_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ int ret, val;
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9063_buck_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9063_buck_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9063_ldo_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9063_ldo_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9063_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9063_regulator_info *rinfo = regl->info;
+ int ret, sel;
+
+ sel = regulator_map_voltage_linear(rdev, uV, uV);
+ if (sel < 0)
+ return sel;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+ rdev->desc->vsel_mask, sel);
+
+ return ret;
+}
+
+static int da9063_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9063_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static const struct regulator_ops da9063_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9063_set_current_limit,
+ .get_current_limit = da9063_get_current_limit,
+ .set_mode = da9063_buck_set_mode,
+ .get_mode = da9063_buck_get_mode,
+ .get_status = da9063_buck_get_status,
+ .set_suspend_voltage = da9063_set_suspend_voltage,
+ .set_suspend_enable = da9063_suspend_enable,
+ .set_suspend_disable = da9063_suspend_disable,
+ .set_suspend_mode = da9063_buck_set_suspend_mode,
+};
+
+static const struct regulator_ops da9063_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = da9063_ldo_set_mode,
+ .get_mode = da9063_ldo_get_mode,
+ .get_status = da9063_ldo_get_status,
+ .set_suspend_voltage = da9063_set_suspend_voltage,
+ .set_suspend_enable = da9063_suspend_enable,
+ .set_suspend_disable = da9063_suspend_disable,
+ .set_suspend_mode = da9063_ldo_set_suspend_mode,
+};
+
+/* Info of regulators for DA9063 */
+static const struct da9063_regulator_info da9063_regulator_info[] = {
+ {
+ DA9063_BUCK(DA9063, BCORE1, 300, 10, 1570,
+ da9063_buck_a_limits),
+ DA9063_BUCK_COMMON_FIELDS(BCORE1),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE1_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C,
+ DA9063_BCORE1_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BCORE2, 300, 10, 1570,
+ da9063_buck_a_limits),
+ DA9063_BUCK_COMMON_FIELDS(BCORE2),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE2_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C,
+ DA9063_BCORE2_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BPRO, 530, 10, 1800,
+ da9063_buck_a_limits),
+ DA9063_BUCK_COMMON_FIELDS(BPRO),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBPRO_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_B,
+ DA9063_BPRO_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BMEM, 800, 20, 3340,
+ da9063_buck_b_limits),
+ DA9063_BUCK_COMMON_FIELDS(BMEM),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBMEM_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A,
+ DA9063_BMEM_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BIO, 800, 20, 3340,
+ da9063_buck_b_limits),
+ DA9063_BUCK_COMMON_FIELDS(BIO),
+ .suspend = BFIELD(DA9063_REG_DVC_2, DA9063_VBIO_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A,
+ DA9063_BIO_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BPERI, 800, 20, 3340,
+ da9063_buck_b_limits),
+ DA9063_BUCK_COMMON_FIELDS(BPERI),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBPERI_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_B,
+ DA9063_BPERI_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BCORES_MERGED, 300, 10, 1570,
+ da9063_bcores_merged_limits),
+ /* BCORES_MERGED uses the same register fields as BCORE1 */
+ DA9063_BUCK_COMMON_FIELDS(BCORE1),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE1_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C,
+ DA9063_BCORE1_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BMEM_BIO_MERGED, 800, 20, 3340,
+ da9063_bmem_bio_merged_limits),
+ /* BMEM_BIO_MERGED uses the same register fields as BMEM */
+ DA9063_BUCK_COMMON_FIELDS(BMEM),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBMEM_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A,
+ DA9063_BMEM_ILIM_MASK),
+ },
+ {
+ DA9063_LDO(DA9063, LDO3, 900, 20, 3440),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO3_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO3_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO7, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO7_CONT, DA9063_VLDO7_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO7_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO8, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO8_CONT, DA9063_VLDO8_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO8_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO9, 950, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO9_CONT, DA9063_VLDO9_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO11, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO11_CONT, DA9063_VLDO11_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO11_LIM),
+ },
+
+ /* The following LDOs are present only on DA9063, not on DA9063L */
+ {
+ DA9063_LDO(DA9063, LDO1, 600, 20, 1860),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO1_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO2, 600, 20, 1860),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO2_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO4, 900, 20, 3440),
+ .suspend = BFIELD(DA9063_REG_DVC_2, DA9063_VLDO4_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO4_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO5, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO5_CONT, DA9063_VLDO5_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO6, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO6_CONT, DA9063_VLDO6_SEL),
+ },
+
+ {
+ DA9063_LDO(DA9063, LDO10, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO10_CONT, DA9063_VLDO10_SEL),
+ },
+};
+
+/* Link chip model with regulators info table */
+static struct da9063_dev_model regulators_models[] = {
+ {
+ .regulator_info = da9063_regulator_info,
+ .n_regulators = ARRAY_SIZE(da9063_regulator_info),
+ .type = PMIC_TYPE_DA9063,
+ },
+ {
+ .regulator_info = da9063_regulator_info,
+ .n_regulators = ARRAY_SIZE(da9063_regulator_info) - 6,
+ .type = PMIC_TYPE_DA9063L,
+ },
+ { }
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9063_ldo_lim_event(int irq, void *data)
+{
+ struct da9063_regulators *regulators = data;
+ struct da9063 *hw = regulators->regulator[0].hw;
+ struct da9063_regulator *regl;
+ int bits, i , ret;
+
+ ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = regulators->n_regulators - 1; i >= 0; i--) {
+ regl = &regulators->regulator[i];
+ if (regl->info->oc_event.reg != DA9063_REG_STATUS_D)
+ continue;
+
+ if (BIT(regl->info->oc_event.lsb) & bits)
+ regulator_notifier_call_chain(regl->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Probing and Initialisation functions
+ */
+static const struct regulator_init_data *da9063_get_regulator_initdata(
+ const struct da9063_regulators_pdata *regl_pdata, int id)
+{
+ int i;
+
+ for (i = 0; i < regl_pdata->n_regulators; i++) {
+ if (id == regl_pdata->regulator_data[i].id)
+ return regl_pdata->regulator_data[i].initdata;
+ }
+
+ return NULL;
+}
+
+#ifdef CONFIG_OF
+static struct of_regulator_match da9063_matches[] = {
+ [DA9063_ID_BCORE1] = { .name = "bcore1" },
+ [DA9063_ID_BCORE2] = { .name = "bcore2" },
+ [DA9063_ID_BPRO] = { .name = "bpro", },
+ [DA9063_ID_BMEM] = { .name = "bmem", },
+ [DA9063_ID_BIO] = { .name = "bio", },
+ [DA9063_ID_BPERI] = { .name = "bperi", },
+ [DA9063_ID_BCORES_MERGED] = { .name = "bcores-merged" },
+ [DA9063_ID_BMEM_BIO_MERGED] = { .name = "bmem-bio-merged", },
+ [DA9063_ID_LDO3] = { .name = "ldo3", },
+ [DA9063_ID_LDO7] = { .name = "ldo7", },
+ [DA9063_ID_LDO8] = { .name = "ldo8", },
+ [DA9063_ID_LDO9] = { .name = "ldo9", },
+ [DA9063_ID_LDO11] = { .name = "ldo11", },
+ /* The following LDOs are present only on DA9063, not on DA9063L */
+ [DA9063_ID_LDO1] = { .name = "ldo1", },
+ [DA9063_ID_LDO2] = { .name = "ldo2", },
+ [DA9063_ID_LDO4] = { .name = "ldo4", },
+ [DA9063_ID_LDO5] = { .name = "ldo5", },
+ [DA9063_ID_LDO6] = { .name = "ldo6", },
+ [DA9063_ID_LDO10] = { .name = "ldo10", },
+};
+
+static struct da9063_regulators_pdata *da9063_parse_regulators_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **da9063_reg_matches)
+{
+ struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+ struct da9063_regulators_pdata *pdata;
+ struct da9063_regulator_data *rdata;
+ struct device_node *node;
+ int da9063_matches_len = ARRAY_SIZE(da9063_matches);
+ int i, n, num;
+
+ if (da9063->type == PMIC_TYPE_DA9063L)
+ da9063_matches_len -= 6;
+
+ node = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
+ if (!node) {
+ dev_err(&pdev->dev, "Regulators device node not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ num = of_regulator_match(&pdev->dev, node, da9063_matches,
+ da9063_matches_len);
+ of_node_put(node);
+ if (num < 0) {
+ dev_err(&pdev->dev, "Failed to match regulators\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->regulator_data = devm_kcalloc(&pdev->dev,
+ num, sizeof(*pdata->regulator_data),
+ GFP_KERNEL);
+ if (!pdata->regulator_data)
+ return ERR_PTR(-ENOMEM);
+ pdata->n_regulators = num;
+
+ n = 0;
+ for (i = 0; i < da9063_matches_len; i++) {
+ if (!da9063_matches[i].init_data)
+ continue;
+
+ rdata = &pdata->regulator_data[n];
+ rdata->id = i;
+ rdata->initdata = da9063_matches[i].init_data;
+
+ n++;
+ }
+
+ *da9063_reg_matches = da9063_matches;
+ return pdata;
+}
+#else
+static struct da9063_regulators_pdata *da9063_parse_regulators_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **da9063_reg_matches)
+{
+ *da9063_reg_matches = NULL;
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
+static int da9063_regulator_probe(struct platform_device *pdev)
+{
+ struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+ struct da9063_pdata *da9063_pdata = dev_get_platdata(da9063->dev);
+ struct of_regulator_match *da9063_reg_matches = NULL;
+ struct da9063_regulators_pdata *regl_pdata;
+ const struct da9063_dev_model *model;
+ struct da9063_regulators *regulators;
+ struct da9063_regulator *regl;
+ struct regulator_config config;
+ bool bcores_merged, bmem_bio_merged;
+ int id, irq, n, n_regulators, ret, val;
+ size_t size;
+
+ regl_pdata = da9063_pdata ? da9063_pdata->regulators_pdata : NULL;
+
+ if (!regl_pdata)
+ regl_pdata = da9063_parse_regulators_dt(pdev,
+ &da9063_reg_matches);
+
+ if (IS_ERR(regl_pdata) || regl_pdata->n_regulators == 0) {
+ dev_err(&pdev->dev,
+ "No regulators defined for the platform\n");
+ return -ENODEV;
+ }
+
+ /* Find regulators set for particular device model */
+ for (model = regulators_models; model->regulator_info; model++) {
+ if (model->type == da9063->type)
+ break;
+ }
+ if (!model->regulator_info) {
+ dev_err(&pdev->dev, "Chip model not recognised (%u)\n",
+ da9063->type);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(da9063->regmap, DA9063_REG_CONFIG_H, &val);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Error while reading BUCKs configuration\n");
+ return ret;
+ }
+ bcores_merged = val & DA9063_BCORE_MERGE;
+ bmem_bio_merged = val & DA9063_BUCK_MERGE;
+
+ n_regulators = model->n_regulators;
+ if (bcores_merged)
+ n_regulators -= 2; /* remove BCORE1, BCORE2 */
+ else
+ n_regulators--; /* remove BCORES_MERGED */
+ if (bmem_bio_merged)
+ n_regulators -= 2; /* remove BMEM, BIO */
+ else
+ n_regulators--; /* remove BMEM_BIO_MERGED */
+
+ /* Allocate memory required by usable regulators */
+ size = sizeof(struct da9063_regulators) +
+ n_regulators * sizeof(struct da9063_regulator);
+ regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (!regulators)
+ return -ENOMEM;
+
+ regulators->n_regulators = n_regulators;
+ platform_set_drvdata(pdev, regulators);
+
+ /* Register all regulators declared in platform information */
+ n = 0;
+ id = 0;
+ while (n < regulators->n_regulators) {
+ /* Skip regulator IDs depending on merge mode configuration */
+ switch (id) {
+ case DA9063_ID_BCORE1:
+ case DA9063_ID_BCORE2:
+ if (bcores_merged) {
+ id++;
+ continue;
+ }
+ break;
+ case DA9063_ID_BMEM:
+ case DA9063_ID_BIO:
+ if (bmem_bio_merged) {
+ id++;
+ continue;
+ }
+ break;
+ case DA9063_ID_BCORES_MERGED:
+ if (!bcores_merged) {
+ id++;
+ continue;
+ }
+ break;
+ case DA9063_ID_BMEM_BIO_MERGED:
+ if (!bmem_bio_merged) {
+ id++;
+ continue;
+ }
+ break;
+ }
+
+ /* Initialise regulator structure */
+ regl = &regulators->regulator[n];
+ regl->hw = da9063;
+ regl->info = &model->regulator_info[id];
+ regl->desc = regl->info->desc;
+ regl->desc.type = REGULATOR_VOLTAGE;
+ regl->desc.owner = THIS_MODULE;
+
+ if (regl->info->mode.reg)
+ regl->mode = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->mode);
+ if (regl->info->suspend.reg)
+ regl->suspend = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->suspend);
+ if (regl->info->sleep.reg)
+ regl->sleep = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->sleep);
+ if (regl->info->suspend_sleep.reg)
+ regl->suspend_sleep = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->suspend_sleep);
+ if (regl->info->ilimit.reg)
+ regl->ilimit = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->ilimit);
+
+ /* Register regulator */
+ memset(&config, 0, sizeof(config));
+ config.dev = &pdev->dev;
+ config.init_data = da9063_get_regulator_initdata(regl_pdata, id);
+ config.driver_data = regl;
+ if (da9063_reg_matches)
+ config.of_node = da9063_reg_matches[id].of_node;
+ config.regmap = da9063->regmap;
+ regl->rdev = devm_regulator_register(&pdev->dev, &regl->desc,
+ &config);
+ if (IS_ERR(regl->rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %s regulator\n",
+ regl->desc.name);
+ return PTR_ERR(regl->rdev);
+ }
+ id++;
+ n++;
+ }
+
+ /* LDOs overcurrent event support */
+ irq = platform_get_irq_byname(pdev, "LDO_LIM");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, da9063_ldo_lim_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "LDO_LIM", regulators);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver da9063_regulator_driver = {
+ .driver = {
+ .name = DA9063_DRVNAME_REGULATORS,
+ },
+ .probe = da9063_regulator_probe,
+};
+
+static int __init da9063_regulator_init(void)
+{
+ return platform_driver_register(&da9063_regulator_driver);
+}
+subsys_initcall(da9063_regulator_init);
+
+static void __exit da9063_regulator_cleanup(void)
+{
+ platform_driver_unregister(&da9063_regulator_driver);
+}
+module_exit(da9063_regulator_cleanup);
+
+
+/* Module information */
+MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>");
+MODULE_DESCRIPTION("DA9063 regulators driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DA9063_DRVNAME_REGULATORS);
diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c
new file mode 100644
index 000000000..d0496d6b0
--- /dev/null
+++ b/drivers/regulator/da9210-regulator.c
@@ -0,0 +1,293 @@
+/*
+ * da9210-regulator.c - Regulator device driver for DA9210
+ * Copyright (C) 2013 Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+
+#include "da9210-regulator.h"
+
+struct da9210 {
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+};
+
+static const struct regmap_config da9210_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA,
+ int max_uA);
+static int da9210_get_current_limit(struct regulator_dev *rdev);
+
+static const struct regulator_ops da9210_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9210_set_current_limit,
+ .get_current_limit = da9210_get_current_limit,
+};
+
+/* Default limits measured in millivolts and milliamps */
+#define DA9210_MIN_MV 300
+#define DA9210_MAX_MV 1570
+#define DA9210_STEP_MV 10
+
+/* Current limits for buck (uA) indices corresponds with register values */
+static const int da9210_buck_limits[] = {
+ 1600000, 1800000, 2000000, 2200000, 2400000, 2600000, 2800000, 3000000,
+ 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, 4600000
+};
+
+static const struct regulator_desc da9210_reg = {
+ .name = "DA9210",
+ .id = 0,
+ .ops = &da9210_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = ((DA9210_MAX_MV - DA9210_MIN_MV) / DA9210_STEP_MV) + 1,
+ .min_uV = (DA9210_MIN_MV * 1000),
+ .uV_step = (DA9210_STEP_MV * 1000),
+ .vsel_reg = DA9210_REG_VBUCK_A,
+ .vsel_mask = DA9210_VBUCK_MASK,
+ .enable_reg = DA9210_REG_BUCK_CONT,
+ .enable_mask = DA9210_BUCK_EN,
+ .owner = THIS_MODULE,
+};
+
+static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA,
+ int max_uA)
+{
+ struct da9210 *chip = rdev_get_drvdata(rdev);
+ unsigned int sel;
+ int i;
+
+ /* search for closest to maximum */
+ for (i = ARRAY_SIZE(da9210_buck_limits)-1; i >= 0; i--) {
+ if (min_uA <= da9210_buck_limits[i] &&
+ max_uA >= da9210_buck_limits[i]) {
+ sel = i;
+ sel = sel << DA9210_BUCK_ILIM_SHIFT;
+ return regmap_update_bits(chip->regmap,
+ DA9210_REG_BUCK_ILIM,
+ DA9210_BUCK_ILIM_MASK, sel);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int da9210_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9210 *chip = rdev_get_drvdata(rdev);
+ unsigned int data;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_read(chip->regmap, DA9210_REG_BUCK_ILIM, &data);
+ if (ret < 0)
+ return ret;
+
+ /* select one of 16 values: 0000 (1600mA) to 1111 (4600mA) */
+ sel = (data & DA9210_BUCK_ILIM_MASK) >> DA9210_BUCK_ILIM_SHIFT;
+
+ return da9210_buck_limits[sel];
+}
+
+static irqreturn_t da9210_irq_handler(int irq, void *data)
+{
+ struct da9210 *chip = data;
+ unsigned int val, handled = 0;
+ int error, ret = IRQ_NONE;
+
+ error = regmap_read(chip->regmap, DA9210_REG_EVENT_B, &val);
+ if (error < 0)
+ goto error_i2c;
+
+ mutex_lock(&chip->rdev->mutex);
+
+ if (val & DA9210_E_OVCURR) {
+ regulator_notifier_call_chain(chip->rdev,
+ REGULATOR_EVENT_OVER_CURRENT,
+ NULL);
+ handled |= DA9210_E_OVCURR;
+ }
+ if (val & DA9210_E_NPWRGOOD) {
+ regulator_notifier_call_chain(chip->rdev,
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ handled |= DA9210_E_NPWRGOOD;
+ }
+ if (val & (DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT)) {
+ regulator_notifier_call_chain(chip->rdev,
+ REGULATOR_EVENT_OVER_TEMP, NULL);
+ handled |= val & (DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT);
+ }
+ if (val & DA9210_E_VMAX) {
+ regulator_notifier_call_chain(chip->rdev,
+ REGULATOR_EVENT_REGULATION_OUT,
+ NULL);
+ handled |= DA9210_E_VMAX;
+ }
+
+ mutex_unlock(&chip->rdev->mutex);
+
+ if (handled) {
+ /* Clear handled events */
+ error = regmap_write(chip->regmap, DA9210_REG_EVENT_B, handled);
+ if (error < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(regmap_get_device(chip->regmap), "I2C error : %d\n", error);
+ return ret;
+}
+
+/*
+ * I2C driver interface functions
+ */
+
+static const struct of_device_id da9210_dt_ids[] = {
+ { .compatible = "dlg,da9210", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da9210_dt_ids);
+
+static int da9210_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9210 *chip;
+ struct device *dev = &i2c->dev;
+ struct da9210_pdata *pdata = dev_get_platdata(dev);
+ struct regulator_dev *rdev = NULL;
+ struct regulator_config config = { };
+ int error;
+ const struct of_device_id *match;
+
+ if (i2c->dev.of_node && !pdata) {
+ match = of_match_device(of_match_ptr(da9210_dt_ids),
+ &i2c->dev);
+ if (!match) {
+ dev_err(&i2c->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ }
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct da9210), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->regmap = devm_regmap_init_i2c(i2c, &da9210_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ config.dev = &i2c->dev;
+ config.init_data = pdata ? &pdata->da9210_constraints :
+ of_get_regulator_init_data(dev, dev->of_node, &da9210_reg);
+ config.driver_data = chip;
+ config.regmap = chip->regmap;
+ config.of_node = dev->of_node;
+
+ /* Mask all interrupt sources to deassert interrupt line */
+ error = regmap_write(chip->regmap, DA9210_REG_MASK_A, ~0);
+ if (!error)
+ error = regmap_write(chip->regmap, DA9210_REG_MASK_B, ~0);
+ if (error) {
+ dev_err(&i2c->dev, "Failed to write to mask reg: %d\n", error);
+ return error;
+ }
+
+ rdev = devm_regulator_register(&i2c->dev, &da9210_reg, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&i2c->dev, "Failed to register DA9210 regulator\n");
+ return PTR_ERR(rdev);
+ }
+
+ chip->rdev = rdev;
+ if (i2c->irq) {
+ error = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ da9210_irq_handler,
+ IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT | IRQF_SHARED,
+ "da9210", chip);
+ if (error) {
+ dev_err(&i2c->dev, "Failed to request IRQ%u: %d\n",
+ i2c->irq, error);
+ return error;
+ }
+
+ error = regmap_update_bits(chip->regmap, DA9210_REG_MASK_B,
+ DA9210_M_OVCURR | DA9210_M_NPWRGOOD |
+ DA9210_M_TEMP_WARN |
+ DA9210_M_TEMP_CRIT | DA9210_M_VMAX, 0);
+ if (error < 0) {
+ dev_err(&i2c->dev, "Failed to update mask reg: %d\n",
+ error);
+ return error;
+ }
+ } else {
+ dev_warn(&i2c->dev, "No IRQ configured\n");
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id da9210_i2c_id[] = {
+ {"da9210", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, da9210_i2c_id);
+
+static struct i2c_driver da9210_regulator_driver = {
+ .driver = {
+ .name = "da9210",
+ .of_match_table = of_match_ptr(da9210_dt_ids),
+ },
+ .probe = da9210_i2c_probe,
+ .id_table = da9210_i2c_id,
+};
+
+module_i2c_driver(da9210_regulator_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Dialog DA9210");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/da9210-regulator.h b/drivers/regulator/da9210-regulator.h
new file mode 100644
index 000000000..749c55080
--- /dev/null
+++ b/drivers/regulator/da9210-regulator.h
@@ -0,0 +1,288 @@
+
+/*
+ * da9210-regulator.h - Regulator definitions for DA9210
+ * Copyright (C) 2013 Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DA9210_REGISTERS_H__
+#define __DA9210_REGISTERS_H__
+
+struct da9210_pdata {
+ struct regulator_init_data da9210_constraints;
+};
+
+/* Page selection */
+#define DA9210_REG_PAGE_CON 0x00
+
+/* System Control and Event Registers */
+#define DA9210_REG_STATUS_A 0x50
+#define DA9210_REG_STATUS_B 0x51
+#define DA9210_REG_EVENT_A 0x52
+#define DA9210_REG_EVENT_B 0x53
+#define DA9210_REG_MASK_A 0x54
+#define DA9210_REG_MASK_B 0x55
+#define DA9210_REG_CONTROL_A 0x56
+
+/* GPIO Control Registers */
+#define DA9210_REG_GPIO_0_1 0x58
+#define DA9210_REG_GPIO_2_3 0x59
+#define DA9210_REG_GPIO_4_5 0x5A
+#define DA9210_REG_GPIO_6 0x5B
+
+/* Regulator Registers */
+#define DA9210_REG_BUCK_CONT 0x5D
+#define DA9210_REG_BUCK_ILIM 0xD0
+#define DA9210_REG_BUCK_CONF1 0xD1
+#define DA9210_REG_BUCK_CONF2 0xD2
+#define DA9210_REG_VBACK_AUTO 0xD4
+#define DA9210_REG_VBACK_BASE 0xD5
+#define DA9210_REG_VBACK_MAX_DVC_IF 0xD6
+#define DA9210_REG_VBACK_DVC 0xD7
+#define DA9210_REG_VBUCK_A 0xD8
+#define DA9210_REG_VBUCK_B 0xD9
+
+/* I2C Interface Settings */
+#define DA9210_REG_INTERFACE 0x105
+
+/* OTP */
+#define DA9210_REG_OPT_COUNT 0x140
+#define DA9210_REG_OPT_ADDR 0x141
+#define DA9210_REG_OPT_DATA 0x142
+
+/* Customer Trim and Configuration */
+#define DA9210_REG_CONFIG_A 0x143
+#define DA9210_REG_CONFIG_B 0x144
+#define DA9210_REG_CONFIG_C 0x145
+#define DA9210_REG_CONFIG_D 0x146
+#define DA9210_REG_CONFIG_E 0x147
+
+
+/*
+ * Registers bits
+ */
+/* DA9210_REG_PAGE_CON (addr=0x00) */
+#define DA9210_PEG_PAGE_SHIFT 0
+#define DA9210_REG_PAGE_MASK 0x0F
+/* On I2C registers 0x00 - 0xFF */
+#define DA9210_REG_PAGE0 0
+/* On I2C registers 0x100 - 0x1FF */
+#define DA9210_REG_PAGE2 2
+#define DA9210_PAGE_WRITE_MODE 0x00
+#define DA9210_REPEAT_WRITE_MODE 0x40
+#define DA9210_PAGE_REVERT 0x80
+
+/* DA9210_REG_STATUS_A (addr=0x50) */
+#define DA9210_GPI0 0x01
+#define DA9210_GPI1 0x02
+#define DA9210_GPI2 0x04
+#define DA9210_GPI3 0x08
+#define DA9210_GPI4 0x10
+#define DA9210_GPI5 0x20
+#define DA9210_GPI6 0x40
+
+/* DA9210_REG_EVENT_A (addr=0x52) */
+#define DA9210_E_GPI0 0x01
+#define DA9210_E_GPI1 0x02
+#define DA9210_E_GPI2 0x04
+#define DA9210_E_GPI3 0x08
+#define DA9210_E_GPI4 0x10
+#define DA9210_E_GPI5 0x20
+#define DA9210_E_GPI6 0x40
+
+/* DA9210_REG_EVENT_B (addr=0x53) */
+#define DA9210_E_OVCURR 0x01
+#define DA9210_E_NPWRGOOD 0x02
+#define DA9210_E_TEMP_WARN 0x04
+#define DA9210_E_TEMP_CRIT 0x08
+#define DA9210_E_VMAX 0x10
+
+/* DA9210_REG_MASK_A (addr=0x54) */
+#define DA9210_M_GPI0 0x01
+#define DA9210_M_GPI1 0x02
+#define DA9210_M_GPI2 0x04
+#define DA9210_M_GPI3 0x08
+#define DA9210_M_GPI4 0x10
+#define DA9210_M_GPI5 0x20
+#define DA9210_M_GPI6 0x40
+
+/* DA9210_REG_MASK_B (addr=0x55) */
+#define DA9210_M_OVCURR 0x01
+#define DA9210_M_NPWRGOOD 0x02
+#define DA9210_M_TEMP_WARN 0x04
+#define DA9210_M_TEMP_CRIT 0x08
+#define DA9210_M_VMAX 0x10
+
+/* DA9210_REG_CONTROL_A (addr=0x56) */
+#define DA9210_DEBOUNCING_SHIFT 0
+#define DA9210_DEBOUNCING_MASK 0x07
+#define DA9210_SLEW_RATE_SHIFT 3
+#define DA9210_SLEW_RATE_MASK 0x18
+#define DA9210_V_LOCK 0x20
+
+/* DA9210_REG_GPIO_0_1 (addr=0x58) */
+#define DA9210_GPIO0_PIN_SHIFT 0
+#define DA9210_GPIO0_PIN_MASK 0x03
+#define DA9210_GPIO0_PIN_GPI 0x00
+#define DA9210_GPIO0_PIN_GPO_OD 0x02
+#define DA9210_GPIO0_PIN_GPO 0x03
+#define DA9210_GPIO0_TYPE 0x04
+#define DA9210_GPIO0_TYPE_GPI 0x00
+#define DA9210_GPIO0_TYPE_GPO 0x04
+#define DA9210_GPIO0_MODE 0x08
+#define DA9210_GPIO1_PIN_SHIFT 4
+#define DA9210_GPIO1_PIN_MASK 0x30
+#define DA9210_GPIO1_PIN_GPI 0x00
+#define DA9210_GPIO1_PIN_VERROR 0x10
+#define DA9210_GPIO1_PIN_GPO_OD 0x20
+#define DA9210_GPIO1_PIN_GPO 0x30
+#define DA9210_GPIO1_TYPE_SHIFT 0x40
+#define DA9210_GPIO1_TYPE_GPI 0x00
+#define DA9210_GPIO1_TYPE_GPO 0x40
+#define DA9210_GPIO1_MODE 0x80
+
+/* DA9210_REG_GPIO_2_3 (addr=0x59) */
+#define DA9210_GPIO2_PIN_SHIFT 0
+#define DA9210_GPIO2_PIN_MASK 0x03
+#define DA9210_GPIO2_PIN_GPI 0x00
+#define DA9210_GPIO5_PIN_BUCK_CLK 0x10
+#define DA9210_GPIO2_PIN_GPO_OD 0x02
+#define DA9210_GPIO2_PIN_GPO 0x03
+#define DA9210_GPIO2_TYPE 0x04
+#define DA9210_GPIO2_TYPE_GPI 0x00
+#define DA9210_GPIO2_TYPE_GPO 0x04
+#define DA9210_GPIO2_MODE 0x08
+#define DA9210_GPIO3_PIN_SHIFT 4
+#define DA9210_GPIO3_PIN_MASK 0x30
+#define DA9210_GPIO3_PIN_GPI 0x00
+#define DA9210_GPIO3_PIN_IERROR 0x10
+#define DA9210_GPIO3_PIN_GPO_OD 0x20
+#define DA9210_GPIO3_PIN_GPO 0x30
+#define DA9210_GPIO3_TYPE_SHIFT 0x40
+#define DA9210_GPIO3_TYPE_GPI 0x00
+#define DA9210_GPIO3_TYPE_GPO 0x40
+#define DA9210_GPIO3_MODE 0x80
+
+/* DA9210_REG_GPIO_4_5 (addr=0x5A) */
+#define DA9210_GPIO4_PIN_SHIFT 0
+#define DA9210_GPIO4_PIN_MASK 0x03
+#define DA9210_GPIO4_PIN_GPI 0x00
+#define DA9210_GPIO4_PIN_GPO_OD 0x02
+#define DA9210_GPIO4_PIN_GPO 0x03
+#define DA9210_GPIO4_TYPE 0x04
+#define DA9210_GPIO4_TYPE_GPI 0x00
+#define DA9210_GPIO4_TYPE_GPO 0x04
+#define DA9210_GPIO4_MODE 0x08
+#define DA9210_GPIO5_PIN_SHIFT 4
+#define DA9210_GPIO5_PIN_MASK 0x30
+#define DA9210_GPIO5_PIN_GPI 0x00
+#define DA9210_GPIO5_PIN_INTERFACE 0x01
+#define DA9210_GPIO5_PIN_GPO_OD 0x20
+#define DA9210_GPIO5_PIN_GPO 0x30
+#define DA9210_GPIO5_TYPE_SHIFT 0x40
+#define DA9210_GPIO5_TYPE_GPI 0x00
+#define DA9210_GPIO5_TYPE_GPO 0x40
+#define DA9210_GPIO5_MODE 0x80
+
+/* DA9210_REG_GPIO_6 (addr=0x5B) */
+#define DA9210_GPIO6_PIN_SHIFT 0
+#define DA9210_GPIO6_PIN_MASK 0x03
+#define DA9210_GPIO6_PIN_GPI 0x00
+#define DA9210_GPIO6_PIN_INTERFACE 0x01
+#define DA9210_GPIO6_PIN_GPO_OD 0x02
+#define DA9210_GPIO6_PIN_GPO 0x03
+#define DA9210_GPIO6_TYPE 0x04
+#define DA9210_GPIO6_TYPE_GPI 0x00
+#define DA9210_GPIO6_TYPE_GPO 0x04
+#define DA9210_GPIO6_MODE 0x08
+
+/* DA9210_REG_BUCK_CONT (addr=0x5D) */
+#define DA9210_BUCK_EN 0x01
+#define DA9210_BUCK_GPI_SHIFT 1
+#define DA9210_BUCK_GPI_MASK 0x06
+#define DA9210_BUCK_GPI_OFF 0x00
+#define DA9210_BUCK_GPI_GPIO0 0x02
+#define DA9210_BUCK_GPI_GPIO3 0x04
+#define DA9210_BUCK_GPI_GPIO4 0x06
+#define DA9210_BUCK_PD_DIS 0x08
+#define DA9210_VBUCK_SEL 0x10
+#define DA9210_VBUCK_SEL_A 0x00
+#define DA9210_VBUCK_SEL_B 0x10
+#define DA9210_VBUCK_GPI_SHIFT 5
+#define DA9210_VBUCK_GPI_MASK 0x60
+#define DA9210_VBUCK_GPI_OFF 0x00
+#define DA9210_VBUCK_GPI_GPIO0 0x20
+#define DA9210_VBUCK_GPI_GPIO3 0x40
+#define DA9210_VBUCK_GPI_GPIO4 0x60
+#define DA9210_DVC_CTRL_EN 0x80
+
+/* DA9210_REG_BUCK_ILIM (addr=0xD0) */
+#define DA9210_BUCK_ILIM_SHIFT 0
+#define DA9210_BUCK_ILIM_MASK 0x0F
+#define DA9210_BUCK_IALARM 0x10
+
+/* DA9210_REG_BUCK_CONF1 (addr=0xD1) */
+#define DA9210_BUCK_MODE_SHIFT 0
+#define DA9210_BUCK_MODE_MASK 0x03
+#define DA9210_BUCK_MODE_MANUAL 0x00
+#define DA9210_BUCK_MODE_SLEEP 0x01
+#define DA9210_BUCK_MODE_SYNC 0x02
+#define DA9210_BUCK_MODE_AUTO 0x03
+#define DA9210_STARTUP_CTRL_SHIFT 2
+#define DA9210_STARTUP_CTRL_MASK 0x1C
+#define DA9210_PWR_DOWN_CTRL_SHIFT 5
+#define DA9210_PWR_DOWN_CTRL_MASK 0xE0
+
+/* DA9210_REG_BUCK_CONF2 (addr=0xD2) */
+#define DA9210_PHASE_SEL_SHIFT 0
+#define DA9210_PHASE_SEL_MASK 0x03
+#define DA9210_FREQ_SEL 0x40
+
+/* DA9210_REG_BUCK_AUTO (addr=0xD4) */
+#define DA9210_VBUCK_AUTO_SHIFT 0
+#define DA9210_VBUCK_AUTO_MASK 0x7F
+
+/* DA9210_REG_BUCK_BASE (addr=0xD5) */
+#define DA9210_VBUCK_BASE_SHIFT 0
+#define DA9210_VBUCK_BASE_MASK 0x7F
+
+/* DA9210_REG_VBUCK_MAX_DVC_IF (addr=0xD6) */
+#define DA9210_VBUCK_MAX_SHIFT 0
+#define DA9210_VBUCK_MAX_MASK 0x7F
+#define DA9210_DVC_STEP_SIZE 0x80
+#define DA9210_DVC_STEP_SIZE_10MV 0x00
+#define DA9210_DVC_STEP_SIZE_20MV 0x80
+
+/* DA9210_REG_VBUCK_DVC (addr=0xD7) */
+#define DA9210_VBUCK_DVC_SHIFT 0
+#define DA9210_VBUCK_DVC_MASK 0x7F
+
+/* DA9210_REG_VBUCK_A/B (addr=0xD8/0xD9) */
+#define DA9210_VBUCK_SHIFT 0
+#define DA9210_VBUCK_MASK 0x7F
+#define DA9210_VBUCK_BIAS 0
+#define DA9210_BUCK_SL 0x80
+
+/* DA9210_REG_INTERFACE (addr=0x105) */
+#define DA9210_IF_BASE_ADDR_SHIFT 4
+#define DA9210_IF_BASE_ADDR_MASK 0xF0
+
+/* DA9210_REG_CONFIG_E (addr=0x147) */
+#define DA9210_STAND_ALONE 0x01
+
+#endif /* __DA9210_REGISTERS_H__ */
+
diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c
new file mode 100644
index 000000000..6c122b3df
--- /dev/null
+++ b/drivers/regulator/da9211-regulator.c
@@ -0,0 +1,535 @@
+/*
+ * da9211-regulator.c - Regulator device driver for DA9211/DA9212
+ * /DA9213/DA9223/DA9214/DA9224/DA9215/DA9225
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/da9211.h>
+#include "da9211-regulator.h"
+
+/* DEVICE IDs */
+#define DA9211_DEVICE_ID 0x22
+#define DA9213_DEVICE_ID 0x23
+#define DA9215_DEVICE_ID 0x24
+
+#define DA9211_BUCK_MODE_SLEEP 1
+#define DA9211_BUCK_MODE_SYNC 2
+#define DA9211_BUCK_MODE_AUTO 3
+
+/* DA9211 REGULATOR IDs */
+#define DA9211_ID_BUCKA 0
+#define DA9211_ID_BUCKB 1
+
+struct da9211 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct da9211_pdata *pdata;
+ struct regulator_dev *rdev[DA9211_MAX_REGULATORS];
+ int num_regulator;
+ int chip_irq;
+ int chip_id;
+};
+
+static const struct regmap_range_cfg da9211_regmap_range[] = {
+ {
+ .selector_reg = DA9211_REG_PAGE_CON,
+ .selector_mask = DA9211_REG_PAGE_MASK,
+ .selector_shift = DA9211_REG_PAGE_SHIFT,
+ .window_start = 0,
+ .window_len = 256,
+ .range_min = 0,
+ .range_max = 5*128,
+ },
+};
+
+static const struct regmap_config da9211_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 5 * 128,
+ .ranges = da9211_regmap_range,
+ .num_ranges = ARRAY_SIZE(da9211_regmap_range),
+};
+
+/* Default limits measured in millivolts and milliamps */
+#define DA9211_MIN_MV 300
+#define DA9211_MAX_MV 1570
+#define DA9211_STEP_MV 10
+
+/* Current limits for DA9211 buck (uA) indices
+ * corresponds with register values
+ */
+static const int da9211_current_limits[] = {
+ 2000000, 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000,
+ 3600000, 3800000, 4000000, 4200000, 4400000, 4600000, 4800000, 5000000
+};
+/* Current limits for DA9213 buck (uA) indices
+ * corresponds with register values
+ */
+static const int da9213_current_limits[] = {
+ 3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000,
+ 4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000
+};
+/* Current limits for DA9215 buck (uA) indices
+ * corresponds with register values
+ */
+static const int da9215_current_limits[] = {
+ 4000000, 4200000, 4400000, 4600000, 4800000, 5000000, 5200000, 5400000,
+ 5600000, 5800000, 6000000, 6200000, 6400000, 6600000, 6800000, 7000000
+};
+
+static unsigned int da9211_buck_get_mode(struct regulator_dev *rdev)
+{
+ int id = rdev_get_id(rdev);
+ struct da9211 *chip = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret, mode = 0;
+
+ ret = regmap_read(chip->regmap, DA9211_REG_BUCKA_CONF+id, &data);
+ if (ret < 0)
+ return ret;
+
+ switch (data & 0x03) {
+ case DA9211_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case DA9211_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case DA9211_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ }
+
+ return mode;
+}
+
+static int da9211_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int id = rdev_get_id(rdev);
+ struct da9211 *chip = rdev_get_drvdata(rdev);
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = DA9211_BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = DA9211_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = DA9211_BUCK_MODE_SLEEP;
+ break;
+ }
+
+ return regmap_update_bits(chip->regmap, DA9211_REG_BUCKA_CONF+id,
+ 0x03, val);
+}
+
+static int da9211_set_current_limit(struct regulator_dev *rdev, int min,
+ int max)
+{
+ int id = rdev_get_id(rdev);
+ struct da9211 *chip = rdev_get_drvdata(rdev);
+ int i, max_size;
+ const int *current_limits;
+
+ switch (chip->chip_id) {
+ case DA9211:
+ current_limits = da9211_current_limits;
+ max_size = ARRAY_SIZE(da9211_current_limits)-1;
+ break;
+ case DA9213:
+ current_limits = da9213_current_limits;
+ max_size = ARRAY_SIZE(da9213_current_limits)-1;
+ break;
+ case DA9215:
+ current_limits = da9215_current_limits;
+ max_size = ARRAY_SIZE(da9215_current_limits)-1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* search for closest to maximum */
+ for (i = max_size; i >= 0; i--) {
+ if (min <= current_limits[i] &&
+ max >= current_limits[i]) {
+ return regmap_update_bits(chip->regmap,
+ DA9211_REG_BUCK_ILIM,
+ (0x0F << id*4), (i << id*4));
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int da9211_get_current_limit(struct regulator_dev *rdev)
+{
+ int id = rdev_get_id(rdev);
+ struct da9211 *chip = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+ const int *current_limits;
+
+ switch (chip->chip_id) {
+ case DA9211:
+ current_limits = da9211_current_limits;
+ break;
+ case DA9213:
+ current_limits = da9213_current_limits;
+ break;
+ case DA9215:
+ current_limits = da9215_current_limits;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_read(chip->regmap, DA9211_REG_BUCK_ILIM, &data);
+ if (ret < 0)
+ return ret;
+
+ /* select one of 16 values: 0000 (2000mA or 3000mA)
+ * to 1111 (5000mA or 6000mA).
+ */
+ data = (data >> id*4) & 0x0F;
+ return current_limits[data];
+}
+
+static const struct regulator_ops da9211_buck_ops = {
+ .get_mode = da9211_buck_get_mode,
+ .set_mode = da9211_buck_set_mode,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9211_set_current_limit,
+ .get_current_limit = da9211_get_current_limit,
+};
+
+#define DA9211_BUCK(_id) \
+{\
+ .name = #_id,\
+ .ops = &da9211_buck_ops,\
+ .type = REGULATOR_VOLTAGE,\
+ .id = DA9211_ID_##_id,\
+ .n_voltages = (DA9211_MAX_MV - DA9211_MIN_MV) / DA9211_STEP_MV + 1,\
+ .min_uV = (DA9211_MIN_MV * 1000),\
+ .uV_step = (DA9211_STEP_MV * 1000),\
+ .enable_reg = DA9211_REG_BUCKA_CONT + DA9211_ID_##_id,\
+ .enable_mask = DA9211_BUCKA_EN,\
+ .vsel_reg = DA9211_REG_VBUCKA_A + DA9211_ID_##_id * 2,\
+ .vsel_mask = DA9211_VBUCK_MASK,\
+ .owner = THIS_MODULE,\
+}
+
+static struct regulator_desc da9211_regulators[] = {
+ DA9211_BUCK(BUCKA),
+ DA9211_BUCK(BUCKB),
+};
+
+#ifdef CONFIG_OF
+static struct of_regulator_match da9211_matches[] = {
+ [DA9211_ID_BUCKA] = { .name = "BUCKA" },
+ [DA9211_ID_BUCKB] = { .name = "BUCKB" },
+ };
+
+static struct da9211_pdata *da9211_parse_regulators_dt(
+ struct device *dev)
+{
+ struct da9211_pdata *pdata;
+ struct device_node *node;
+ int i, num, n;
+
+ node = of_get_child_by_name(dev->of_node, "regulators");
+ if (!node) {
+ dev_err(dev, "regulators node not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ num = of_regulator_match(dev, node, da9211_matches,
+ ARRAY_SIZE(da9211_matches));
+ of_node_put(node);
+ if (num < 0) {
+ dev_err(dev, "Failed to match regulators\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->num_buck = num;
+
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(da9211_matches); i++) {
+ if (!da9211_matches[i].init_data)
+ continue;
+
+ pdata->init_data[n] = da9211_matches[i].init_data;
+ pdata->reg_node[n] = da9211_matches[i].of_node;
+ pdata->gpiod_ren[n] = devm_gpiod_get_from_of_node(dev,
+ da9211_matches[i].of_node,
+ "enable",
+ 0,
+ GPIOD_OUT_HIGH,
+ "da9211-enable");
+ n++;
+ }
+
+ return pdata;
+}
+#else
+static struct da9211_pdata *da9211_parse_regulators_dt(
+ struct device *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
+static irqreturn_t da9211_irq_handler(int irq, void *data)
+{
+ struct da9211 *chip = data;
+ int reg_val, err, ret = IRQ_NONE;
+
+ err = regmap_read(chip->regmap, DA9211_REG_EVENT_B, &reg_val);
+ if (err < 0)
+ goto error_i2c;
+
+ if (reg_val & DA9211_E_OV_CURR_A) {
+ regulator_notifier_call_chain(chip->rdev[0],
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+
+ err = regmap_write(chip->regmap, DA9211_REG_EVENT_B,
+ DA9211_E_OV_CURR_A);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (reg_val & DA9211_E_OV_CURR_B) {
+ regulator_notifier_call_chain(chip->rdev[1],
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+
+ err = regmap_write(chip->regmap, DA9211_REG_EVENT_B,
+ DA9211_E_OV_CURR_B);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(chip->dev, "I2C error : %d\n", err);
+ return IRQ_NONE;
+}
+
+static int da9211_regulator_init(struct da9211 *chip)
+{
+ struct regulator_config config = { };
+ int i, ret;
+ unsigned int data;
+
+ ret = regmap_read(chip->regmap, DA9211_REG_CONFIG_E, &data);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read CONFIG_E reg: %d\n", ret);
+ return ret;
+ }
+
+ data &= DA9211_SLAVE_SEL;
+ /* If configuration for 1/2 bucks is different between platform data
+ * and the register, driver should exit.
+ */
+ if (chip->pdata->num_buck == 1 && data == 0x00)
+ chip->num_regulator = 1;
+ else if (chip->pdata->num_buck == 2 && data != 0x00)
+ chip->num_regulator = 2;
+ else {
+ dev_err(chip->dev, "Configuration is mismatched\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < chip->num_regulator; i++) {
+ config.init_data = chip->pdata->init_data[i];
+ config.dev = chip->dev;
+ config.driver_data = chip;
+ config.regmap = chip->regmap;
+ config.of_node = chip->pdata->reg_node[i];
+
+ if (chip->pdata->gpiod_ren[i])
+ config.ena_gpiod = chip->pdata->gpiod_ren[i];
+ else
+ config.ena_gpiod = NULL;
+
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ &da9211_regulators[i], &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev,
+ "Failed to register DA9211 regulator\n");
+ return PTR_ERR(chip->rdev[i]);
+ }
+
+ if (chip->chip_irq != 0) {
+ ret = regmap_update_bits(chip->regmap,
+ DA9211_REG_MASK_B, DA9211_M_OV_CURR_A << i, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to update mask reg: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int da9211_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9211 *chip;
+ int error, ret;
+ unsigned int data;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct da9211), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &i2c->dev;
+ chip->regmap = devm_regmap_init_i2c(i2c, &da9211_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ chip->pdata = i2c->dev.platform_data;
+
+ ret = regmap_read(chip->regmap, DA9211_REG_DEVICE_ID, &data);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read DEVICE_ID reg: %d\n", ret);
+ return ret;
+ }
+
+ switch (data) {
+ case DA9211_DEVICE_ID:
+ chip->chip_id = DA9211;
+ break;
+ case DA9213_DEVICE_ID:
+ chip->chip_id = DA9213;
+ break;
+ case DA9215_DEVICE_ID:
+ chip->chip_id = DA9215;
+ break;
+ default:
+ dev_err(chip->dev, "Unsupported device id = 0x%x.\n", data);
+ return -ENODEV;
+ }
+
+ if (!chip->pdata)
+ chip->pdata = da9211_parse_regulators_dt(chip->dev);
+
+ if (IS_ERR(chip->pdata)) {
+ dev_err(chip->dev, "No regulators defined for the platform\n");
+ return PTR_ERR(chip->pdata);
+ }
+
+ chip->chip_irq = i2c->irq;
+
+ if (chip->chip_irq != 0) {
+ ret = devm_request_threaded_irq(chip->dev, chip->chip_irq, NULL,
+ da9211_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "da9211", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to request IRQ: %d\n",
+ chip->chip_irq);
+ return ret;
+ }
+ } else {
+ dev_warn(chip->dev, "No IRQ configured\n");
+ }
+
+ ret = da9211_regulator_init(chip);
+
+ if (ret < 0)
+ dev_err(chip->dev, "Failed to initialize regulator: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id da9211_i2c_id[] = {
+ {"da9211", DA9211},
+ {"da9212", DA9212},
+ {"da9213", DA9213},
+ {"da9223", DA9223},
+ {"da9214", DA9214},
+ {"da9224", DA9224},
+ {"da9215", DA9215},
+ {"da9225", DA9225},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, da9211_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id da9211_dt_ids[] = {
+ { .compatible = "dlg,da9211", .data = &da9211_i2c_id[0] },
+ { .compatible = "dlg,da9212", .data = &da9211_i2c_id[1] },
+ { .compatible = "dlg,da9213", .data = &da9211_i2c_id[2] },
+ { .compatible = "dlg,da9223", .data = &da9211_i2c_id[3] },
+ { .compatible = "dlg,da9214", .data = &da9211_i2c_id[4] },
+ { .compatible = "dlg,da9224", .data = &da9211_i2c_id[5] },
+ { .compatible = "dlg,da9215", .data = &da9211_i2c_id[6] },
+ { .compatible = "dlg,da9225", .data = &da9211_i2c_id[7] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, da9211_dt_ids);
+#endif
+
+static struct i2c_driver da9211_regulator_driver = {
+ .driver = {
+ .name = "da9211",
+ .of_match_table = of_match_ptr(da9211_dt_ids),
+ },
+ .probe = da9211_i2c_probe,
+ .id_table = da9211_i2c_id,
+};
+
+module_i2c_driver(da9211_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("DA9211/DA9212/DA9213/DA9223/DA9214/DA9224/DA9215/DA9225 regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/da9211-regulator.h b/drivers/regulator/da9211-regulator.h
new file mode 100644
index 000000000..2cb32aab4
--- /dev/null
+++ b/drivers/regulator/da9211-regulator.h
@@ -0,0 +1,275 @@
+/*
+ * da9211-regulator.h - Regulator definitions for DA9211/DA9212
+ * /DA9213/DA9223/DA9214/DA9224/DA9215/DA9225
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __DA9211_REGISTERS_H__
+#define __DA9211_REGISTERS_H__
+
+/* Page selection */
+#define DA9211_REG_PAGE_CON 0x00
+
+/* System Control and Event Registers */
+#define DA9211_REG_STATUS_A 0x50
+#define DA9211_REG_STATUS_B 0x51
+#define DA9211_REG_EVENT_A 0x52
+#define DA9211_REG_EVENT_B 0x53
+#define DA9211_REG_MASK_A 0x54
+#define DA9211_REG_MASK_B 0x55
+#define DA9211_REG_CONTROL_A 0x56
+
+/* GPIO Control Registers */
+#define DA9211_REG_GPIO_0_1 0x58
+#define DA9211_REG_GPIO_2_3 0x59
+#define DA9211_REG_GPIO_4 0x5A
+
+/* Regulator Registers */
+#define DA9211_REG_BUCKA_CONT 0x5D
+#define DA9211_REG_BUCKB_CONT 0x5E
+#define DA9211_REG_BUCK_ILIM 0xD0
+#define DA9211_REG_BUCKA_CONF 0xD1
+#define DA9211_REG_BUCKB_CONF 0xD2
+#define DA9211_REG_BUCK_CONF 0xD3
+#define DA9211_REG_VBACKA_MAX 0xD5
+#define DA9211_REG_VBACKB_MAX 0xD6
+#define DA9211_REG_VBUCKA_A 0xD7
+#define DA9211_REG_VBUCKA_B 0xD8
+#define DA9211_REG_VBUCKB_A 0xD9
+#define DA9211_REG_VBUCKB_B 0xDA
+
+/* I2C Interface Settings */
+#define DA9211_REG_INTERFACE 0x105
+
+/* BUCK Phase Selection*/
+#define DA9211_REG_CONFIG_E 0x147
+
+/* Device ID */
+#define DA9211_REG_DEVICE_ID 0x201
+
+/*
+ * Registers bits
+ */
+/* DA9211_REG_PAGE_CON (addr=0x00) */
+#define DA9211_REG_PAGE_SHIFT 1
+#define DA9211_REG_PAGE_MASK 0x06
+/* On I2C registers 0x00 - 0xFF */
+#define DA9211_REG_PAGE0 0
+/* On I2C registers 0x100 - 0x1FF */
+#define DA9211_REG_PAGE2 2
+#define DA9211_PAGE_WRITE_MODE 0x00
+#define DA9211_REPEAT_WRITE_MODE 0x40
+#define DA9211_PAGE_REVERT 0x80
+
+/* DA9211_REG_STATUS_A (addr=0x50) */
+#define DA9211_GPI0 0x01
+#define DA9211_GPI1 0x02
+#define DA9211_GPI2 0x04
+#define DA9211_GPI3 0x08
+#define DA9211_GPI4 0x10
+
+/* DA9211_REG_EVENT_A (addr=0x52) */
+#define DA9211_E_GPI0 0x01
+#define DA9211_E_GPI1 0x02
+#define DA9211_E_GPI2 0x04
+#define DA9211_E_GPI3 0x08
+#define DA9211_E_GPI4 0x10
+#define DA9211_E_UVLO_IO 0x40
+
+/* DA9211_REG_EVENT_B (addr=0x53) */
+#define DA9211_E_PWRGOOD_A 0x01
+#define DA9211_E_PWRGOOD_B 0x02
+#define DA9211_E_TEMP_WARN 0x04
+#define DA9211_E_TEMP_CRIT 0x08
+#define DA9211_E_OV_CURR_A 0x10
+#define DA9211_E_OV_CURR_B 0x20
+
+/* DA9211_REG_MASK_A (addr=0x54) */
+#define DA9211_M_GPI0 0x01
+#define DA9211_M_GPI1 0x02
+#define DA9211_M_GPI2 0x04
+#define DA9211_M_GPI3 0x08
+#define DA9211_M_GPI4 0x10
+#define DA9211_M_UVLO_IO 0x40
+
+/* DA9211_REG_MASK_B (addr=0x55) */
+#define DA9211_M_PWRGOOD_A 0x01
+#define DA9211_M_PWRGOOD_B 0x02
+#define DA9211_M_TEMP_WARN 0x04
+#define DA9211_M_TEMP_CRIT 0x08
+#define DA9211_M_OV_CURR_A 0x10
+#define DA9211_M_OV_CURR_B 0x20
+
+/* DA9211_REG_CONTROL_A (addr=0x56) */
+#define DA9211_DEBOUNCING_SHIFT 0
+#define DA9211_DEBOUNCING_MASK 0x07
+#define DA9211_SLEW_RATE_SHIFT 3
+#define DA9211_SLEW_RATE_A_MASK 0x18
+#define DA9211_SLEW_RATE_B_SHIFT 5
+#define DA9211_SLEW_RATE_B_MASK 0x60
+#define DA9211_V_LOCK 0x80
+
+/* DA9211_REG_GPIO_0_1 (addr=0x58) */
+#define DA9211_GPIO0_PIN_SHIFT 0
+#define DA9211_GPIO0_PIN_MASK 0x03
+#define DA9211_GPIO0_PIN_GPI 0x00
+#define DA9211_GPIO0_PIN_GPO_OD 0x02
+#define DA9211_GPIO0_PIN_GPO 0x03
+#define DA9211_GPIO0_TYPE 0x04
+#define DA9211_GPIO0_TYPE_GPI 0x00
+#define DA9211_GPIO0_TYPE_GPO 0x04
+#define DA9211_GPIO0_MODE 0x08
+#define DA9211_GPIO1_PIN_SHIFT 4
+#define DA9211_GPIO1_PIN_MASK 0x30
+#define DA9211_GPIO1_PIN_GPI 0x00
+#define DA9211_GPIO1_PIN_VERROR 0x10
+#define DA9211_GPIO1_PIN_GPO_OD 0x20
+#define DA9211_GPIO1_PIN_GPO 0x30
+#define DA9211_GPIO1_TYPE_SHIFT 0x40
+#define DA9211_GPIO1_TYPE_GPI 0x00
+#define DA9211_GPIO1_TYPE_GPO 0x40
+#define DA9211_GPIO1_MODE 0x80
+
+/* DA9211_REG_GPIO_2_3 (addr=0x59) */
+#define DA9211_GPIO2_PIN_SHIFT 0
+#define DA9211_GPIO2_PIN_MASK 0x03
+#define DA9211_GPIO2_PIN_GPI 0x00
+#define DA9211_GPIO5_PIN_BUCK_CLK 0x10
+#define DA9211_GPIO2_PIN_GPO_OD 0x02
+#define DA9211_GPIO2_PIN_GPO 0x03
+#define DA9211_GPIO2_TYPE 0x04
+#define DA9211_GPIO2_TYPE_GPI 0x00
+#define DA9211_GPIO2_TYPE_GPO 0x04
+#define DA9211_GPIO2_MODE 0x08
+#define DA9211_GPIO3_PIN_SHIFT 4
+#define DA9211_GPIO3_PIN_MASK 0x30
+#define DA9211_GPIO3_PIN_GPI 0x00
+#define DA9211_GPIO3_PIN_IERROR 0x10
+#define DA9211_GPIO3_PIN_GPO_OD 0x20
+#define DA9211_GPIO3_PIN_GPO 0x30
+#define DA9211_GPIO3_TYPE_SHIFT 0x40
+#define DA9211_GPIO3_TYPE_GPI 0x00
+#define DA9211_GPIO3_TYPE_GPO 0x40
+#define DA9211_GPIO3_MODE 0x80
+
+/* DA9211_REG_GPIO_4 (addr=0x5A) */
+#define DA9211_GPIO4_PIN_SHIFT 0
+#define DA9211_GPIO4_PIN_MASK 0x03
+#define DA9211_GPIO4_PIN_GPI 0x00
+#define DA9211_GPIO4_PIN_GPO_OD 0x02
+#define DA9211_GPIO4_PIN_GPO 0x03
+#define DA9211_GPIO4_TYPE 0x04
+#define DA9211_GPIO4_TYPE_GPI 0x00
+#define DA9211_GPIO4_TYPE_GPO 0x04
+#define DA9211_GPIO4_MODE 0x08
+
+/* DA9211_REG_BUCKA_CONT (addr=0x5D) */
+#define DA9211_BUCKA_EN 0x01
+#define DA9211_BUCKA_GPI_SHIFT 1
+#define DA9211_BUCKA_GPI_MASK 0x06
+#define DA9211_BUCKA_GPI_OFF 0x00
+#define DA9211_BUCKA_GPI_GPIO0 0x02
+#define DA9211_BUCKA_GPI_GPIO1 0x04
+#define DA9211_BUCKA_GPI_GPIO3 0x06
+#define DA9211_BUCKA_PD_DIS 0x08
+#define DA9211_VBUCKA_SEL 0x10
+#define DA9211_VBUCKA_SEL_A 0x00
+#define DA9211_VBUCKA_SEL_B 0x10
+#define DA9211_VBUCKA_GPI_SHIFT 5
+#define DA9211_VBUCKA_GPI_MASK 0x60
+#define DA9211_VBUCKA_GPI_OFF 0x00
+#define DA9211_VBUCKA_GPI_GPIO1 0x20
+#define DA9211_VBUCKA_GPI_GPIO2 0x40
+#define DA9211_VBUCKA_GPI_GPIO4 0x60
+
+/* DA9211_REG_BUCKB_CONT (addr=0x5E) */
+#define DA9211_BUCKB_EN 0x01
+#define DA9211_BUCKB_GPI_SHIFT 1
+#define DA9211_BUCKB_GPI_MASK 0x06
+#define DA9211_BUCKB_GPI_OFF 0x00
+#define DA9211_BUCKB_GPI_GPIO0 0x02
+#define DA9211_BUCKB_GPI_GPIO1 0x04
+#define DA9211_BUCKB_GPI_GPIO3 0x06
+#define DA9211_BUCKB_PD_DIS 0x08
+#define DA9211_VBUCKB_SEL 0x10
+#define DA9211_VBUCKB_SEL_A 0x00
+#define DA9211_VBUCKB_SEL_B 0x10
+#define DA9211_VBUCKB_GPI_SHIFT 5
+#define DA9211_VBUCKB_GPI_MASK 0x60
+#define DA9211_VBUCKB_GPI_OFF 0x00
+#define DA9211_VBUCKB_GPI_GPIO1 0x20
+#define DA9211_VBUCKB_GPI_GPIO2 0x40
+#define DA9211_VBUCKB_GPI_GPIO4 0x60
+
+/* DA9211_REG_BUCK_ILIM (addr=0xD0) */
+#define DA9211_BUCKA_ILIM_SHIFT 0
+#define DA9211_BUCKA_ILIM_MASK 0x0F
+#define DA9211_BUCKB_ILIM_SHIFT 4
+#define DA9211_BUCKB_ILIM_MASK 0xF0
+
+/* DA9211_REG_BUCKA_CONF (addr=0xD1) */
+#define DA9211_BUCKA_MODE_SHIFT 0
+#define DA9211_BUCKA_MODE_MASK 0x03
+#define DA9211_BUCKA_MODE_MANUAL 0x00
+#define DA9211_BUCKA_MODE_SLEEP 0x01
+#define DA9211_BUCKA_MODE_SYNC 0x02
+#define DA9211_BUCKA_MODE_AUTO 0x03
+#define DA9211_BUCKA_UP_CTRL_SHIFT 2
+#define DA9211_BUCKA_UP_CTRL_MASK 0x1C
+#define DA9211_BUCKA_DOWN_CTRL_SHIFT 5
+#define DA9211_BUCKA_DOWN_CTRL_MASK 0xE0
+
+/* DA9211_REG_BUCKB_CONF (addr=0xD2) */
+#define DA9211_BUCKB_MODE_SHIFT 0
+#define DA9211_BUCKB_MODE_MASK 0x03
+#define DA9211_BUCKB_MODE_MANUAL 0x00
+#define DA9211_BUCKB_MODE_SLEEP 0x01
+#define DA9211_BUCKB_MODE_SYNC 0x02
+#define DA9211_BUCKB_MODE_AUTO 0x03
+#define DA9211_BUCKB_UP_CTRL_SHIFT 2
+#define DA9211_BUCKB_UP_CTRL_MASK 0x1C
+#define DA9211_BUCKB_DOWN_CTRL_SHIFT 5
+#define DA9211_BUCKB_DOWN_CTRL_MASK 0xE0
+
+/* DA9211_REG_BUCK_CONF (addr=0xD3) */
+#define DA9211_PHASE_SEL_A_SHIFT 0
+#define DA9211_PHASE_SEL_A_MASK 0x03
+#define DA9211_PHASE_SEL_B_SHIFT 2
+#define DA9211_PHASE_SEL_B_MASK 0x04
+#define DA9211_PH_SH_EN_A_SHIFT 3
+#define DA9211_PH_SH_EN_A_MASK 0x08
+#define DA9211_PH_SH_EN_B_SHIFT 4
+#define DA9211_PH_SH_EN_B_MASK 0x10
+
+/* DA9211_REG_VBUCKA_MAX (addr=0xD5) */
+#define DA9211_VBUCKA_BASE_SHIFT 0
+#define DA9211_VBUCKA_BASE_MASK 0x7F
+
+/* DA9211_REG_VBUCKB_MAX (addr=0xD6) */
+#define DA9211_VBUCKB_BASE_SHIFT 0
+#define DA9211_VBUCKB_BASE_MASK 0x7F
+
+/* DA9211_REG_VBUCKA/B_A/B (addr=0xD7/0xD8/0xD9/0xDA) */
+#define DA9211_VBUCK_SHIFT 0
+#define DA9211_VBUCK_MASK 0x7F
+#define DA9211_VBUCK_BIAS 0
+#define DA9211_BUCK_SL 0x80
+
+/* DA9211_REG_INTERFACE (addr=0x105) */
+#define DA9211_IF_BASE_ADDR_SHIFT 4
+#define DA9211_IF_BASE_ADDR_MASK 0xF0
+
+/* DA9211_REG_CONFIG_E (addr=0x147) */
+#define DA9211_SLAVE_SEL 0x40
+
+#endif /* __DA9211_REGISTERS_H__ */
diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c
new file mode 100644
index 000000000..7cec535cf
--- /dev/null
+++ b/drivers/regulator/db8500-prcmu.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Power domain regulators on DB8500
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/db8500-prcmu.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#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,
+ .fixed_uV = 1800000,
+ .n_voltages = 1,
+ },
+ .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 = devm_regulator_register(&pdev->dev, &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);
+ 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)
+{
+ ux500_regulator_debug_exit();
+
+ return 0;
+}
+
+static struct platform_driver db8500_regulator_driver = {
+ .driver = {
+ .name = "db8500-prcmu-regulators",
+ },
+ .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 000000000..8976141c1
--- /dev/null
+++ b/drivers/regulator/dbx500-prcmu.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * UX500 common part of Power domain regulators
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/regulator/driver.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#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 CONFIG_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;
+
+static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p)
+{
+ /* print power state count */
+ seq_printf(s, "ux500-regulator power state count: %i\n",
+ power_state_active_get());
+
+ 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)
+{
+ int i;
+
+ /* print dump header */
+ seq_puts(s, "ux500-regulator status:\n");
+ seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after");
+
+ for (i = 0; i < rdebug.num_regulators; i++) {
+ struct dbx500_regulator_info *info;
+ /* Access per-regulator data */
+ info = &rdebug.regulator_array[i];
+
+ /* print status */
+ 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");
+ }
+
+ 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)
+ goto exit_destroy_power_state;
+
+ rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
+ if (!rdebug.state_after_suspend)
+ 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 000000000..c8e51ace9
--- /dev/null
+++ b/drivers/regulator/dbx500-prcmu.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Bengt Jonsson <bengt.jonsson@stericsson.com> for ST-Ericsson,
+ * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License Terms: GNU General Public License v2
+ *
+ */
+
+#ifndef DBX500_REGULATOR_H
+#define DBX500_REGULATOR_H
+
+#include <linux/platform_device.h>
+
+/**
+ * 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 CONFIG_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/devres.c b/drivers/regulator/devres.c
new file mode 100644
index 000000000..784e3bf32
--- /dev/null
+++ b/drivers/regulator/devres.c
@@ -0,0 +1,486 @@
+/*
+ * devres.c -- Voltage/Current Regulator framework devres implementation.
+ *
+ * Copyright 2013 Linaro Ltd
+ *
+ * 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 <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+static void devm_regulator_release(struct device *dev, void *res)
+{
+ regulator_put(*(struct regulator **)res);
+}
+
+static struct regulator *_devm_regulator_get(struct device *dev, const char *id,
+ int get_type)
+{
+ 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, get_type);
+ if (!IS_ERR(regulator)) {
+ *ptr = regulator;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return regulator;
+}
+
+/**
+ * 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)
+{
+ return _devm_regulator_get(dev, id, NORMAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get);
+
+/**
+ * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive()
+ * @dev: device for regulator "consumer"
+ * @id: Supply name or regulator ID.
+ *
+ * Managed regulator_get_exclusive(). Regulators returned from this function
+ * are automatically regulator_put() on driver detach. See regulator_get() for
+ * more information.
+ */
+struct regulator *devm_regulator_get_exclusive(struct device *dev,
+ const char *id)
+{
+ return _devm_regulator_get(dev, id, EXCLUSIVE_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive);
+
+/**
+ * devm_regulator_get_optional - Resource managed regulator_get_optional()
+ * @dev: device for regulator "consumer"
+ * @id: Supply name or regulator ID.
+ *
+ * Managed regulator_get_optional(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * regulator_get_optional() for more information.
+ */
+struct regulator *devm_regulator_get_optional(struct device *dev,
+ const char *id)
+{
+ return _devm_regulator_get(dev, id, OPTIONAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_optional);
+
+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);
+
+struct regulator_bulk_devres {
+ struct regulator_bulk_data *consumers;
+ int num_consumers;
+};
+
+static void devm_regulator_bulk_release(struct device *dev, void *res)
+{
+ struct regulator_bulk_devres *devres = res;
+
+ regulator_bulk_free(devres->num_consumers, devres->consumers);
+}
+
+/**
+ * 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)
+{
+ struct regulator_bulk_devres *devres;
+ int ret;
+
+ devres = devres_alloc(devm_regulator_bulk_release,
+ sizeof(*devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = regulator_bulk_get(dev, num_consumers, consumers);
+ if (!ret) {
+ devres->consumers = consumers;
+ devres->num_consumers = num_consumers;
+ devres_add(dev, devres);
+ } else {
+ devres_free(devres);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_get);
+
+static void devm_rdev_release(struct device *dev, void *res)
+{
+ regulator_unregister(*(struct regulator_dev **)res);
+}
+
+/**
+ * devm_regulator_register - Resource managed regulator_register()
+ * @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. The regulator will automatically be released when the device
+ * is unbound.
+ */
+struct regulator_dev *devm_regulator_register(struct device *dev,
+ const struct regulator_desc *regulator_desc,
+ const struct regulator_config *config)
+{
+ struct regulator_dev **ptr, *rdev;
+
+ ptr = devres_alloc(devm_rdev_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ rdev = regulator_register(regulator_desc, config);
+ if (!IS_ERR(rdev)) {
+ *ptr = rdev;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return rdev;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_register);
+
+static int devm_rdev_match(struct device *dev, void *res, void *data)
+{
+ struct regulator_dev **r = res;
+ if (!r || !*r) {
+ WARN_ON(!r || !*r);
+ return 0;
+ }
+ return *r == data;
+}
+
+/**
+ * devm_regulator_unregister - Resource managed regulator_unregister()
+ * @regulator: regulator to free
+ *
+ * Unregister a regulator registered with devm_regulator_register().
+ * Normally this function will not need to be called and the resource
+ * management code will ensure that the resource is freed.
+ */
+void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev);
+ if (rc != 0)
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_unregister);
+
+struct regulator_supply_alias_match {
+ struct device *dev;
+ const char *id;
+};
+
+static int devm_regulator_match_supply_alias(struct device *dev, void *res,
+ void *data)
+{
+ struct regulator_supply_alias_match *match = res;
+ struct regulator_supply_alias_match *target = data;
+
+ return match->dev == target->dev && strcmp(match->id, target->id) == 0;
+}
+
+static void devm_regulator_destroy_supply_alias(struct device *dev, void *res)
+{
+ struct regulator_supply_alias_match *match = res;
+
+ regulator_unregister_supply_alias(match->dev, match->id);
+}
+
+/**
+ * devm_regulator_register_supply_alias - Resource managed
+ * regulator_register_supply_alias()
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: Supply name or regulator ID
+ * @alias_dev: device that should be used to lookup the supply
+ * @alias_id: Supply name or regulator ID that should be used to lookup the
+ * supply
+ *
+ * The supply alias will automatically be unregistered when the source
+ * device is unbound.
+ */
+int devm_regulator_register_supply_alias(struct device *dev, const char *id,
+ struct device *alias_dev,
+ const char *alias_id)
+{
+ struct regulator_supply_alias_match *match;
+ int ret;
+
+ match = devres_alloc(devm_regulator_destroy_supply_alias,
+ sizeof(struct regulator_supply_alias_match),
+ GFP_KERNEL);
+ if (!match)
+ return -ENOMEM;
+
+ match->dev = dev;
+ match->id = id;
+
+ ret = regulator_register_supply_alias(dev, id, alias_dev, alias_id);
+ if (ret < 0) {
+ devres_free(match);
+ return ret;
+ }
+
+ devres_add(dev, match);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_register_supply_alias);
+
+/**
+ * devm_regulator_unregister_supply_alias - Resource managed
+ * regulator_unregister_supply_alias()
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: Supply name or regulator ID
+ *
+ * Unregister an alias registered with
+ * devm_regulator_register_supply_alias(). Normally this function
+ * will not need to be called and the resource management code
+ * will ensure that the resource is freed.
+ */
+void devm_regulator_unregister_supply_alias(struct device *dev, const char *id)
+{
+ struct regulator_supply_alias_match match;
+ int rc;
+
+ match.dev = dev;
+ match.id = id;
+
+ rc = devres_release(dev, devm_regulator_destroy_supply_alias,
+ devm_regulator_match_supply_alias, &match);
+ if (rc != 0)
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_unregister_supply_alias);
+
+/**
+ * devm_regulator_bulk_register_supply_alias - Managed register
+ * multiple aliases
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: List of supply names or regulator IDs
+ * @alias_dev: device that should be used to lookup the supply
+ * @alias_id: List of supply names or regulator IDs that should be used to
+ * lookup the supply
+ * @num_id: Number of aliases to register
+ *
+ * @return 0 on success, an errno on failure.
+ *
+ * This helper function allows drivers to register several supply
+ * aliases in one operation, the aliases will be automatically
+ * unregisters when the source device is unbound. If any of the
+ * aliases cannot be registered any aliases that were registered
+ * will be removed before returning to the caller.
+ */
+int devm_regulator_bulk_register_supply_alias(struct device *dev,
+ const char *const *id,
+ struct device *alias_dev,
+ const char *const *alias_id,
+ int num_id)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < num_id; ++i) {
+ ret = devm_regulator_register_supply_alias(dev, id[i],
+ alias_dev,
+ alias_id[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_err(dev,
+ "Failed to create supply alias %s,%s -> %s,%s\n",
+ id[i], dev_name(dev), alias_id[i], dev_name(alias_dev));
+
+ while (--i >= 0)
+ devm_regulator_unregister_supply_alias(dev, id[i]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_register_supply_alias);
+
+/**
+ * devm_regulator_bulk_unregister_supply_alias - Managed unregister
+ * multiple aliases
+ *
+ * @dev: device that will be given as the regulator "consumer"
+ * @id: List of supply names or regulator IDs
+ * @num_id: Number of aliases to unregister
+ *
+ * Unregister aliases registered with
+ * devm_regulator_bulk_register_supply_alias(). Normally this function
+ * will not need to be called and the resource management code
+ * will ensure that the resource is freed.
+ */
+void devm_regulator_bulk_unregister_supply_alias(struct device *dev,
+ const char *const *id,
+ int num_id)
+{
+ int i;
+
+ for (i = 0; i < num_id; ++i)
+ devm_regulator_unregister_supply_alias(dev, id[i]);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias);
+
+struct regulator_notifier_match {
+ struct regulator *regulator;
+ struct notifier_block *nb;
+};
+
+static int devm_regulator_match_notifier(struct device *dev, void *res,
+ void *data)
+{
+ struct regulator_notifier_match *match = res;
+ struct regulator_notifier_match *target = data;
+
+ return match->regulator == target->regulator && match->nb == target->nb;
+}
+
+static void devm_regulator_destroy_notifier(struct device *dev, void *res)
+{
+ struct regulator_notifier_match *match = res;
+
+ regulator_unregister_notifier(match->regulator, match->nb);
+}
+
+/**
+ * devm_regulator_register_notifier - Resource managed
+ * regulator_register_notifier
+ *
+ * @regulator: regulator source
+ * @nb: notifier block
+ *
+ * The notifier will be registers under the consumer device and be
+ * automatically be unregistered when the source device is unbound.
+ */
+int devm_regulator_register_notifier(struct regulator *regulator,
+ struct notifier_block *nb)
+{
+ struct regulator_notifier_match *match;
+ int ret;
+
+ match = devres_alloc(devm_regulator_destroy_notifier,
+ sizeof(struct regulator_notifier_match),
+ GFP_KERNEL);
+ if (!match)
+ return -ENOMEM;
+
+ match->regulator = regulator;
+ match->nb = nb;
+
+ ret = regulator_register_notifier(regulator, nb);
+ if (ret < 0) {
+ devres_free(match);
+ return ret;
+ }
+
+ devres_add(regulator->dev, match);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_register_notifier);
+
+/**
+ * devm_regulator_unregister_notifier - Resource managed
+ * regulator_unregister_notifier()
+ *
+ * @regulator: regulator source
+ * @nb: notifier block
+ *
+ * Unregister a notifier registered with devm_regulator_register_notifier().
+ * Normally this function will not need to be called and the resource
+ * management code will ensure that the resource is freed.
+ */
+void devm_regulator_unregister_notifier(struct regulator *regulator,
+ struct notifier_block *nb)
+{
+ struct regulator_notifier_match match;
+ int rc;
+
+ match.regulator = regulator;
+ match.nb = nb;
+
+ rc = devres_release(regulator->dev, devm_regulator_destroy_notifier,
+ devm_regulator_match_notifier, &match);
+ if (rc != 0)
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_unregister_notifier);
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
new file mode 100644
index 000000000..cde749778
--- /dev/null
+++ b/drivers/regulator/dummy.c
@@ -0,0 +1,93 @@
+/*
+ * dummy.c
+ *
+ * Copyright 2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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.
+ *
+ * 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 <linux/err.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include "dummy.h"
+
+struct regulator_dev *dummy_regulator_rdev;
+
+static struct regulator_init_data dummy_initdata = {
+ .constraints = {
+ .always_on = 1,
+ },
+};
+
+static struct regulator_ops dummy_ops;
+
+static const 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",
+ },
+};
+
+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 000000000..97a11b7e8
--- /dev/null
+++ b/drivers/regulator/dummy.h
@@ -0,0 +1,27 @@
+/*
+ * dummy.h
+ *
+ * Copyright 2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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.
+ *
+ * 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 000000000..a3bc80371
--- /dev/null
+++ b/drivers/regulator/fan53555.c
@@ -0,0 +1,496 @@
+/*
+ * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver.
+ *
+ * Supported Part Numbers:
+ * FAN53555UC00X/01X/03X/04X/05X
+ *
+ * Copyright (c) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/fan53555.h>
+
+/* 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 */
+
+enum fan53555_vendor {
+ FAN53555_VENDOR_FAIRCHILD = 0,
+ FAN53555_VENDOR_SILERGY,
+};
+
+/* 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,
+ FAN53555_CHIP_ID_08 = 8,
+};
+
+/* IC mask revision */
+enum {
+ FAN53555_CHIP_REV_00 = 0x3,
+ FAN53555_CHIP_REV_13 = 0xf,
+};
+
+enum {
+ SILERGY_SYR82X = 8,
+};
+
+struct fan53555_device_info {
+ enum fan53555_vendor vendor;
+ 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 ret;
+ ret = regmap_update_bits(di->regmap, di->sleep_reg,
+ VSEL_NSEL_MASK, ret);
+ if (ret < 0)
+ return ret;
+ /* 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_suspend_enable(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+ return regmap_update_bits(di->regmap, di->sleep_reg,
+ VSEL_BUCK_EN, VSEL_BUCK_EN);
+}
+
+static int fan53555_set_suspend_disable(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+ return regmap_update_bits(di->regmap, di->sleep_reg,
+ VSEL_BUCK_EN, 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 const int slew_rates[] = {
+ 64000,
+ 32000,
+ 16000,
+ 8000,
+ 4000,
+ 2000,
+ 1000,
+ 500,
+};
+
+static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int regval = -1, i;
+
+ for (i = 0; i < ARRAY_SIZE(slew_rates); i++) {
+ if (ramp <= slew_rates[i])
+ regval = i;
+ else
+ break;
+ }
+
+ if (regval < 0) {
+ dev_err(di->dev, "unsupported ramp value %d\n", ramp);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(di->regmap, FAN53555_CONTROL,
+ CTL_SLEW_MASK, regval << CTL_SLEW_SHIFT);
+}
+
+static const struct regulator_ops fan53555_regulator_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .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,
+ .set_ramp_delay = fan53555_set_ramp,
+ .set_suspend_enable = fan53555_set_suspend_enable,
+ .set_suspend_disable = fan53555_set_suspend_disable,
+};
+
+static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
+{
+ /* Init voltage range and step */
+ switch (di->chip_id) {
+ case FAN53555_CHIP_ID_00:
+ switch (di->chip_rev) {
+ case FAN53555_CHIP_REV_00:
+ di->vsel_min = 600000;
+ di->vsel_step = 10000;
+ break;
+ case FAN53555_CHIP_REV_13:
+ di->vsel_min = 800000;
+ di->vsel_step = 10000;
+ break;
+ default:
+ dev_err(di->dev,
+ "Chip ID %d with rev %d not supported!\n",
+ di->chip_id, di->chip_rev);
+ return -EINVAL;
+ }
+ break;
+ case FAN53555_CHIP_ID_01:
+ case FAN53555_CHIP_ID_03:
+ case FAN53555_CHIP_ID_05:
+ case FAN53555_CHIP_ID_08:
+ 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 not supported!\n", di->chip_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fan53555_voltages_setup_silergy(struct fan53555_device_info *di)
+{
+ /* Init voltage range and step */
+ switch (di->chip_id) {
+ case SILERGY_SYR82X:
+ di->vsel_min = 712500;
+ di->vsel_step = 12500;
+ break;
+ default:
+ dev_err(di->dev,
+ "Chip ID %d not supported!\n", di->chip_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* 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)
+{
+ int ret = 0;
+
+ /* 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;
+ }
+
+ switch (di->vendor) {
+ case FAN53555_VENDOR_FAIRCHILD:
+ ret = fan53555_voltages_setup_fairchild(di);
+ break;
+ case FAN53555_VENDOR_SILERGY:
+ ret = fan53555_voltages_setup_silergy(di);
+ break;
+ default:
+ dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+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->supply_name = "vin";
+ 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 = devm_regulator_register(di->dev, &di->desc, config);
+ return PTR_ERR_OR_ZERO(di->rdev);
+}
+
+static const struct regmap_config fan53555_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev,
+ struct device_node *np,
+ const struct regulator_desc *desc)
+{
+ struct fan53555_platform_data *pdata;
+ int ret;
+ u32 tmp;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->regulator = of_get_regulator_init_data(dev, np, desc);
+
+ ret = of_property_read_u32(np, "fcs,suspend-voltage-selector",
+ &tmp);
+ if (!ret)
+ pdata->sleep_vsel_id = tmp;
+
+ return pdata;
+}
+
+static const struct of_device_id fan53555_dt_ids[] = {
+ {
+ .compatible = "fcs,fan53555",
+ .data = (void *)FAN53555_VENDOR_FAIRCHILD
+ }, {
+ .compatible = "silergy,syr827",
+ .data = (void *)FAN53555_VENDOR_SILERGY,
+ }, {
+ .compatible = "silergy,syr828",
+ .data = (void *)FAN53555_VENDOR_SILERGY,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, fan53555_dt_ids);
+
+static int fan53555_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct fan53555_device_info *di;
+ struct fan53555_platform_data *pdata;
+ struct regulator_config config = { };
+ unsigned int val;
+ int ret;
+
+ di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
+ GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata)
+ pdata = fan53555_parse_dt(&client->dev, np, &di->desc);
+
+ if (!pdata || !pdata->regulator) {
+ dev_err(&client->dev, "Platform data not found!\n");
+ return -ENODEV;
+ }
+
+ di->regulator = pdata->regulator;
+ if (client->dev.of_node) {
+ di->vendor =
+ (unsigned long)of_device_get_match_data(&client->dev);
+ } else {
+ /* if no ramp constraint set, get the pdata ramp_delay */
+ if (!di->regulator->constraints.ramp_delay) {
+ int slew_idx = (pdata->slew_rate & 0x7)
+ ? pdata->slew_rate : 0;
+
+ di->regulator->constraints.ramp_delay
+ = slew_rates[slew_idx];
+ }
+
+ di->vendor = id->driver_data;
+ }
+
+ 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;
+ 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 ret;
+ }
+ 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 ret;
+ }
+ 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;
+ config.of_node = np;
+
+ ret = fan53555_regulator_register(di, &config);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register regulator!\n");
+ return ret;
+
+}
+
+static const struct i2c_device_id fan53555_id[] = {
+ {
+ .name = "fan53555",
+ .driver_data = FAN53555_VENDOR_FAIRCHILD
+ }, {
+ .name = "syr827",
+ .driver_data = FAN53555_VENDOR_SILERGY
+ }, {
+ .name = "syr828",
+ .driver_data = FAN53555_VENDOR_SILERGY
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, fan53555_id);
+
+static struct i2c_driver fan53555_regulator_driver = {
+ .driver = {
+ .name = "fan53555-regulator",
+ .of_match_table = of_match_ptr(fan53555_dt_ids),
+ },
+ .probe = fan53555_regulator_probe,
+ .id_table = fan53555_id,
+};
+
+module_i2c_driver(fan53555_regulator_driver);
+
+MODULE_AUTHOR("Yunfan Zhang <yfzhang@marvell.com>");
+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 000000000..777fac6fb
--- /dev/null
+++ b/drivers/regulator/fixed-helper.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+
+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 000000000..988a7472c
--- /dev/null
+++ b/drivers/regulator/fixed.c
@@ -0,0 +1,225 @@
+/*
+ * fixed.c
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Copyright (c) 2009 Nokia Corporation
+ * Roger Quadros <ext-roger.quadros@nokia.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.
+ *
+ * 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 <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/fixed.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+
+struct fixed_voltage_data {
+ struct regulator_desc desc;
+ struct regulator_dev *dev;
+};
+
+
+/**
+ * of_get_fixed_voltage_config - extract fixed_voltage_config structure info
+ * @dev: device requesting for fixed_voltage_config
+ * @desc: regulator description
+ *
+ * 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,
+ const struct regulator_desc *desc)
+{
+ struct fixed_voltage_config *config;
+ struct device_node *np = dev->of_node;
+ 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, desc);
+ 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);
+ if ((config->gpio < 0) && (config->gpio != -ENOENT))
+ return ERR_PTR(config->gpio);
+
+ of_property_read_u32(np, "startup-delay-us", &config->startup_delay);
+
+ config->enable_high = of_property_read_bool(np, "enable-active-high");
+ config->gpio_is_open_drain = of_property_read_bool(np,
+ "gpio-open-drain");
+
+ if (of_find_property(np, "vin-supply", NULL))
+ config->input_supply = "vin";
+
+ return config;
+}
+
+static struct regulator_ops fixed_voltage_ops = {
+};
+
+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;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ config = of_get_fixed_voltage_config(&pdev->dev,
+ &drvdata->desc);
+ if (IS_ERR(config))
+ return PTR_ERR(config);
+ } else {
+ config = dev_get_platdata(&pdev->dev);
+ }
+
+ if (!config)
+ return -ENOMEM;
+
+ drvdata->desc.name = devm_kstrdup(&pdev->dev,
+ config->supply_name,
+ GFP_KERNEL);
+ if (drvdata->desc.name == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate supply name\n");
+ return -ENOMEM;
+ }
+ 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 = devm_kstrdup(&pdev->dev,
+ config->input_supply,
+ GFP_KERNEL);
+ if (!drvdata->desc.supply_name) {
+ dev_err(&pdev->dev,
+ "Failed to allocate input supply\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (config->microvolts)
+ drvdata->desc.n_voltages = 1;
+
+ drvdata->desc.fixed_uV = config->microvolts;
+
+ if (gpio_is_valid(config->gpio)) {
+ cfg.ena_gpio = config->gpio;
+ if (pdev->dev.of_node)
+ cfg.ena_gpio_initialized = true;
+ }
+ 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 = devm_regulator_register(&pdev->dev, &drvdata->desc,
+ &cfg);
+ if (IS_ERR(drvdata->dev)) {
+ ret = PTR_ERR(drvdata->dev);
+ dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+ dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name,
+ drvdata->desc.fixed_uV);
+
+ 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,
+ .driver = {
+ .name = "reg-fixed-voltage",
+ .of_match_table = of_match_ptr(fixed_of_match),
+ },
+};
+
+static int __init regulator_fixed_voltage_init(void)
+{
+ return platform_driver_register(&regulator_fixed_voltage_driver);
+}
+subsys_initcall(regulator_fixed_voltage_init);
+
+static void __exit regulator_fixed_voltage_exit(void)
+{
+ platform_driver_unregister(&regulator_fixed_voltage_driver);
+}
+module_exit(regulator_fixed_voltage_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+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 000000000..b2f5ec4f6
--- /dev/null
+++ b/drivers/regulator/gpio-regulator.c
@@ -0,0 +1,430 @@
+/*
+ * gpio-regulator.c
+ *
+ * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on fixed.c
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Copyright (c) 2009 Nokia Corporation
+ * Roger Quadros <ext-roger.quadros@nokia.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.
+ *
+ * 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 <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/gpio-regulator.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+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,
+ const struct regulator_desc *desc)
+{
+ struct gpio_regulator_config *config;
+ const char *regtype;
+ int proplen, gpio, i;
+ int ret;
+
+ 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, desc);
+ 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);
+ if (config->enable_gpio < 0 && config->enable_gpio != -ENOENT)
+ return ERR_PTR(config->enable_gpio);
+
+ /* Fetch GPIOs. - optional property*/
+ ret = of_gpio_count(np);
+ if ((ret < 0) && (ret != -ENOENT))
+ return ERR_PTR(ret);
+
+ if (ret > 0) {
+ config->nr_gpios = ret;
+ config->gpios = devm_kcalloc(dev,
+ config->nr_gpios, sizeof(struct gpio),
+ GFP_KERNEL);
+ if (!config->gpios)
+ return ERR_PTR(-ENOMEM);
+
+ proplen = of_property_count_u32_elems(np, "gpios-states");
+ /* optional property */
+ if (proplen < 0)
+ proplen = 0;
+
+ if (proplen > 0 && proplen != config->nr_gpios) {
+ dev_warn(dev, "gpios <-> gpios-states mismatch\n");
+ proplen = 0;
+ }
+
+ for (i = 0; i < config->nr_gpios; i++) {
+ gpio = of_get_named_gpio(np, "gpios", i);
+ if (gpio < 0) {
+ if (gpio != -ENOENT)
+ return ERR_PTR(gpio);
+ break;
+ }
+ config->gpios[i].gpio = gpio;
+ config->gpios[i].label = config->supply_name;
+ if (proplen > 0) {
+ of_property_read_u32_index(np, "gpios-states",
+ i, &ret);
+ if (ret)
+ config->gpios[i].flags =
+ GPIOF_OUT_INIT_HIGH;
+ }
+ }
+ }
+
+ /* Fetch states. */
+ proplen = of_property_count_u32_elems(np, "states");
+ if (proplen < 0) {
+ dev_err(dev, "No 'states' property found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ config->states = devm_kcalloc(dev,
+ proplen / 2,
+ sizeof(struct gpio_regulator_state),
+ GFP_KERNEL);
+ if (!config->states)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < proplen / 2; i++) {
+ of_property_read_u32_index(np, "states", i * 2,
+ &config->states[i].value);
+ of_property_read_u32_index(np, "states", i * 2 + 1,
+ &config->states[i].gpios);
+ }
+ config->nr_states = i;
+
+ config->type = REGULATOR_VOLTAGE;
+ ret = of_property_read_string(np, "regulator-type", &regtype);
+ if (ret >= 0) {
+ if (!strncmp("voltage", regtype, 7))
+ config->type = REGULATOR_VOLTAGE;
+ else if (!strncmp("current", regtype, 7))
+ config->type = REGULATOR_CURRENT;
+ else
+ dev_warn(dev, "Unknown regulator-type '%s'\n",
+ regtype);
+ }
+
+ 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 = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ struct gpio_regulator_data *drvdata;
+ struct regulator_config cfg = { };
+ int ptr, ret, state;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data),
+ GFP_KERNEL);
+ if (drvdata == NULL)
+ return -ENOMEM;
+
+ if (np) {
+ config = of_get_gpio_regulator_config(&pdev->dev, np,
+ &drvdata->desc);
+ if (IS_ERR(config))
+ return PTR_ERR(config);
+ }
+
+ drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL);
+ if (drvdata->desc.name == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate supply name\n");
+ return -ENOMEM;
+ }
+
+ if (config->nr_gpios != 0) {
+ 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->nr_gpios = config->nr_gpios;
+ ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "Could not obtain regulator setting GPIOs: %d\n",
+ ret);
+ goto err_memgpio;
+ }
+ }
+
+ 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_stategpio;
+ }
+ 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_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 (gpio_is_valid(config->enable_gpio)) {
+ cfg.ena_gpio = config->enable_gpio;
+ cfg.ena_gpio_initialized = true;
+ }
+ 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_memstate;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+ return 0;
+
+err_memstate:
+ kfree(drvdata->states);
+err_stategpio:
+ gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
+err_memgpio:
+ kfree(drvdata->gpios);
+err_name:
+ kfree(drvdata->desc.name);
+ 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", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, regulator_gpio_of_match);
+#endif
+
+static struct platform_driver gpio_regulator_driver = {
+ .probe = gpio_regulator_probe,
+ .remove = gpio_regulator_remove,
+ .driver = {
+ .name = "gpio-regulator",
+ .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 <heiko@sntech.de>");
+MODULE_DESCRIPTION("gpio voltage regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-regulator");
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
new file mode 100644
index 000000000..2ae7c3ac5
--- /dev/null
+++ b/drivers/regulator/helpers.c
@@ -0,0 +1,530 @@
+/*
+ * helpers.c -- Voltage/Current Regulator framework helper functions.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008 SlimLogic Ltd.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/module.h>
+
+/**
+ * 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;
+
+ val &= rdev->desc->enable_mask;
+
+ if (rdev->desc->enable_is_inverted) {
+ if (rdev->desc->enable_val)
+ return val != rdev->desc->enable_val;
+ return val == 0;
+ } else {
+ if (rdev->desc->enable_val)
+ return val == rdev->desc->enable_val;
+ return val != 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 = rdev->desc->disable_val;
+ } else {
+ val = rdev->desc->enable_val;
+ if (!val)
+ 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_val;
+ if (!val)
+ val = rdev->desc->enable_mask;
+ } else {
+ val = rdev->desc->disable_val;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_disable_regmap);
+
+/**
+ * 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);
+
+/**
+ * regulator_map_voltage_linear_range - map_voltage() for multiple linear ranges
+ *
+ * @rdev: Regulator to operate on
+ * @min_uV: Lower bound for voltage
+ * @max_uV: Upper bound for voltage
+ *
+ * Drivers providing linear_ranges in their descriptor can use this as
+ * their map_voltage() callback.
+ */
+int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ const struct regulator_linear_range *range;
+ int ret = -EINVAL;
+ int voltage, i;
+
+ if (!rdev->desc->n_linear_ranges) {
+ BUG_ON(!rdev->desc->n_linear_ranges);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
+ int linear_max_uV;
+
+ range = &rdev->desc->linear_ranges[i];
+ linear_max_uV = range->min_uV +
+ (range->max_sel - range->min_sel) * range->uV_step;
+
+ if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV))
+ continue;
+
+ if (min_uV <= range->min_uV)
+ min_uV = range->min_uV;
+
+ /* range->uV_step == 0 means fixed voltage range */
+ if (range->uV_step == 0) {
+ ret = 0;
+ } else {
+ ret = DIV_ROUND_UP(min_uV - range->min_uV,
+ range->uV_step);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret += range->min_sel;
+
+ break;
+ }
+
+ if (i == rdev->desc->n_linear_ranges)
+ return -EINVAL;
+
+ /* 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_range);
+
+/**
+ * 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_linear_range - List voltages for linear ranges
+ *
+ * @rdev: Regulator device
+ * @selector: Selector to convert into a voltage
+ *
+ * Regulators with a series of simple linear mappings between voltages
+ * and selectors can set linear_ranges in the regulator descriptor and
+ * then use this function as their list_voltage() operation,
+ */
+int regulator_list_voltage_linear_range(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ const struct regulator_linear_range *range;
+ int i;
+
+ if (!rdev->desc->n_linear_ranges) {
+ BUG_ON(!rdev->desc->n_linear_ranges);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
+ range = &rdev->desc->linear_ranges[i];
+
+ if (!(selector >= range->min_sel &&
+ selector <= range->max_sel))
+ continue;
+
+ selector -= range->min_sel;
+
+ return range->min_uV + (range->uV_step * selector);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regulator_list_voltage_linear_range);
+
+/**
+ * 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_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_val_on;
+ if (!val)
+ val = rdev->desc->bypass_mask;
+ } else {
+ val = rdev->desc->bypass_val_off;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
+ rdev->desc->bypass_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+
+/**
+ * regulator_set_soft_start_regmap - Default set_soft_start() using regmap
+ *
+ * @rdev: device to operate on.
+ */
+int regulator_set_soft_start_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ val = rdev->desc->soft_start_val_on;
+ if (!val)
+ val = rdev->desc->soft_start_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->soft_start_reg,
+ rdev->desc->soft_start_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_soft_start_regmap);
+
+/**
+ * regulator_set_pull_down_regmap - Default set_pull_down() using regmap
+ *
+ * @rdev: device to operate on.
+ */
+int regulator_set_pull_down_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ val = rdev->desc->pull_down_val_on;
+ if (!val)
+ val = rdev->desc->pull_down_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->pull_down_reg,
+ rdev->desc->pull_down_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_pull_down_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;
+ unsigned int val_on = rdev->desc->bypass_val_on;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ if (!val_on)
+ val_on = rdev->desc->bypass_mask;
+
+ *enable = (val & rdev->desc->bypass_mask) == val_on;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
+
+/**
+ * regulator_set_active_discharge_regmap - Default set_active_discharge()
+ * using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: state to set, 0 to disable and 1 to enable.
+ */
+int regulator_set_active_discharge_regmap(struct regulator_dev *rdev,
+ bool enable)
+{
+ unsigned int val;
+
+ if (enable)
+ val = rdev->desc->active_discharge_on;
+ else
+ val = rdev->desc->active_discharge_off;
+
+ return regmap_update_bits(rdev->regmap,
+ rdev->desc->active_discharge_reg,
+ rdev->desc->active_discharge_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_active_discharge_regmap);
diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c
new file mode 100644
index 000000000..259c3a865
--- /dev/null
+++ b/drivers/regulator/hi6421-regulator.c
@@ -0,0 +1,641 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) <2011-2014> HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright (c) <2013-2014> Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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 <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+/*
+ * struct hi6421_regulator_pdata - Hi6421 regulator data of platform device
+ * @lock: mutex to serialize regulator enable
+ */
+struct hi6421_regulator_pdata {
+ struct mutex lock;
+};
+
+/*
+ * struct hi6421_regulator_info - hi6421 regulator information
+ * @desc: regulator description
+ * @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep
+ * @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only
+ */
+struct hi6421_regulator_info {
+ struct regulator_desc desc;
+ u8 mode_mask;
+ u32 eco_microamp;
+};
+
+/* HI6421 regulators */
+enum hi6421_regulator_id {
+ HI6421_LDO0,
+ HI6421_LDO1,
+ HI6421_LDO2,
+ HI6421_LDO3,
+ HI6421_LDO4,
+ HI6421_LDO5,
+ HI6421_LDO6,
+ HI6421_LDO7,
+ HI6421_LDO8,
+ HI6421_LDO9,
+ HI6421_LDO10,
+ HI6421_LDO11,
+ HI6421_LDO12,
+ HI6421_LDO13,
+ HI6421_LDO14,
+ HI6421_LDO15,
+ HI6421_LDO16,
+ HI6421_LDO17,
+ HI6421_LDO18,
+ HI6421_LDO19,
+ HI6421_LDO20,
+ HI6421_LDOAUDIO,
+ HI6421_BUCK0,
+ HI6421_BUCK1,
+ HI6421_BUCK2,
+ HI6421_BUCK3,
+ HI6421_BUCK4,
+ HI6421_BUCK5,
+ HI6421_NUM_REGULATORS,
+};
+
+#define HI6421_REGULATOR_OF_MATCH(_name, id) \
+{ \
+ .name = #_name, \
+ .driver_data = (void *) HI6421_##id, \
+}
+
+static struct of_regulator_match hi6421_regulator_match[] = {
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout0, LDO0),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout1, LDO1),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout2, LDO2),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout3, LDO3),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout4, LDO4),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout5, LDO5),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout6, LDO6),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout7, LDO7),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout8, LDO8),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout9, LDO9),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout10, LDO10),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout11, LDO11),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout12, LDO12),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout13, LDO13),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout14, LDO14),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout15, LDO15),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout16, LDO16),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout17, LDO17),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout18, LDO18),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout19, LDO19),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout20, LDO20),
+ HI6421_REGULATOR_OF_MATCH(hi6421_vout_audio, LDOAUDIO),
+ HI6421_REGULATOR_OF_MATCH(hi6421_buck0, BUCK0),
+ HI6421_REGULATOR_OF_MATCH(hi6421_buck1, BUCK1),
+ HI6421_REGULATOR_OF_MATCH(hi6421_buck2, BUCK2),
+ HI6421_REGULATOR_OF_MATCH(hi6421_buck3, BUCK3),
+ HI6421_REGULATOR_OF_MATCH(hi6421_buck4, BUCK4),
+ HI6421_REGULATOR_OF_MATCH(hi6421_buck5, BUCK5),
+};
+
+/* LDO 0, 4~7, 9~14, 16~20 have same voltage table. */
+static const unsigned int ldo_0_voltages[] = {
+ 1500000, 1800000, 2400000, 2500000,
+ 2600000, 2700000, 2850000, 3000000,
+};
+
+/* LDO 8, 15 have same voltage table. */
+static const unsigned int ldo_8_voltages[] = {
+ 1500000, 1800000, 2400000, 2600000,
+ 2700000, 2850000, 3000000, 3300000,
+};
+
+/* Ranges are sorted in ascending order. */
+static const struct regulator_linear_range ldo_audio_volt_range[] = {
+ REGULATOR_LINEAR_RANGE(2800000, 0, 3, 50000),
+ REGULATOR_LINEAR_RANGE(3000000, 4, 7, 100000),
+};
+
+static const unsigned int buck_3_voltages[] = {
+ 950000, 1050000, 1100000, 1117000,
+ 1134000, 1150000, 1167000, 1200000,
+};
+
+static const unsigned int buck_4_voltages[] = {
+ 1150000, 1200000, 1250000, 1350000,
+ 1700000, 1800000, 1900000, 2000000,
+};
+
+static const unsigned int buck_5_voltages[] = {
+ 1150000, 1200000, 1250000, 1350000,
+ 1600000, 1700000, 1800000, 1900000,
+};
+
+static const struct regulator_ops hi6421_ldo_ops;
+static const struct regulator_ops hi6421_ldo_linear_ops;
+static const struct regulator_ops hi6421_ldo_linear_range_ops;
+static const struct regulator_ops hi6421_buck012_ops;
+static const struct regulator_ops hi6421_buck345_ops;
+
+#define HI6421_LDO_ENABLE_TIME (350)
+/*
+ * _id - LDO id name string
+ * v_table - voltage table
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421_LDO(_id, v_table, vreg, vmask, ereg, emask, \
+ odelay, ecomask, ecoamp) \
+ [HI6421_##_id] = { \
+ .desc = { \
+ .name = #_id, \
+ .ops = &hi6421_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI6421_##_id, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(v_table), \
+ .volt_table = v_table, \
+ .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
+ .enable_mask = emask, \
+ .enable_time = HI6421_LDO_ENABLE_TIME, \
+ .off_on_delay = odelay, \
+ }, \
+ .mode_mask = ecomask, \
+ .eco_microamp = ecoamp, \
+ }
+
+/* HI6421 LDO1~3 are linear voltage regulators at fixed uV_step
+ *
+ * _id - LDO id name string
+ * _min_uV - minimum voltage supported in uV
+ * n_volt - number of votages available
+ * vstep - voltage increase in each linear step in uV
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421_LDO_LINEAR(_id, _min_uV, n_volt, vstep, vreg, vmask, \
+ ereg, emask, odelay, ecomask, ecoamp) \
+ [HI6421_##_id] = { \
+ .desc = { \
+ .name = #_id, \
+ .ops = &hi6421_ldo_linear_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI6421_##_id, \
+ .owner = THIS_MODULE, \
+ .min_uV = _min_uV, \
+ .n_voltages = n_volt, \
+ .uV_step = vstep, \
+ .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
+ .enable_mask = emask, \
+ .enable_time = HI6421_LDO_ENABLE_TIME, \
+ .off_on_delay = odelay, \
+ }, \
+ .mode_mask = ecomask, \
+ .eco_microamp = ecoamp, \
+ }
+
+/* HI6421 LDOAUDIO is a linear voltage regulator with two 4-step ranges
+ *
+ * _id - LDO id name string
+ * n_volt - number of votages available
+ * volt_ranges - array of regulator_linear_range
+ * vstep - voltage increase in each linear step in uV
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421_LDO_LINEAR_RANGE(_id, n_volt, volt_ranges, vreg, vmask, \
+ ereg, emask, odelay, ecomask, ecoamp) \
+ [HI6421_##_id] = { \
+ .desc = { \
+ .name = #_id, \
+ .ops = &hi6421_ldo_linear_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI6421_##_id, \
+ .owner = THIS_MODULE, \
+ .n_voltages = n_volt, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
+ .enable_mask = emask, \
+ .enable_time = HI6421_LDO_ENABLE_TIME, \
+ .off_on_delay = odelay, \
+ }, \
+ .mode_mask = ecomask, \
+ .eco_microamp = ecoamp, \
+ }
+
+/* HI6421 BUCK0/1/2 are linear voltage regulators at fixed uV_step
+ *
+ * _id - BUCK0/1/2 id name string
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * sleepmask - mask of sleep mode
+ * etime - enable time
+ * odelay - off/on delay time in uS
+ */
+#define HI6421_BUCK012(_id, vreg, vmask, ereg, emask, sleepmask, \
+ etime, odelay) \
+ [HI6421_##_id] = { \
+ .desc = { \
+ .name = #_id, \
+ .ops = &hi6421_buck012_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI6421_##_id, \
+ .owner = THIS_MODULE, \
+ .min_uV = 700000, \
+ .n_voltages = 128, \
+ .uV_step = 7086, \
+ .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
+ .enable_mask = emask, \
+ .enable_time = etime, \
+ .off_on_delay = odelay, \
+ }, \
+ .mode_mask = sleepmask, \
+ }
+
+/* HI6421 BUCK3/4/5 share similar configurations as LDOs, with exception
+ * that it supports SLEEP mode, so has different .ops.
+ *
+ * _id - LDO id name string
+ * v_table - voltage table
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * sleepmask - mask of sleep mode
+ */
+#define HI6421_BUCK345(_id, v_table, vreg, vmask, ereg, emask, \
+ odelay, sleepmask) \
+ [HI6421_##_id] = { \
+ .desc = { \
+ .name = #_id, \
+ .ops = &hi6421_buck345_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI6421_##_id, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(v_table), \
+ .volt_table = v_table, \
+ .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
+ .enable_mask = emask, \
+ .enable_time = HI6421_LDO_ENABLE_TIME, \
+ .off_on_delay = odelay, \
+ }, \
+ .mode_mask = sleepmask, \
+ }
+
+/* HI6421 regulator information */
+static struct hi6421_regulator_info
+ hi6421_regulator_info[HI6421_NUM_REGULATORS] = {
+ HI6421_LDO(LDO0, ldo_0_voltages, 0x20, 0x07, 0x20, 0x10,
+ 10000, 0x20, 8000),
+ HI6421_LDO_LINEAR(LDO1, 1700000, 4, 100000, 0x21, 0x03, 0x21, 0x10,
+ 10000, 0x20, 5000),
+ HI6421_LDO_LINEAR(LDO2, 1050000, 8, 50000, 0x22, 0x07, 0x22, 0x10,
+ 20000, 0x20, 8000),
+ HI6421_LDO_LINEAR(LDO3, 1050000, 8, 50000, 0x23, 0x07, 0x23, 0x10,
+ 20000, 0x20, 8000),
+ HI6421_LDO(LDO4, ldo_0_voltages, 0x24, 0x07, 0x24, 0x10,
+ 20000, 0x20, 8000),
+ HI6421_LDO(LDO5, ldo_0_voltages, 0x25, 0x07, 0x25, 0x10,
+ 20000, 0x20, 8000),
+ HI6421_LDO(LDO6, ldo_0_voltages, 0x26, 0x07, 0x26, 0x10,
+ 20000, 0x20, 8000),
+ HI6421_LDO(LDO7, ldo_0_voltages, 0x27, 0x07, 0x27, 0x10,
+ 20000, 0x20, 5000),
+ HI6421_LDO(LDO8, ldo_8_voltages, 0x28, 0x07, 0x28, 0x10,
+ 20000, 0x20, 8000),
+ HI6421_LDO(LDO9, ldo_0_voltages, 0x29, 0x07, 0x29, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO10, ldo_0_voltages, 0x2a, 0x07, 0x2a, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO11, ldo_0_voltages, 0x2b, 0x07, 0x2b, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO12, ldo_0_voltages, 0x2c, 0x07, 0x2c, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO13, ldo_0_voltages, 0x2d, 0x07, 0x2d, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO14, ldo_0_voltages, 0x2e, 0x07, 0x2e, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO15, ldo_8_voltages, 0x2f, 0x07, 0x2f, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO16, ldo_0_voltages, 0x30, 0x07, 0x30, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO17, ldo_0_voltages, 0x31, 0x07, 0x31, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO18, ldo_0_voltages, 0x32, 0x07, 0x32, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO19, ldo_0_voltages, 0x33, 0x07, 0x33, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO(LDO20, ldo_0_voltages, 0x34, 0x07, 0x34, 0x10,
+ 40000, 0x20, 8000),
+ HI6421_LDO_LINEAR_RANGE(LDOAUDIO, 8, ldo_audio_volt_range, 0x36,
+ 0x70, 0x36, 0x01, 40000, 0x02, 5000),
+ HI6421_BUCK012(BUCK0, 0x0d, 0x7f, 0x0c, 0x01, 0x10, 400, 20000),
+ HI6421_BUCK012(BUCK1, 0x0f, 0x7f, 0x0e, 0x01, 0x10, 400, 20000),
+ HI6421_BUCK012(BUCK2, 0x11, 0x7f, 0x10, 0x01, 0x10, 350, 100),
+ HI6421_BUCK345(BUCK3, buck_3_voltages, 0x13, 0x07, 0x12, 0x01,
+ 20000, 0x10),
+ HI6421_BUCK345(BUCK4, buck_4_voltages, 0x15, 0x07, 0x14, 0x01,
+ 20000, 0x10),
+ HI6421_BUCK345(BUCK5, buck_5_voltages, 0x17, 0x07, 0x16, 0x01,
+ 20000, 0x10),
+};
+
+static int hi6421_regulator_enable(struct regulator_dev *rdev)
+{
+ struct hi6421_regulator_pdata *pdata;
+
+ pdata = dev_get_drvdata(rdev->dev.parent);
+ /* hi6421 spec requires regulator enablement must be serialized:
+ * - Because when BUCK, LDO switching from off to on, it will have
+ * a huge instantaneous current; so you can not turn on two or
+ * more LDO or BUCKs simultaneously, or it may burn the chip.
+ */
+ mutex_lock(&pdata->lock);
+
+ /* call regulator regmap helper */
+ regulator_enable_regmap(rdev);
+
+ mutex_unlock(&pdata->lock);
+ return 0;
+}
+
+static unsigned int hi6421_regulator_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+ u32 reg_val;
+
+ regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
+ if (reg_val & info->mode_mask)
+ return REGULATOR_MODE_IDLE;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static unsigned int hi6421_regulator_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+ u32 reg_val;
+
+ regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
+ if (reg_val & info->mode_mask)
+ return REGULATOR_MODE_STANDBY;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int hi6421_regulator_ldo_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+ u32 new_mode;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ new_mode = 0;
+ break;
+ case REGULATOR_MODE_IDLE:
+ new_mode = info->mode_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode */
+ regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ info->mode_mask, new_mode);
+
+ return 0;
+}
+
+static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+ u32 new_mode;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ new_mode = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ new_mode = info->mode_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode */
+ regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ info->mode_mask, new_mode);
+
+ return 0;
+}
+
+static unsigned int
+hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (load_uA > info->eco_microamp)
+ return REGULATOR_MODE_NORMAL;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static const struct regulator_ops hi6421_ldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = hi6421_regulator_enable,
+ .disable = regulator_disable_regmap,
+ .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,
+ .get_mode = hi6421_regulator_ldo_get_mode,
+ .set_mode = hi6421_regulator_ldo_set_mode,
+ .get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
+};
+
+static const struct regulator_ops hi6421_ldo_linear_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = hi6421_regulator_enable,
+ .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 = hi6421_regulator_ldo_get_mode,
+ .set_mode = hi6421_regulator_ldo_set_mode,
+ .get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
+};
+
+static const struct regulator_ops hi6421_ldo_linear_range_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = hi6421_regulator_enable,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_mode = hi6421_regulator_ldo_get_mode,
+ .set_mode = hi6421_regulator_ldo_set_mode,
+ .get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
+};
+
+static const struct regulator_ops hi6421_buck012_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = hi6421_regulator_enable,
+ .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 = hi6421_regulator_buck_get_mode,
+ .set_mode = hi6421_regulator_buck_set_mode,
+};
+
+static const struct regulator_ops hi6421_buck345_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = hi6421_regulator_enable,
+ .disable = regulator_disable_regmap,
+ .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,
+ .get_mode = hi6421_regulator_buck_get_mode,
+ .set_mode = hi6421_regulator_buck_set_mode,
+};
+
+static int hi6421_regulator_register(struct platform_device *pdev,
+ struct regmap *rmap,
+ struct regulator_init_data *init_data,
+ int id, struct device_node *np)
+{
+ struct hi6421_regulator_info *info = NULL;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+
+ /* assign per-regulator data */
+ info = &hi6421_regulator_info[id];
+
+ config.dev = &pdev->dev;
+ config.init_data = init_data;
+ config.driver_data = info;
+ config.regmap = rmap;
+ config.of_node = np;
+
+ /* register regulator with framework */
+ rdev = devm_regulator_register(&pdev->dev, &info->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+static int hi6421_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np;
+ struct hi6421_pmic *pmic;
+ struct hi6421_regulator_pdata *pdata;
+ int i, ret = 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ mutex_init(&pdata->lock);
+ platform_set_drvdata(pdev, pdata);
+
+ np = of_get_child_by_name(dev->parent->of_node, "regulators");
+ if (!np)
+ return -ENODEV;
+
+ ret = of_regulator_match(dev, np,
+ hi6421_regulator_match,
+ ARRAY_SIZE(hi6421_regulator_match));
+ of_node_put(np);
+ if (ret < 0) {
+ dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+ return ret;
+ }
+
+ pmic = dev_get_drvdata(dev->parent);
+
+ for (i = 0; i < ARRAY_SIZE(hi6421_regulator_info); i++) {
+ ret = hi6421_regulator_register(pdev, pmic->regmap,
+ hi6421_regulator_match[i].init_data, i,
+ hi6421_regulator_match[i].of_node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id hi6421_regulator_table[] = {
+ { .name = "hi6421-regulator" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, hi6421_regulator_table);
+
+static struct platform_driver hi6421_regulator_driver = {
+ .id_table = hi6421_regulator_table,
+ .driver = {
+ .name = "hi6421-regulator",
+ },
+ .probe = hi6421_regulator_probe,
+};
+module_platform_driver(hi6421_regulator_driver);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/hi6421v530-regulator.c b/drivers/regulator/hi6421v530-regulator.c
new file mode 100644
index 000000000..c09bc7153
--- /dev/null
+++ b/drivers/regulator/hi6421v530-regulator.c
@@ -0,0 +1,214 @@
+/*
+ * Device driver for regulators in Hi6421V530 IC
+ *
+ * Copyright (c) <2017> HiSilicon Technologies Co., Ltd.
+ * http://www.hisilicon.com
+ * Copyright (c) <2017> Linaro Ltd.
+ * http://www.linaro.org
+ *
+ * Author: Wang Xiaoyin <hw.wangxiaoyin@hisilicon.com>
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * 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 <linux/mfd/hi6421-pmic.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+
+/*
+ * struct hi6421v530_regulator_info - hi6421v530 regulator information
+ * @desc: regulator description
+ * @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep
+ * @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only
+ */
+struct hi6421v530_regulator_info {
+ struct regulator_desc rdesc;
+ u8 mode_mask;
+ u32 eco_microamp;
+};
+
+/* HI6421v530 regulators */
+enum hi6421v530_regulator_id {
+ HI6421V530_LDO3,
+ HI6421V530_LDO9,
+ HI6421V530_LDO11,
+ HI6421V530_LDO15,
+ HI6421V530_LDO16,
+};
+
+static const unsigned int ldo_3_voltages[] = {
+ 1800000, 1825000, 1850000, 1875000,
+ 1900000, 1925000, 1950000, 1975000,
+ 2000000, 2025000, 2050000, 2075000,
+ 2100000, 2125000, 2150000, 2200000,
+};
+
+static const unsigned int ldo_9_11_voltages[] = {
+ 1750000, 1800000, 1825000, 2800000,
+ 2850000, 2950000, 3000000, 3300000,
+};
+
+static const unsigned int ldo_15_16_voltages[] = {
+ 1750000, 1800000, 2400000, 2600000,
+ 2700000, 2850000, 2950000, 3000000,
+};
+
+static const struct regulator_ops hi6421v530_ldo_ops;
+
+#define HI6421V530_LDO_ENABLE_TIME (350)
+
+/*
+ * _id - LDO id name string
+ * v_table - voltage table
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421V530_LDO(_ID, v_table, vreg, vmask, ereg, emask, \
+ odelay, ecomask, ecoamp) { \
+ .rdesc = { \
+ .name = #_ID, \
+ .of_match = of_match_ptr(#_ID), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &hi6421v530_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI6421V530_##_ID, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(v_table), \
+ .volt_table = v_table, \
+ .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
+ .enable_mask = emask, \
+ .enable_time = HI6421V530_LDO_ENABLE_TIME, \
+ .off_on_delay = odelay, \
+ }, \
+ .mode_mask = ecomask, \
+ .eco_microamp = ecoamp, \
+}
+
+/* HI6421V530 regulator information */
+
+static struct hi6421v530_regulator_info hi6421v530_regulator_info[] = {
+ HI6421V530_LDO(LDO3, ldo_3_voltages, 0x061, 0xf, 0x060, 0x2,
+ 20000, 0x6, 8000),
+ HI6421V530_LDO(LDO9, ldo_9_11_voltages, 0x06b, 0x7, 0x06a, 0x2,
+ 40000, 0x6, 8000),
+ HI6421V530_LDO(LDO11, ldo_9_11_voltages, 0x06f, 0x7, 0x06e, 0x2,
+ 40000, 0x6, 8000),
+ HI6421V530_LDO(LDO15, ldo_15_16_voltages, 0x077, 0x7, 0x076, 0x2,
+ 40000, 0x6, 8000),
+ HI6421V530_LDO(LDO16, ldo_15_16_voltages, 0x079, 0x7, 0x078, 0x2,
+ 40000, 0x6, 8000),
+};
+
+static unsigned int hi6421v530_regulator_ldo_get_mode(
+ struct regulator_dev *rdev)
+{
+ struct hi6421v530_regulator_info *info;
+ unsigned int reg_val;
+
+ info = rdev_get_drvdata(rdev);
+ regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
+
+ if (reg_val & (info->mode_mask))
+ return REGULATOR_MODE_IDLE;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int hi6421v530_regulator_ldo_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct hi6421v530_regulator_info *info;
+ unsigned int new_mode;
+
+ info = rdev_get_drvdata(rdev);
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ new_mode = 0;
+ break;
+ case REGULATOR_MODE_IDLE:
+ new_mode = info->mode_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ info->mode_mask, new_mode);
+
+ return 0;
+}
+
+
+static const struct regulator_ops hi6421v530_ldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .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,
+ .get_mode = hi6421v530_regulator_ldo_get_mode,
+ .set_mode = hi6421v530_regulator_ldo_set_mode,
+};
+
+static int hi6421v530_regulator_probe(struct platform_device *pdev)
+{
+ struct hi6421_pmic *pmic;
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ unsigned int i;
+
+ pmic = dev_get_drvdata(pdev->dev.parent);
+ if (!pmic) {
+ dev_err(&pdev->dev, "no pmic in the regulator parent node\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hi6421v530_regulator_info); i++) {
+ config.dev = pdev->dev.parent;
+ config.regmap = pmic->regmap;
+ config.driver_data = &hi6421v530_regulator_info[i];
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &hi6421v530_regulator_info[i].rdesc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ hi6421v530_regulator_info[i].rdesc.name);
+ return PTR_ERR(rdev);
+ }
+ }
+ return 0;
+}
+
+static const struct platform_device_id hi6421v530_regulator_table[] = {
+ { .name = "hi6421v530-regulator" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, hi6421v530_regulator_table);
+
+static struct platform_driver hi6421v530_regulator_driver = {
+ .id_table = hi6421v530_regulator_table,
+ .driver = {
+ .name = "hi6421v530-regulator",
+ },
+ .probe = hi6421v530_regulator_probe,
+};
+module_platform_driver(hi6421v530_regulator_driver);
+
+MODULE_AUTHOR("Wang Xiaoyin <hw.wangxiaoyin@hisilicon.com>");
+MODULE_DESCRIPTION("Hi6421v530 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c
new file mode 100644
index 000000000..36ae54b53
--- /dev/null
+++ b/drivers/regulator/hi655x-regulator.c
@@ -0,0 +1,234 @@
+/*
+ * Device driver for regulators in Hi655x IC
+ *
+ * Copyright (c) 2016 Hisilicon.
+ *
+ * Authors:
+ * Chen Feng <puck.chen@hisilicon.com>
+ * Fei Wang <w.f@huawei.com>
+ *
+ * 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 <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hi655x-pmic.h>
+
+struct hi655x_regulator {
+ unsigned int disable_reg;
+ unsigned int status_reg;
+ unsigned int ctrl_regs;
+ unsigned int ctrl_mask;
+ struct regulator_desc rdesc;
+};
+
+/* LDO7 & LDO10 */
+static const unsigned int ldo7_voltages[] = {
+ 1800000, 1850000, 2850000, 2900000,
+ 3000000, 3100000, 3200000, 3300000,
+};
+
+static const unsigned int ldo19_voltages[] = {
+ 1800000, 1850000, 1900000, 1750000,
+ 2800000, 2850000, 2900000, 3000000,
+};
+
+static const unsigned int ldo22_voltages[] = {
+ 900000, 1000000, 1050000, 1100000,
+ 1150000, 1175000, 1185000, 1200000,
+};
+
+enum hi655x_regulator_id {
+ HI655X_LDO0,
+ HI655X_LDO1,
+ HI655X_LDO2,
+ HI655X_LDO3,
+ HI655X_LDO4,
+ HI655X_LDO5,
+ HI655X_LDO6,
+ HI655X_LDO7,
+ HI655X_LDO8,
+ HI655X_LDO9,
+ HI655X_LDO10,
+ HI655X_LDO11,
+ HI655X_LDO12,
+ HI655X_LDO13,
+ HI655X_LDO14,
+ HI655X_LDO15,
+ HI655X_LDO16,
+ HI655X_LDO17,
+ HI655X_LDO18,
+ HI655X_LDO19,
+ HI655X_LDO20,
+ HI655X_LDO21,
+ HI655X_LDO22,
+};
+
+static int hi655x_is_enabled(struct regulator_dev *rdev)
+{
+ unsigned int value = 0;
+
+ struct hi655x_regulator *regulator = rdev_get_drvdata(rdev);
+
+ regmap_read(rdev->regmap, regulator->status_reg, &value);
+ return (value & BIT(regulator->ctrl_mask));
+}
+
+static int hi655x_disable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+
+ struct hi655x_regulator *regulator = rdev_get_drvdata(rdev);
+
+ ret = regmap_write(rdev->regmap, regulator->disable_reg,
+ BIT(regulator->ctrl_mask));
+ return ret;
+}
+
+static const struct regulator_ops hi655x_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = hi655x_disable,
+ .is_enabled = hi655x_is_enabled,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_ops hi655x_ldo_linear_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = hi655x_disable,
+ .is_enabled = hi655x_is_enabled,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+#define HI655X_LDO(_ID, vreg, vmask, ereg, dreg, \
+ sreg, cmask, vtable) { \
+ .rdesc = { \
+ .name = #_ID, \
+ .of_match = of_match_ptr(#_ID), \
+ .ops = &hi655x_regulator_ops, \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI655X_##_ID, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(vtable), \
+ .volt_table = vtable, \
+ .vsel_reg = HI655X_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI655X_BUS_ADDR(ereg), \
+ .enable_mask = BIT(cmask), \
+ }, \
+ .disable_reg = HI655X_BUS_ADDR(dreg), \
+ .status_reg = HI655X_BUS_ADDR(sreg), \
+ .ctrl_mask = cmask, \
+}
+
+#define HI655X_LDO_LINEAR(_ID, vreg, vmask, ereg, dreg, \
+ sreg, cmask, minv, nvolt, vstep) { \
+ .rdesc = { \
+ .name = #_ID, \
+ .of_match = of_match_ptr(#_ID), \
+ .ops = &hi655x_ldo_linear_ops, \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = HI655X_##_ID, \
+ .owner = THIS_MODULE, \
+ .min_uV = minv, \
+ .n_voltages = nvolt, \
+ .uV_step = vstep, \
+ .vsel_reg = HI655X_BUS_ADDR(vreg), \
+ .vsel_mask = vmask, \
+ .enable_reg = HI655X_BUS_ADDR(ereg), \
+ .enable_mask = BIT(cmask), \
+ }, \
+ .disable_reg = HI655X_BUS_ADDR(dreg), \
+ .status_reg = HI655X_BUS_ADDR(sreg), \
+ .ctrl_mask = cmask, \
+}
+
+static struct hi655x_regulator regulators[] = {
+ HI655X_LDO_LINEAR(LDO2, 0x72, 0x07, 0x29, 0x2a, 0x2b, 0x01,
+ 2500000, 8, 100000),
+ HI655X_LDO(LDO7, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x06, ldo7_voltages),
+ HI655X_LDO(LDO10, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x01, ldo7_voltages),
+ HI655X_LDO_LINEAR(LDO13, 0x7e, 0x07, 0x2c, 0x2d, 0x2e, 0x04,
+ 1600000, 8, 50000),
+ HI655X_LDO_LINEAR(LDO14, 0x7f, 0x07, 0x2c, 0x2d, 0x2e, 0x05,
+ 2500000, 8, 100000),
+ HI655X_LDO_LINEAR(LDO15, 0x80, 0x07, 0x2c, 0x2d, 0x2e, 0x06,
+ 1600000, 8, 50000),
+ HI655X_LDO_LINEAR(LDO17, 0x82, 0x07, 0x2f, 0x30, 0x31, 0x00,
+ 2500000, 8, 100000),
+ HI655X_LDO(LDO19, 0x84, 0x07, 0x2f, 0x30, 0x31, 0x02, ldo19_voltages),
+ HI655X_LDO_LINEAR(LDO21, 0x86, 0x07, 0x2f, 0x30, 0x31, 0x04,
+ 1650000, 8, 50000),
+ HI655X_LDO(LDO22, 0x87, 0x07, 0x2f, 0x30, 0x31, 0x05, ldo22_voltages),
+};
+
+static int hi655x_regulator_probe(struct platform_device *pdev)
+{
+ unsigned int i;
+ struct hi655x_regulator *regulator;
+ struct hi655x_pmic *pmic;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+
+ pmic = dev_get_drvdata(pdev->dev.parent);
+ if (!pmic) {
+ dev_err(&pdev->dev, "no pmic in the regulator parent node\n");
+ return -ENODEV;
+ }
+
+ regulator = devm_kzalloc(&pdev->dev, sizeof(*regulator), GFP_KERNEL);
+ if (!regulator)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, regulator);
+
+ config.dev = pdev->dev.parent;
+ config.regmap = pmic->regmap;
+ config.driver_data = regulator;
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i].rdesc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ regulator->rdesc.name);
+ return PTR_ERR(rdev);
+ }
+ }
+ return 0;
+}
+
+static const struct platform_device_id hi655x_regulator_table[] = {
+ { .name = "hi655x-regulator" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, hi655x_regulator_table);
+
+static struct platform_driver hi655x_regulator_driver = {
+ .id_table = hi655x_regulator_table,
+ .driver = {
+ .name = "hi655x-regulator",
+ },
+ .probe = hi655x_regulator_probe,
+};
+module_platform_driver(hi655x_regulator_driver);
+
+MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>");
+MODULE_DESCRIPTION("Hisilicon Hi655x regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h
new file mode 100644
index 000000000..943926a15
--- /dev/null
+++ b/drivers/regulator/internal.h
@@ -0,0 +1,116 @@
+/*
+ * internal.h -- Voltage/Current Regulator framework internal code
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008 SlimLogic Ltd.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * 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 __REGULATOR_INTERNAL_H
+#define __REGULATOR_INTERNAL_H
+
+#include <linux/suspend.h>
+
+#define REGULATOR_STATES_NUM (PM_SUSPEND_MAX + 1)
+
+struct regulator_voltage {
+ int min_uV;
+ int max_uV;
+};
+
+/*
+ * struct regulator
+ *
+ * One for each consumer device.
+ * @voltage - a voltage array for each state of runtime, i.e.:
+ * PM_SUSPEND_ON
+ * PM_SUSPEND_TO_IDLE
+ * PM_SUSPEND_STANDBY
+ * PM_SUSPEND_MEM
+ * PM_SUSPEND_MAX
+ */
+struct regulator {
+ struct device *dev;
+ struct list_head list;
+ unsigned int always_on:1;
+ unsigned int bypass:1;
+ int uA_load;
+ struct regulator_voltage voltage[REGULATOR_STATES_NUM];
+ const char *supply_name;
+ struct device_attribute dev_attr;
+ struct regulator_dev *rdev;
+ struct dentry *debugfs;
+};
+
+extern struct class regulator_class;
+
+static inline struct regulator_dev *dev_to_rdev(struct device *dev)
+{
+ return container_of(dev, struct regulator_dev, dev);
+}
+
+#ifdef CONFIG_OF
+struct regulator_dev *of_find_regulator_by_node(struct device_node *np);
+struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
+ const struct regulator_desc *desc,
+ struct regulator_config *config,
+ struct device_node **node);
+
+struct regulator_dev *of_parse_coupled_regulator(struct regulator_dev *rdev,
+ int index);
+
+int of_get_n_coupled(struct regulator_dev *rdev);
+
+bool of_check_coupling_data(struct regulator_dev *rdev);
+
+#else
+static inline struct regulator_dev *
+of_find_regulator_by_node(struct device_node *np)
+{
+ return NULL;
+}
+
+static inline struct regulator_init_data *
+regulator_of_get_init_data(struct device *dev,
+ const struct regulator_desc *desc,
+ struct regulator_config *config,
+ struct device_node **node)
+{
+ return NULL;
+}
+
+static inline struct regulator_dev *
+of_parse_coupled_regulator(struct regulator_dev *rdev,
+ int index)
+{
+ return NULL;
+}
+
+static inline int of_get_n_coupled(struct regulator_dev *rdev)
+{
+ return 0;
+}
+
+static inline bool of_check_coupling_data(struct regulator_dev *rdev)
+{
+ return false;
+}
+
+#endif
+enum regulator_get_type {
+ NORMAL_GET,
+ EXCLUSIVE_GET,
+ OPTIONAL_GET,
+ MAX_GET_TYPE
+};
+
+struct regulator *_regulator_get(struct device *dev, const char *id,
+ enum regulator_get_type get_type);
+#endif
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
new file mode 100644
index 000000000..4abd8e9c8
--- /dev/null
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -0,0 +1,179 @@
+/*
+ * isl6271a-regulator.c
+ *
+ * Support for Intersil ISL6271A voltage regulator
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#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 = dev_get_platdata(&i2c->dev);
+ struct isl_pmic *pmic;
+ int 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 = NULL;
+ config.driver_data = pmic;
+
+ pmic->rdev[i] = devm_regulator_register(&i2c->dev, &isl_rd[i],
+ &config);
+ if (IS_ERR(pmic->rdev[i])) {
+ dev_err(&i2c->dev, "failed to register %s\n", id->name);
+ return PTR_ERR(pmic->rdev[i]);
+ }
+ }
+
+ i2c_set_clientdata(i2c, pmic);
+
+ 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",
+ },
+ .probe = isl6271a_probe,
+ .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 <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/isl9305.c b/drivers/regulator/isl9305.c
new file mode 100644
index 000000000..257c1943e
--- /dev/null
+++ b/drivers/regulator/isl9305.c
@@ -0,0 +1,209 @@
+/*
+ * isl9305 - Intersil ISL9305 DCDC regulator
+ *
+ * Copyright 2014 Linaro Ltd
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ *
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/platform_data/isl9305.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/*
+ * Registers
+ */
+#define ISL9305_DCD1OUT 0x0
+#define ISL9305_DCD2OUT 0x1
+#define ISL9305_LDO1OUT 0x2
+#define ISL9305_LDO2OUT 0x3
+#define ISL9305_DCD_PARAMETER 0x4
+#define ISL9305_SYSTEM_PARAMETER 0x5
+#define ISL9305_DCD_SRCTL 0x6
+
+#define ISL9305_MAX_REG ISL9305_DCD_SRCTL
+
+/*
+ * DCD_PARAMETER
+ */
+#define ISL9305_DCD_PHASE 0x40
+#define ISL9305_DCD2_ULTRA 0x20
+#define ISL9305_DCD1_ULTRA 0x10
+#define ISL9305_DCD2_BLD 0x08
+#define ISL9305_DCD1_BLD 0x04
+#define ISL9305_DCD2_MODE 0x02
+#define ISL9305_DCD1_MODE 0x01
+
+/*
+ * SYSTEM_PARAMETER
+ */
+#define ISL9305_I2C_EN 0x40
+#define ISL9305_DCDPOR_MASK 0x30
+#define ISL9305_LDO2_EN 0x08
+#define ISL9305_LDO1_EN 0x04
+#define ISL9305_DCD2_EN 0x02
+#define ISL9305_DCD1_EN 0x01
+
+/*
+ * DCD_SRCTL
+ */
+#define ISL9305_DCD2SR_MASK 0xc0
+#define ISL9305_DCD1SR_MASK 0x07
+
+static const struct regulator_ops isl9305_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_desc isl9305_regulators[] = {
+ [ISL9305_DCD1] = {
+ .name = "DCD1",
+ .of_match = of_match_ptr("dcd1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .n_voltages = 0x70,
+ .min_uV = 825000,
+ .uV_step = 25000,
+ .vsel_reg = ISL9305_DCD1OUT,
+ .vsel_mask = 0x7f,
+ .enable_reg = ISL9305_SYSTEM_PARAMETER,
+ .enable_mask = ISL9305_DCD1_EN,
+ .supply_name = "VINDCD1",
+ .ops = &isl9305_ops,
+ },
+ [ISL9305_DCD2] = {
+ .name = "DCD2",
+ .of_match = of_match_ptr("dcd2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .n_voltages = 0x70,
+ .min_uV = 825000,
+ .uV_step = 25000,
+ .vsel_reg = ISL9305_DCD2OUT,
+ .vsel_mask = 0x7f,
+ .enable_reg = ISL9305_SYSTEM_PARAMETER,
+ .enable_mask = ISL9305_DCD2_EN,
+ .supply_name = "VINDCD2",
+ .ops = &isl9305_ops,
+ },
+ [ISL9305_LDO1] = {
+ .name = "LDO1",
+ .of_match = of_match_ptr("ldo1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .n_voltages = 0x37,
+ .min_uV = 900000,
+ .uV_step = 50000,
+ .vsel_reg = ISL9305_LDO1OUT,
+ .vsel_mask = 0x3f,
+ .enable_reg = ISL9305_SYSTEM_PARAMETER,
+ .enable_mask = ISL9305_LDO1_EN,
+ .supply_name = "VINLDO1",
+ .ops = &isl9305_ops,
+ },
+ [ISL9305_LDO2] = {
+ .name = "LDO2",
+ .of_match = of_match_ptr("ldo2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .n_voltages = 0x37,
+ .min_uV = 900000,
+ .uV_step = 50000,
+ .vsel_reg = ISL9305_LDO2OUT,
+ .vsel_mask = 0x3f,
+ .enable_reg = ISL9305_SYSTEM_PARAMETER,
+ .enable_mask = ISL9305_LDO2_EN,
+ .supply_name = "VINLDO2",
+ .ops = &isl9305_ops,
+ },
+};
+
+static const struct regmap_config isl9305_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ISL9305_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int isl9305_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_config config = { };
+ struct isl9305_pdata *pdata = i2c->dev.platform_data;
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ int i, ret;
+
+ regmap = devm_regmap_init_i2c(i2c, &isl9305_regmap);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ config.dev = &i2c->dev;
+
+ for (i = 0; i < ARRAY_SIZE(isl9305_regulators); i++) {
+ if (pdata)
+ config.init_data = pdata->init_data[i];
+ else
+ config.init_data = NULL;
+
+ rdev = devm_regulator_register(&i2c->dev,
+ &isl9305_regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&i2c->dev, "Failed to register %s: %d\n",
+ isl9305_regulators[i].name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id isl9305_dt_ids[] = {
+ { .compatible = "isl,isl9305" }, /* for backward compat., don't use */
+ { .compatible = "isil,isl9305" },
+ { .compatible = "isl,isl9305h" }, /* for backward compat., don't use */
+ { .compatible = "isil,isl9305h" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, isl9305_dt_ids);
+#endif
+
+static const struct i2c_device_id isl9305_i2c_id[] = {
+ { "isl9305", },
+ { "isl9305h", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl9305_i2c_id);
+
+static struct i2c_driver isl9305_regulator_driver = {
+ .driver = {
+ .name = "isl9305",
+ .of_match_table = of_match_ptr(isl9305_dt_ids),
+ },
+ .probe = isl9305_i2c_probe,
+ .id_table = isl9305_i2c_id,
+};
+
+module_i2c_driver(isl9305_regulator_driver);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("Intersil ISL9305 DCDC regulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c
new file mode 100644
index 000000000..27c0a67cf
--- /dev/null
+++ b/drivers/regulator/lm363x-regulator.c
@@ -0,0 +1,291 @@
+/*
+ * TI LM363X Regulator Driver
+ *
+ * Copyright 2015 Texas Instruments
+ *
+ * Author: Milo Kim <milo.kim@ti.com>
+ *
+ * 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 <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mfd/ti-lmu.h>
+#include <linux/mfd/ti-lmu-register.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/* LM3631 */
+#define LM3631_BOOST_VSEL_MAX 0x25
+#define LM3631_LDO_VSEL_MAX 0x28
+#define LM3631_CONT_VSEL_MAX 0x03
+#define LM3631_VBOOST_MIN 4500000
+#define LM3631_VCONT_MIN 1800000
+#define LM3631_VLDO_MIN 4000000
+#define ENABLE_TIME_USEC 1000
+
+/* LM3632 */
+#define LM3632_BOOST_VSEL_MAX 0x26
+#define LM3632_LDO_VSEL_MAX 0x28
+#define LM3632_VBOOST_MIN 4500000
+#define LM3632_VLDO_MIN 4000000
+
+/* Common */
+#define LM363X_STEP_50mV 50000
+#define LM363X_STEP_500mV 500000
+
+static const int ldo_cont_enable_time[] = {
+ 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000,
+};
+
+static int lm363x_regulator_enable_time(struct regulator_dev *rdev)
+{
+ enum lm363x_regulator_id id = rdev_get_id(rdev);
+ u8 val, addr, mask;
+
+ switch (id) {
+ case LM3631_LDO_CONT:
+ addr = LM3631_REG_ENTIME_VCONT;
+ mask = LM3631_ENTIME_CONT_MASK;
+ break;
+ case LM3631_LDO_OREF:
+ addr = LM3631_REG_ENTIME_VOREF;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ case LM3631_LDO_POS:
+ addr = LM3631_REG_ENTIME_VPOS;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ case LM3631_LDO_NEG:
+ addr = LM3631_REG_ENTIME_VNEG;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ default:
+ return 0;
+ }
+
+ if (regmap_read(rdev->regmap, addr, (unsigned int *)&val))
+ return -EINVAL;
+
+ val = (val & mask) >> LM3631_ENTIME_SHIFT;
+
+ if (id == LM3631_LDO_CONT)
+ return ldo_cont_enable_time[val];
+ else
+ return ENABLE_TIME_USEC * val;
+}
+
+static struct regulator_ops lm363x_boost_voltage_table_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 lm363x_regulator_voltage_table_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 = lm363x_regulator_enable_time,
+};
+
+static const struct regulator_desc lm363x_regulator_desc[] = {
+ /* LM3631 */
+ {
+ .name = "vboost",
+ .of_match = "vboost",
+ .id = LM3631_BOOST,
+ .ops = &lm363x_boost_voltage_table_ops,
+ .n_voltages = LM3631_BOOST_VSEL_MAX + 1,
+ .min_uV = LM3631_VBOOST_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_BOOST,
+ .vsel_mask = LM3631_VOUT_MASK,
+ },
+ {
+ .name = "ldo_cont",
+ .of_match = "vcont",
+ .id = LM3631_LDO_CONT,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_CONT_VSEL_MAX + 1,
+ .min_uV = LM3631_VCONT_MIN,
+ .uV_step = LM363X_STEP_500mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_CONT,
+ .vsel_mask = LM3631_VOUT_CONT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL2,
+ .enable_mask = LM3631_EN_CONT_MASK,
+ },
+ {
+ .name = "ldo_oref",
+ .of_match = "voref",
+ .id = LM3631_LDO_OREF,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_LDO_VSEL_MAX + 1,
+ .min_uV = LM3631_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_OREF,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_OREF_MASK,
+ },
+ {
+ .name = "ldo_vpos",
+ .of_match = "vpos",
+ .id = LM3631_LDO_POS,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_LDO_VSEL_MAX + 1,
+ .min_uV = LM3631_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_POS,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_VPOS_MASK,
+ },
+ {
+ .name = "ldo_vneg",
+ .of_match = "vneg",
+ .id = LM3631_LDO_NEG,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_LDO_VSEL_MAX + 1,
+ .min_uV = LM3631_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_NEG,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_VNEG_MASK,
+ },
+ /* LM3632 */
+ {
+ .name = "vboost",
+ .of_match = "vboost",
+ .id = LM3632_BOOST,
+ .ops = &lm363x_boost_voltage_table_ops,
+ .n_voltages = LM3632_BOOST_VSEL_MAX + 1,
+ .min_uV = LM3632_VBOOST_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3632_REG_VOUT_BOOST,
+ .vsel_mask = LM3632_VOUT_MASK,
+ },
+ {
+ .name = "ldo_vpos",
+ .of_match = "vpos",
+ .id = LM3632_LDO_POS,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3632_LDO_VSEL_MAX + 1,
+ .min_uV = LM3632_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3632_REG_VOUT_POS,
+ .vsel_mask = LM3632_VOUT_MASK,
+ .enable_reg = LM3632_REG_BIAS_CONFIG,
+ .enable_mask = LM3632_EN_VPOS_MASK,
+ },
+ {
+ .name = "ldo_vneg",
+ .of_match = "vneg",
+ .id = LM3632_LDO_NEG,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3632_LDO_VSEL_MAX + 1,
+ .min_uV = LM3632_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3632_REG_VOUT_NEG,
+ .vsel_mask = LM3632_VOUT_MASK,
+ .enable_reg = LM3632_REG_BIAS_CONFIG,
+ .enable_mask = LM3632_EN_VNEG_MASK,
+ },
+};
+
+static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev, int id)
+{
+ /*
+ * Check LCM_EN1/2_GPIO is configured.
+ * Those pins are used for enabling VPOS/VNEG LDOs.
+ */
+ switch (id) {
+ case LM3632_LDO_POS:
+ return devm_gpiod_get_index_optional(dev, "enable", 0, GPIOD_OUT_LOW);
+ case LM3632_LDO_NEG:
+ return devm_gpiod_get_index_optional(dev, "enable", 1, GPIOD_OUT_LOW);
+ default:
+ return NULL;
+ }
+}
+
+static int lm363x_regulator_probe(struct platform_device *pdev)
+{
+ struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent);
+ struct regmap *regmap = lmu->regmap;
+ struct regulator_config cfg = { };
+ struct regulator_dev *rdev;
+ struct device *dev = &pdev->dev;
+ int id = pdev->id;
+ struct gpio_desc *gpiod;
+ int ret;
+
+ cfg.dev = dev;
+ cfg.regmap = regmap;
+
+ /*
+ * LM3632 LDOs can be controlled by external pin.
+ * Register update is required if the pin is used.
+ */
+ gpiod = lm363x_regulator_of_get_enable_gpio(dev, id);
+ if (gpiod) {
+ cfg.ena_gpiod = gpiod;
+
+ ret = regmap_update_bits(regmap, LM3632_REG_BIAS_CONFIG,
+ LM3632_EXT_EN_MASK,
+ LM3632_EXT_EN_MASK);
+ if (ret) {
+ dev_err(dev, "External pin err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ rdev = devm_regulator_register(dev, &lm363x_regulator_desc[id], &cfg);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "[%d] regulator register err: %d\n", id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver lm363x_regulator_driver = {
+ .probe = lm363x_regulator_probe,
+ .driver = {
+ .name = "lm363x-regulator",
+ },
+};
+
+module_platform_driver(lm363x_regulator_driver);
+
+MODULE_DESCRIPTION("TI LM363X Regulator Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lm363x-regulator");
diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c
new file mode 100644
index 000000000..204b5c527
--- /dev/null
+++ b/drivers/regulator/lp3971.c
@@ -0,0 +1,464 @@
+/*
+ * Regulator driver for National Semiconductors LP3971 PMIC chip
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * 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 <linux/bug.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/lp3971.h>
+#include <linux/slab.h>
+
+struct lp3971 {
+ struct device *dev;
+ struct mutex io_lock;
+ struct i2c_client *i2c;
+};
+
+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 ret;
+
+ *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);
+ if (ret == 0) {
+ tmp = (tmp & ~mask) | val;
+ 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;
+
+ /* Instantiate the regulators */
+ for (i = 0; i < pdata->num_regulators; i++) {
+ struct regulator_config config = { };
+ struct lp3971_regulator_subdev *reg = &pdata->regulators[i];
+ struct regulator_dev *rdev;
+
+ config.dev = lp3971->dev;
+ config.init_data = reg->initdata;
+ config.driver_data = lp3971;
+
+ rdev = devm_regulator_register(lp3971->dev,
+ &regulators[reg->id], &config);
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ dev_err(lp3971->dev, "regulator init failed: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int lp3971_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct lp3971 *lp3971;
+ struct lp3971_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ int ret;
+ u16 val;
+
+ if (!pdata) {
+ dev_dbg(&i2c->dev, "No platform init data supplied\n");
+ return -ENODEV;
+ }
+
+ lp3971 = devm_kzalloc(&i2c->dev, 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");
+ return ret;
+ }
+
+ ret = setup_regulators(lp3971, pdata);
+ if (ret < 0)
+ return ret;
+
+ i2c_set_clientdata(i2c, 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",
+ },
+ .probe = lp3971_i2c_probe,
+ .id_table = lp3971_i2c_id,
+};
+
+module_i2c_driver(lp3971_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Szyprowski <m.szyprowski@samsung.com>");
+MODULE_DESCRIPTION("LP3971 PMIC driver");
diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c
new file mode 100644
index 000000000..ff0c275f9
--- /dev/null
+++ b/drivers/regulator/lp3972.c
@@ -0,0 +1,572 @@
+/*
+ * 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 <linux/bug.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/lp3972.h>
+#include <linux/slab.h>
+
+struct lp3972 {
+ struct device *dev;
+ struct mutex io_lock;
+ struct i2c_client *i2c;
+};
+
+/* 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);
+ if (ret == 0) {
+ tmp = (tmp & ~mask) | val;
+ 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;
+
+ /* Instantiate the regulators */
+ for (i = 0; i < pdata->num_regulators; i++) {
+ struct lp3972_regulator_subdev *reg = &pdata->regulators[i];
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+
+ config.dev = lp3972->dev;
+ config.init_data = reg->initdata;
+ config.driver_data = lp3972;
+
+ rdev = devm_regulator_register(lp3972->dev,
+ &regulators[reg->id], &config);
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ dev_err(lp3972->dev, "regulator init failed: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int lp3972_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct lp3972 *lp3972;
+ struct lp3972_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ int ret;
+ u16 val;
+
+ if (!pdata) {
+ dev_dbg(&i2c->dev, "No platform init data supplied\n");
+ return -ENODEV;
+ }
+
+ lp3972 = devm_kzalloc(&i2c->dev, 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);
+ return ret;
+ }
+
+ ret = setup_regulators(lp3972, pdata);
+ if (ret < 0)
+ return ret;
+
+ i2c_set_clientdata(i2c, 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",
+ },
+ .probe = lp3972_i2c_probe,
+ .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 <axel.lin@gmail.com>");
+MODULE_DESCRIPTION("LP3972 PMIC driver");
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c
new file mode 100644
index 000000000..38992112f
--- /dev/null
+++ b/drivers/regulator/lp872x.c
@@ -0,0 +1,1019 @@
+/*
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/lp872x.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/of_regulator.h>
+
+/* 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;
+ int num_regulators;
+ enum lp872x_dvs_state dvs_pin;
+};
+
+/* 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 ret;
+
+ 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 time_step_us;
+
+ 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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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",
+ .of_match = of_match_ptr("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))
+ goto set_default_dvs_mode;
+
+ 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;
+
+ 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_hw_enable(struct lp872x *lp)
+{
+ int ret, gpio;
+
+ if (!lp->pdata)
+ return -EINVAL;
+
+ gpio = lp->pdata->enable_gpio;
+ if (!gpio_is_valid(gpio))
+ return 0;
+
+ /* Always set enable GPIO high. */
+ ret = devm_gpio_request_one(lp->dev, gpio, GPIOF_OUT_INIT_HIGH, "LP872X EN");
+ if (ret) {
+ dev_err(lp->dev, "gpio request err: %d\n", ret);
+ return ret;
+ }
+
+ /* Each chip has a different enable delay. */
+ if (lp->chipid == LP8720)
+ usleep_range(LP8720_ENABLE_DELAY, 1.5 * LP8720_ENABLE_DELAY);
+ else
+ usleep_range(LP8725_ENABLE_DELAY, 1.5 * LP8725_ENABLE_DELAY);
+
+ return 0;
+}
+
+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;
+
+ 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 = devm_regulator_register(lp->dev, desc, &cfg);
+ if (IS_ERR(rdev)) {
+ dev_err(lp->dev, "regulator register err");
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct regmap_config lp872x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX_REGISTERS,
+};
+
+#ifdef CONFIG_OF
+
+#define LP872X_VALID_OPMODE (REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL)
+
+static struct of_regulator_match lp8720_matches[] = {
+ { .name = "ldo1", .driver_data = (void *)LP8720_ID_LDO1, },
+ { .name = "ldo2", .driver_data = (void *)LP8720_ID_LDO2, },
+ { .name = "ldo3", .driver_data = (void *)LP8720_ID_LDO3, },
+ { .name = "ldo4", .driver_data = (void *)LP8720_ID_LDO4, },
+ { .name = "ldo5", .driver_data = (void *)LP8720_ID_LDO5, },
+ { .name = "buck", .driver_data = (void *)LP8720_ID_BUCK, },
+};
+
+static struct of_regulator_match lp8725_matches[] = {
+ { .name = "ldo1", .driver_data = (void *)LP8725_ID_LDO1, },
+ { .name = "ldo2", .driver_data = (void *)LP8725_ID_LDO2, },
+ { .name = "ldo3", .driver_data = (void *)LP8725_ID_LDO3, },
+ { .name = "ldo4", .driver_data = (void *)LP8725_ID_LDO4, },
+ { .name = "ldo5", .driver_data = (void *)LP8725_ID_LDO5, },
+ { .name = "lilo1", .driver_data = (void *)LP8725_ID_LILO1, },
+ { .name = "lilo2", .driver_data = (void *)LP8725_ID_LILO2, },
+ { .name = "buck1", .driver_data = (void *)LP8725_ID_BUCK1, },
+ { .name = "buck2", .driver_data = (void *)LP8725_ID_BUCK2, },
+};
+
+static struct lp872x_platform_data
+*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which)
+{
+ struct device_node *np = dev->of_node;
+ struct lp872x_platform_data *pdata;
+ struct of_regulator_match *match;
+ int num_matches;
+ int count;
+ int i;
+ u8 dvs_state;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_property_read_u8(np, "ti,general-config", &pdata->general_config);
+ if (of_find_property(np, "ti,update-config", NULL))
+ pdata->update_config = true;
+
+ pdata->dvs = devm_kzalloc(dev, sizeof(struct lp872x_dvs), GFP_KERNEL);
+ if (!pdata->dvs)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->dvs->gpio = of_get_named_gpio(np, "ti,dvs-gpio", 0);
+ of_property_read_u8(np, "ti,dvs-vsel", (u8 *)&pdata->dvs->vsel);
+ of_property_read_u8(np, "ti,dvs-state", &dvs_state);
+ pdata->dvs->init_state = dvs_state ? DVS_HIGH : DVS_LOW;
+
+ pdata->enable_gpio = of_get_named_gpio(np, "enable-gpios", 0);
+
+ if (of_get_child_count(np) == 0)
+ goto out;
+
+ switch (which) {
+ case LP8720:
+ match = lp8720_matches;
+ num_matches = ARRAY_SIZE(lp8720_matches);
+ break;
+ case LP8725:
+ match = lp8725_matches;
+ num_matches = ARRAY_SIZE(lp8725_matches);
+ break;
+ default:
+ goto out;
+ }
+
+ count = of_regulator_match(dev, np, match, num_matches);
+ if (count <= 0)
+ goto out;
+
+ for (i = 0; i < num_matches; i++) {
+ pdata->regulator_data[i].id =
+ (enum lp872x_regulator_id)match[i].driver_data;
+ pdata->regulator_data[i].init_data = match[i].init_data;
+ }
+out:
+ return pdata;
+}
+#else
+static struct lp872x_platform_data
+*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which)
+{
+ return NULL;
+}
+#endif
+
+static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+ struct lp872x *lp;
+ struct lp872x_platform_data *pdata;
+ int ret;
+ const int lp872x_num_regulators[] = {
+ [LP8720] = LP8720_NUM_REGULATORS,
+ [LP8725] = LP8725_NUM_REGULATORS,
+ };
+
+ if (cl->dev.of_node) {
+ pdata = lp872x_populate_pdata_from_dt(&cl->dev,
+ (enum lp872x_id)id->driver_data);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ } else {
+ pdata = dev_get_platdata(&cl->dev);
+ }
+
+ lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL);
+ if (!lp)
+ return -ENOMEM;
+
+ lp->num_regulators = lp872x_num_regulators[id->driver_data];
+
+ 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);
+ return ret;
+ }
+
+ lp->dev = &cl->dev;
+ lp->pdata = pdata;
+ lp->chipid = id->driver_data;
+ i2c_set_clientdata(cl, lp);
+
+ ret = lp872x_hw_enable(lp);
+ if (ret)
+ return ret;
+
+ ret = lp872x_config(lp);
+ if (ret)
+ return ret;
+
+ return lp872x_regulator_register(lp);
+}
+
+static const struct of_device_id lp872x_dt_ids[] = {
+ { .compatible = "ti,lp8720", },
+ { .compatible = "ti,lp8725", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lp872x_dt_ids);
+
+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",
+ .of_match_table = of_match_ptr(lp872x_dt_ids),
+ },
+ .probe = lp872x_probe,
+ .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/lp873x-regulator.c b/drivers/regulator/lp873x-regulator.c
new file mode 100644
index 000000000..70e3df653
--- /dev/null
+++ b/drivers/regulator/lp873x-regulator.c
@@ -0,0 +1,240 @@
+/*
+ * Regulator driver for LP873X PMIC
+ *
+ * Copyright (C) 2016 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lp873x.h>
+
+#define LP873X_REGULATOR(_name, _id, _of, _ops, _n, _vr, _vm, _er, _em, \
+ _delay, _lr, _cr) \
+ [_id] = { \
+ .desc = { \
+ .name = _name, \
+ .supply_name = _of "-in", \
+ .id = _id, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = of_match_ptr("regulators"),\
+ .ops = &_ops, \
+ .n_voltages = _n, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ .ramp_delay = _delay, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = ARRAY_SIZE(_lr), \
+ }, \
+ .ctrl2_reg = _cr, \
+ }
+
+struct lp873x_regulator {
+ struct regulator_desc desc;
+ unsigned int ctrl2_reg;
+};
+
+static const struct lp873x_regulator regulators[];
+
+static const struct regulator_linear_range buck0_buck1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x13, 0),
+ REGULATOR_LINEAR_RANGE(700000, 0x14, 0x17, 10000),
+ REGULATOR_LINEAR_RANGE(735000, 0x18, 0x9d, 5000),
+ REGULATOR_LINEAR_RANGE(1420000, 0x9e, 0xff, 20000),
+};
+
+static const struct regulator_linear_range ldo0_ldo1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0x0, 0x19, 100000),
+};
+
+static unsigned int lp873x_buck_ramp_delay[] = {
+ 30000, 15000, 10000, 7500, 3800, 1900, 940, 470
+};
+
+/* LP873X BUCK current limit */
+static const unsigned int lp873x_buck_uA[] = {
+ 1500000, 2000000, 2500000, 3000000, 3500000, 4000000,
+};
+
+static int lp873x_buck_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ int id = rdev_get_id(rdev);
+ struct lp873x *lp873 = rdev_get_drvdata(rdev);
+ unsigned int reg;
+ int ret;
+
+ if (ramp_delay <= 470)
+ reg = 7;
+ else if (ramp_delay <= 940)
+ reg = 6;
+ else if (ramp_delay <= 1900)
+ reg = 5;
+ else if (ramp_delay <= 3800)
+ reg = 4;
+ else if (ramp_delay <= 7500)
+ reg = 3;
+ else if (ramp_delay <= 10000)
+ reg = 2;
+ else if (ramp_delay <= 15000)
+ reg = 1;
+ else
+ reg = 0;
+
+ ret = regmap_update_bits(lp873->regmap, regulators[id].ctrl2_reg,
+ LP873X_BUCK0_CTRL_2_BUCK0_SLEW_RATE,
+ reg << __ffs(LP873X_BUCK0_CTRL_2_BUCK0_SLEW_RATE));
+ if (ret) {
+ dev_err(lp873->dev, "SLEW RATE write failed: %d\n", ret);
+ return ret;
+ }
+
+ rdev->constraints->ramp_delay = lp873x_buck_ramp_delay[reg];
+
+ return 0;
+}
+
+static int lp873x_buck_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ int id = rdev_get_id(rdev);
+ struct lp873x *lp873 = rdev_get_drvdata(rdev);
+ int i;
+
+ for (i = ARRAY_SIZE(lp873x_buck_uA) - 1; i >= 0; i--) {
+ if (lp873x_buck_uA[i] >= min_uA &&
+ lp873x_buck_uA[i] <= max_uA)
+ return regmap_update_bits(lp873->regmap,
+ regulators[id].ctrl2_reg,
+ LP873X_BUCK0_CTRL_2_BUCK0_ILIM,
+ i << __ffs(LP873X_BUCK0_CTRL_2_BUCK0_ILIM));
+ }
+
+ return -EINVAL;
+}
+
+static int lp873x_buck_get_current_limit(struct regulator_dev *rdev)
+{
+ int id = rdev_get_id(rdev);
+ struct lp873x *lp873 = rdev_get_drvdata(rdev);
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(lp873->regmap, regulators[id].ctrl2_reg, &val);
+ if (ret)
+ return ret;
+
+ val = (val & LP873X_BUCK0_CTRL_2_BUCK0_ILIM) >>
+ __ffs(LP873X_BUCK0_CTRL_2_BUCK0_ILIM);
+
+ return (val < ARRAY_SIZE(lp873x_buck_uA)) ?
+ lp873x_buck_uA[val] : -EINVAL;
+}
+
+/* Operations permitted on BUCK0, BUCK1 */
+static struct regulator_ops lp873x_buck01_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_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = lp873x_buck_set_ramp_delay,
+ .set_current_limit = lp873x_buck_set_current_limit,
+ .get_current_limit = lp873x_buck_get_current_limit,
+};
+
+/* Operations permitted on LDO0 and LDO1 */
+static struct regulator_ops lp873x_ldo01_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_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static const struct lp873x_regulator regulators[] = {
+ LP873X_REGULATOR("BUCK0", LP873X_BUCK_0, "buck0", lp873x_buck01_ops,
+ 256, LP873X_REG_BUCK0_VOUT,
+ LP873X_BUCK0_VOUT_BUCK0_VSET, LP873X_REG_BUCK0_CTRL_1,
+ LP873X_BUCK0_CTRL_1_BUCK0_EN, 10000,
+ buck0_buck1_ranges, LP873X_REG_BUCK0_CTRL_2),
+ LP873X_REGULATOR("BUCK1", LP873X_BUCK_1, "buck1", lp873x_buck01_ops,
+ 256, LP873X_REG_BUCK1_VOUT,
+ LP873X_BUCK1_VOUT_BUCK1_VSET, LP873X_REG_BUCK1_CTRL_1,
+ LP873X_BUCK1_CTRL_1_BUCK1_EN, 10000,
+ buck0_buck1_ranges, LP873X_REG_BUCK1_CTRL_2),
+ LP873X_REGULATOR("LDO0", LP873X_LDO_0, "ldo0", lp873x_ldo01_ops, 26,
+ LP873X_REG_LDO0_VOUT, LP873X_LDO0_VOUT_LDO0_VSET,
+ LP873X_REG_LDO0_CTRL,
+ LP873X_LDO0_CTRL_LDO0_EN, 0, ldo0_ldo1_ranges, 0xFF),
+ LP873X_REGULATOR("LDO1", LP873X_LDO_1, "ldo1", lp873x_ldo01_ops, 26,
+ LP873X_REG_LDO1_VOUT, LP873X_LDO1_VOUT_LDO1_VSET,
+ LP873X_REG_LDO1_CTRL,
+ LP873X_LDO1_CTRL_LDO1_EN, 0, ldo0_ldo1_ranges, 0xFF),
+};
+
+static int lp873x_regulator_probe(struct platform_device *pdev)
+{
+ struct lp873x *lp873 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i;
+
+ platform_set_drvdata(pdev, lp873);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = lp873->dev->of_node;
+ config.driver_data = lp873;
+ config.regmap = lp873->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i].desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(lp873->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id lp873x_regulator_id_table[] = {
+ { "lp873x-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, lp873x_regulator_id_table);
+
+static struct platform_driver lp873x_regulator_driver = {
+ .driver = {
+ .name = "lp873x-pmic",
+ },
+ .probe = lp873x_regulator_probe,
+ .id_table = lp873x_regulator_id_table,
+};
+module_platform_driver(lp873x_regulator_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("LP873X voltage regulator driver");
+MODULE_ALIAS("platform:lp873x-pmic");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c
new file mode 100644
index 000000000..244822bb6
--- /dev/null
+++ b/drivers/regulator/lp8755.c
@@ -0,0 +1,548 @@
+/*
+ * LP8755 High Performance Power Management Unit : System Interface Driver
+ * (based on rev. 0.26)
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Daniel(Geon Si) Jeong <daniel.jeong@ti.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/platform_data/lp8755.h>
+
+#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, &regval);
+ if (ret < 0) {
+ dev_err(pchip->dev, "i2c access 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 access 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, &regval);
+ 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, &regval);
+ 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 access 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 access error %s\n", __func__);
+ return ret;
+}
+
+static const struct regulator_ops lp8755_buck_ops = {
+ .map_voltage = regulator_map_voltage_linear,
+ .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, &regval);
+ 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 access 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] =
+ devm_regulator_register(pchip->dev,
+ &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);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+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 access 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, &regval);
+ if (ret < 0) {
+ dev_err(pchip->dev, "i2c access error %s\n", __func__);
+ return ret;
+ }
+
+ pchip->irqmask = regval;
+ return devm_request_threaded_irq(pchip->dev, pchip->irq, NULL,
+ lp8755_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "lp8755-irq", pchip);
+}
+
+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 = dev_get_platdata(&client->dev);
+
+ 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;
+ }
+
+ pchip->irq = client->irq;
+ ret = lp8755_int_config(pchip);
+ if (ret < 0) {
+ dev_err(&client->dev, "fail to irq config\n");
+ goto err;
+ }
+
+ return ret;
+
+err:
+ /* 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 < LP8755_BUCK_MAX; icnt++)
+ lp8755_write(pchip, icnt, 0x00);
+
+ 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 <daniel.jeong@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/lp87565-regulator.c b/drivers/regulator/lp87565-regulator.c
new file mode 100644
index 000000000..7cd686240
--- /dev/null
+++ b/drivers/regulator/lp87565-regulator.c
@@ -0,0 +1,241 @@
+/*
+ * Regulator driver for LP87565 PMIC
+ *
+ * Copyright (C) 2017 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lp87565.h>
+
+#define LP87565_REGULATOR(_name, _id, _of, _ops, _n, _vr, _vm, _er, _em, \
+ _delay, _lr, _cr) \
+ [_id] = { \
+ .desc = { \
+ .name = _name, \
+ .supply_name = _of "-in", \
+ .id = _id, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = of_match_ptr("regulators"),\
+ .ops = &_ops, \
+ .n_voltages = _n, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ .ramp_delay = _delay, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = ARRAY_SIZE(_lr), \
+ }, \
+ .ctrl2_reg = _cr, \
+ }
+
+struct lp87565_regulator {
+ struct regulator_desc desc;
+ unsigned int ctrl2_reg;
+};
+
+static const struct lp87565_regulator regulators[];
+
+static const struct regulator_linear_range buck0_1_2_3_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0xA, 0x17, 10000),
+ REGULATOR_LINEAR_RANGE(735000, 0x18, 0x9d, 5000),
+ REGULATOR_LINEAR_RANGE(1420000, 0x9e, 0xff, 20000),
+};
+
+static unsigned int lp87565_buck_ramp_delay[] = {
+ 30000, 15000, 10000, 7500, 3800, 1900, 940, 470
+};
+
+/* LP87565 BUCK current limit */
+static const unsigned int lp87565_buck_uA[] = {
+ 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, 4500000, 5000000,
+};
+
+static int lp87565_buck_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ int id = rdev_get_id(rdev);
+ struct lp87565 *lp87565 = rdev_get_drvdata(rdev);
+ unsigned int reg;
+ int ret;
+
+ if (ramp_delay <= 470)
+ reg = 7;
+ else if (ramp_delay <= 940)
+ reg = 6;
+ else if (ramp_delay <= 1900)
+ reg = 5;
+ else if (ramp_delay <= 3800)
+ reg = 4;
+ else if (ramp_delay <= 7500)
+ reg = 3;
+ else if (ramp_delay <= 10000)
+ reg = 2;
+ else if (ramp_delay <= 15000)
+ reg = 1;
+ else
+ reg = 0;
+
+ ret = regmap_update_bits(lp87565->regmap, regulators[id].ctrl2_reg,
+ LP87565_BUCK_CTRL_2_SLEW_RATE,
+ reg << __ffs(LP87565_BUCK_CTRL_2_SLEW_RATE));
+ if (ret) {
+ dev_err(lp87565->dev, "SLEW RATE write failed: %d\n", ret);
+ return ret;
+ }
+
+ rdev->constraints->ramp_delay = lp87565_buck_ramp_delay[reg];
+
+ /* Conservatively give a 15% margin */
+ rdev->constraints->ramp_delay =
+ rdev->constraints->ramp_delay * 85 / 100;
+
+ return 0;
+}
+
+static int lp87565_buck_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ int id = rdev_get_id(rdev);
+ struct lp87565 *lp87565 = rdev_get_drvdata(rdev);
+ int i;
+
+ for (i = ARRAY_SIZE(lp87565_buck_uA) - 1; i >= 0; i--) {
+ if (lp87565_buck_uA[i] >= min_uA &&
+ lp87565_buck_uA[i] <= max_uA)
+ return regmap_update_bits(lp87565->regmap,
+ regulators[id].ctrl2_reg,
+ LP87565_BUCK_CTRL_2_ILIM,
+ i << __ffs(LP87565_BUCK_CTRL_2_ILIM));
+ }
+
+ return -EINVAL;
+}
+
+static int lp87565_buck_get_current_limit(struct regulator_dev *rdev)
+{
+ int id = rdev_get_id(rdev);
+ struct lp87565 *lp87565 = rdev_get_drvdata(rdev);
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(lp87565->regmap, regulators[id].ctrl2_reg, &val);
+ if (ret)
+ return ret;
+
+ val = (val & LP87565_BUCK_CTRL_2_ILIM) >>
+ __ffs(LP87565_BUCK_CTRL_2_ILIM);
+
+ return (val < ARRAY_SIZE(lp87565_buck_uA)) ?
+ lp87565_buck_uA[val] : -EINVAL;
+}
+
+/* Operations permitted on BUCK0, BUCK1 */
+static struct regulator_ops lp87565_buck_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_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = lp87565_buck_set_ramp_delay,
+ .set_current_limit = lp87565_buck_set_current_limit,
+ .get_current_limit = lp87565_buck_get_current_limit,
+};
+
+static const struct lp87565_regulator regulators[] = {
+ LP87565_REGULATOR("BUCK0", LP87565_BUCK_0, "buck0", lp87565_buck_ops,
+ 256, LP87565_REG_BUCK0_VOUT, LP87565_BUCK_VSET,
+ LP87565_REG_BUCK0_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK0_CTRL_2),
+ LP87565_REGULATOR("BUCK1", LP87565_BUCK_1, "buck1", lp87565_buck_ops,
+ 256, LP87565_REG_BUCK1_VOUT, LP87565_BUCK_VSET,
+ LP87565_REG_BUCK1_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK1_CTRL_2),
+ LP87565_REGULATOR("BUCK2", LP87565_BUCK_2, "buck2", lp87565_buck_ops,
+ 256, LP87565_REG_BUCK2_VOUT, LP87565_BUCK_VSET,
+ LP87565_REG_BUCK2_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK2_CTRL_2),
+ LP87565_REGULATOR("BUCK3", LP87565_BUCK_3, "buck3", lp87565_buck_ops,
+ 256, LP87565_REG_BUCK3_VOUT, LP87565_BUCK_VSET,
+ LP87565_REG_BUCK3_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK3_CTRL_2),
+ LP87565_REGULATOR("BUCK10", LP87565_BUCK_10, "buck10", lp87565_buck_ops,
+ 256, LP87565_REG_BUCK0_VOUT, LP87565_BUCK_VSET,
+ LP87565_REG_BUCK0_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN |
+ LP87565_BUCK_CTRL_1_FPWM_MP_0_2, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK0_CTRL_2),
+ LP87565_REGULATOR("BUCK23", LP87565_BUCK_23, "buck23", lp87565_buck_ops,
+ 256, LP87565_REG_BUCK2_VOUT, LP87565_BUCK_VSET,
+ LP87565_REG_BUCK2_CTRL_1,
+ LP87565_BUCK_CTRL_1_EN, 3230,
+ buck0_1_2_3_ranges, LP87565_REG_BUCK2_CTRL_2),
+};
+
+static int lp87565_regulator_probe(struct platform_device *pdev)
+{
+ struct lp87565 *lp87565 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i, min_idx = LP87565_BUCK_0, max_idx = LP87565_BUCK_3;
+
+ platform_set_drvdata(pdev, lp87565);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = lp87565->dev->of_node;
+ config.driver_data = lp87565;
+ config.regmap = lp87565->regmap;
+
+ if (lp87565->dev_type == LP87565_DEVICE_TYPE_LP87565_Q1) {
+ min_idx = LP87565_BUCK_10;
+ max_idx = LP87565_BUCK_23;
+ }
+
+ for (i = min_idx; i <= max_idx; i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i].desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(lp87565->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id lp87565_regulator_id_table[] = {
+ { "lp87565-regulator", },
+ { "lp87565-q1-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, lp87565_regulator_id_table);
+
+static struct platform_driver lp87565_regulator_driver = {
+ .driver = {
+ .name = "lp87565-pmic",
+ },
+ .probe = lp87565_regulator_probe,
+ .id_table = lp87565_regulator_id_table,
+};
+module_platform_driver(lp87565_regulator_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("LP87565 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c
new file mode 100644
index 000000000..ec46290b6
--- /dev/null
+++ b/drivers/regulator/lp8788-buck.c
@@ -0,0 +1,554 @@
+/*
+ * TI LP8788 MFD - buck regulator driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/gpio.h>
+
+/* 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 const 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 const 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 = devm_regulator_register(&pdev->dev, &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 struct platform_driver lp8788_buck_driver = {
+ .probe = lp8788_buck_probe,
+ .driver = {
+ .name = LP8788_DEV_BUCK,
+ },
+};
+
+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 000000000..f2347474a
--- /dev/null
+++ b/drivers/regulator/lp8788-ldo.c
@@ -0,0 +1,637 @@
+/*
+ * TI LP8788 MFD - ldo regulator driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/lp8788.h>
+
+/* 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
+
+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 gpio_desc *ena_gpiod;
+};
+
+/* 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 const 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 const 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;
+ 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;
+ }
+
+ /* FIXME: check default mode for GPIO here: high or low? */
+ ldo->ena_gpiod = devm_gpiod_get_index_optional(&pdev->dev,
+ "enable",
+ enable_id,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ldo->ena_gpiod))
+ return PTR_ERR(ldo->ena_gpiod);
+
+ /* if no GPIO for ldo pin, then set default enable mode */
+ if (!ldo->ena_gpiod)
+ goto set_default_ldo_enable_mode;
+
+ 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->ena_gpiod)
+ cfg.ena_gpiod = ldo->ena_gpiod;
+
+ 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 = devm_regulator_register(&pdev->dev, &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 struct platform_driver lp8788_dldo_driver = {
+ .probe = lp8788_dldo_probe,
+ .driver = {
+ .name = LP8788_DEV_DLDO,
+ },
+};
+
+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->ena_gpiod)
+ cfg.ena_gpiod = ldo->ena_gpiod;
+
+ 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 = devm_regulator_register(&pdev->dev, &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 struct platform_driver lp8788_aldo_driver = {
+ .probe = lp8788_aldo_probe,
+ .driver = {
+ .name = LP8788_DEV_ALDO,
+ },
+};
+
+static struct platform_driver * const drivers[] = {
+ &lp8788_dldo_driver,
+ &lp8788_aldo_driver,
+};
+
+static int __init lp8788_ldo_init(void)
+{
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
+}
+subsys_initcall(lp8788_ldo_init);
+
+static void __exit lp8788_ldo_exit(void)
+{
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
+}
+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/ltc3589.c b/drivers/regulator/ltc3589.c
new file mode 100644
index 000000000..18d5b01dd
--- /dev/null
+++ b/drivers/regulator/ltc3589.c
@@ -0,0 +1,579 @@
+/*
+ * Linear Technology LTC3589,LTC3589-1 regulator support
+ *
+ * Copyright (c) 2014 Philipp Zabel <p.zabel@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ */
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define DRIVER_NAME "ltc3589"
+
+#define LTC3589_IRQSTAT 0x02
+#define LTC3589_SCR1 0x07
+#define LTC3589_OVEN 0x10
+#define LTC3589_SCR2 0x12
+#define LTC3589_PGSTAT 0x13
+#define LTC3589_VCCR 0x20
+#define LTC3589_CLIRQ 0x21
+#define LTC3589_B1DTV1 0x23
+#define LTC3589_B1DTV2 0x24
+#define LTC3589_VRRCR 0x25
+#define LTC3589_B2DTV1 0x26
+#define LTC3589_B2DTV2 0x27
+#define LTC3589_B3DTV1 0x29
+#define LTC3589_B3DTV2 0x2a
+#define LTC3589_L2DTV1 0x32
+#define LTC3589_L2DTV2 0x33
+
+#define LTC3589_IRQSTAT_PGOOD_TIMEOUT BIT(3)
+#define LTC3589_IRQSTAT_UNDERVOLT_WARN BIT(4)
+#define LTC3589_IRQSTAT_UNDERVOLT_FAULT BIT(5)
+#define LTC3589_IRQSTAT_THERMAL_WARN BIT(6)
+#define LTC3589_IRQSTAT_THERMAL_FAULT BIT(7)
+
+#define LTC3589_OVEN_SW1 BIT(0)
+#define LTC3589_OVEN_SW2 BIT(1)
+#define LTC3589_OVEN_SW3 BIT(2)
+#define LTC3589_OVEN_BB_OUT BIT(3)
+#define LTC3589_OVEN_LDO2 BIT(4)
+#define LTC3589_OVEN_LDO3 BIT(5)
+#define LTC3589_OVEN_LDO4 BIT(6)
+#define LTC3589_OVEN_SW_CTRL BIT(7)
+
+#define LTC3589_VCCR_SW1_GO BIT(0)
+#define LTC3589_VCCR_SW2_GO BIT(2)
+#define LTC3589_VCCR_SW3_GO BIT(4)
+#define LTC3589_VCCR_LDO2_GO BIT(6)
+
+enum ltc3589_variant {
+ LTC3589,
+ LTC3589_1,
+ LTC3589_2,
+};
+
+enum ltc3589_reg {
+ LTC3589_SW1,
+ LTC3589_SW2,
+ LTC3589_SW3,
+ LTC3589_BB_OUT,
+ LTC3589_LDO1,
+ LTC3589_LDO2,
+ LTC3589_LDO3,
+ LTC3589_LDO4,
+ LTC3589_NUM_REGULATORS,
+};
+
+struct ltc3589_regulator {
+ struct regulator_desc desc;
+
+ /* External feedback voltage divider */
+ unsigned int r1;
+ unsigned int r2;
+};
+
+struct ltc3589 {
+ struct regmap *regmap;
+ struct device *dev;
+ enum ltc3589_variant variant;
+ struct ltc3589_regulator regulator_descs[LTC3589_NUM_REGULATORS];
+ struct regulator_dev *regulators[LTC3589_NUM_REGULATORS];
+};
+
+static const int ltc3589_ldo4[] = {
+ 2800000, 2500000, 1800000, 3300000,
+};
+
+static const int ltc3589_12_ldo4[] = {
+ 1200000, 1800000, 2500000, 3200000,
+};
+
+static int ltc3589_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct ltc3589 *ltc3589 = rdev_get_drvdata(rdev);
+ int sel, shift;
+
+ if (unlikely(ramp_delay <= 0))
+ return -EINVAL;
+
+ /* VRRCR slew rate offsets are the same as VCCR go bit offsets */
+ shift = ffs(rdev->desc->apply_bit) - 1;
+
+ /* The slew rate can be set to 0.88, 1.75, 3.5, or 7 mV/uS */
+ for (sel = 0; sel < 4; sel++) {
+ if ((880 << sel) >= ramp_delay) {
+ return regmap_update_bits(ltc3589->regmap,
+ LTC3589_VRRCR,
+ 0x3 << shift, sel << shift);
+ }
+ }
+ return -EINVAL;
+}
+
+static int ltc3589_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct ltc3589 *ltc3589 = rdev_get_drvdata(rdev);
+ int sel;
+
+ sel = regulator_map_voltage_linear(rdev, uV, uV);
+ if (sel < 0)
+ return sel;
+
+ /* DTV2 register follows right after the corresponding DTV1 register */
+ return regmap_update_bits(ltc3589->regmap, rdev->desc->vsel_reg + 1,
+ rdev->desc->vsel_mask, sel);
+}
+
+static int ltc3589_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct ltc3589 *ltc3589 = rdev_get_drvdata(rdev);
+ int mask, bit = 0;
+
+ /* VCCR reference selects are right next to the VCCR go bits */
+ mask = rdev->desc->apply_bit << 1;
+
+ if (mode == REGULATOR_MODE_STANDBY)
+ bit = mask; /* Select DTV2 */
+
+ mask |= rdev->desc->apply_bit;
+ bit |= rdev->desc->apply_bit;
+ return regmap_update_bits(ltc3589->regmap, LTC3589_VCCR, mask, bit);
+}
+
+/* SW1, SW2, SW3, LDO2 */
+static const struct regulator_ops ltc3589_linear_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_ramp_delay = ltc3589_set_ramp_delay,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_suspend_voltage = ltc3589_set_suspend_voltage,
+ .set_suspend_mode = ltc3589_set_suspend_mode,
+};
+
+/* BB_OUT, LDO3 */
+static const struct regulator_ops ltc3589_fixed_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+/* LDO1 */
+static const struct regulator_ops ltc3589_fixed_standby_regulator_ops = {
+};
+
+/* LDO4 */
+static const struct regulator_ops ltc3589_table_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+
+#define LTC3589_REG(_name, _ops, en_bit, dtv1_reg, dtv_mask, go_bit) \
+ [LTC3589_ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = (dtv_mask) + 1, \
+ .min_uV = (go_bit) ? 362500 : 0, \
+ .uV_step = (go_bit) ? 12500 : 0, \
+ .ramp_delay = (go_bit) ? 1750 : 0, \
+ .fixed_uV = (dtv_mask) ? 0 : 800000, \
+ .ops = &ltc3589_ ## _ops ## _regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = LTC3589_ ## _name, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = (dtv1_reg), \
+ .vsel_mask = (dtv_mask), \
+ .apply_reg = (go_bit) ? LTC3589_VCCR : 0, \
+ .apply_bit = (go_bit), \
+ .enable_reg = (en_bit) ? LTC3589_OVEN : 0, \
+ .enable_mask = (en_bit), \
+ }, \
+ }
+
+#define LTC3589_LINEAR_REG(_name, _dtv1) \
+ LTC3589_REG(_name, linear, LTC3589_OVEN_ ## _name, \
+ LTC3589_ ## _dtv1, 0x1f, \
+ LTC3589_VCCR_ ## _name ## _GO)
+
+#define LTC3589_FIXED_REG(_name) \
+ LTC3589_REG(_name, fixed, LTC3589_OVEN_ ## _name, 0, 0, 0)
+
+static struct ltc3589_regulator ltc3589_regulators[LTC3589_NUM_REGULATORS] = {
+ LTC3589_LINEAR_REG(SW1, B1DTV1),
+ LTC3589_LINEAR_REG(SW2, B2DTV1),
+ LTC3589_LINEAR_REG(SW3, B3DTV1),
+ LTC3589_FIXED_REG(BB_OUT),
+ LTC3589_REG(LDO1, fixed_standby, 0, 0, 0, 0),
+ LTC3589_LINEAR_REG(LDO2, L2DTV1),
+ LTC3589_FIXED_REG(LDO3),
+ LTC3589_REG(LDO4, table, LTC3589_OVEN_LDO4, LTC3589_L2DTV2, 0x60, 0),
+};
+
+#ifdef CONFIG_OF
+static struct of_regulator_match ltc3589_matches[LTC3589_NUM_REGULATORS] = {
+ { .name = "sw1", },
+ { .name = "sw2", },
+ { .name = "sw3", },
+ { .name = "bb-out", },
+ { .name = "ldo1", }, /* standby */
+ { .name = "ldo2", },
+ { .name = "ldo3", },
+ { .name = "ldo4", },
+};
+
+static int ltc3589_parse_regulators_dt(struct ltc3589 *ltc3589)
+{
+ struct device *dev = ltc3589->dev;
+ struct device_node *node;
+ int i, ret;
+
+ node = of_get_child_by_name(dev->of_node, "regulators");
+ if (!node) {
+ dev_err(dev, "regulators node not found\n");
+ return -EINVAL;
+ }
+
+ ret = of_regulator_match(dev, node, ltc3589_matches,
+ ARRAY_SIZE(ltc3589_matches));
+ of_node_put(node);
+ if (ret < 0) {
+ dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+ return ret;
+ }
+ if (ret != LTC3589_NUM_REGULATORS) {
+ dev_err(dev, "Only %d regulators described in device tree\n",
+ ret);
+ return -EINVAL;
+ }
+
+ /* Parse feedback voltage dividers. LDO3 and LDO4 don't have them */
+ for (i = 0; i < LTC3589_LDO3; i++) {
+ struct ltc3589_regulator *desc = &ltc3589->regulator_descs[i];
+ struct device_node *np = ltc3589_matches[i].of_node;
+ u32 vdiv[2];
+
+ ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider",
+ vdiv, 2);
+ if (ret) {
+ dev_err(dev, "Failed to parse voltage divider: %d\n",
+ ret);
+ return ret;
+ }
+
+ desc->r1 = vdiv[0];
+ desc->r2 = vdiv[1];
+ }
+
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return ltc3589_matches[index].init_data;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return ltc3589_matches[index].of_node;
+}
+#else
+static inline int ltc3589_parse_regulators_dt(struct ltc3589 *ltc3589)
+{
+ 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 bool ltc3589_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3589_IRQSTAT:
+ case LTC3589_SCR1:
+ case LTC3589_OVEN:
+ case LTC3589_SCR2:
+ case LTC3589_VCCR:
+ case LTC3589_CLIRQ:
+ case LTC3589_B1DTV1:
+ case LTC3589_B1DTV2:
+ case LTC3589_VRRCR:
+ case LTC3589_B2DTV1:
+ case LTC3589_B2DTV2:
+ case LTC3589_B3DTV1:
+ case LTC3589_B3DTV2:
+ case LTC3589_L2DTV1:
+ case LTC3589_L2DTV2:
+ return true;
+ }
+ return false;
+}
+
+static bool ltc3589_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3589_IRQSTAT:
+ case LTC3589_SCR1:
+ case LTC3589_OVEN:
+ case LTC3589_SCR2:
+ case LTC3589_PGSTAT:
+ case LTC3589_VCCR:
+ case LTC3589_B1DTV1:
+ case LTC3589_B1DTV2:
+ case LTC3589_VRRCR:
+ case LTC3589_B2DTV1:
+ case LTC3589_B2DTV2:
+ case LTC3589_B3DTV1:
+ case LTC3589_B3DTV2:
+ case LTC3589_L2DTV1:
+ case LTC3589_L2DTV2:
+ return true;
+ }
+ return false;
+}
+
+static bool ltc3589_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3589_IRQSTAT:
+ case LTC3589_PGSTAT:
+ case LTC3589_VCCR:
+ return true;
+ }
+ return false;
+}
+
+static const struct reg_default ltc3589_reg_defaults[] = {
+ { LTC3589_SCR1, 0x00 },
+ { LTC3589_OVEN, 0x00 },
+ { LTC3589_SCR2, 0x00 },
+ { LTC3589_VCCR, 0x00 },
+ { LTC3589_B1DTV1, 0x19 },
+ { LTC3589_B1DTV2, 0x19 },
+ { LTC3589_VRRCR, 0xff },
+ { LTC3589_B2DTV1, 0x19 },
+ { LTC3589_B2DTV2, 0x19 },
+ { LTC3589_B3DTV1, 0x19 },
+ { LTC3589_B3DTV2, 0x19 },
+ { LTC3589_L2DTV1, 0x19 },
+ { LTC3589_L2DTV2, 0x19 },
+};
+
+static const struct regmap_config ltc3589_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = ltc3589_writeable_reg,
+ .readable_reg = ltc3589_readable_reg,
+ .volatile_reg = ltc3589_volatile_reg,
+ .max_register = LTC3589_L2DTV2,
+ .reg_defaults = ltc3589_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ltc3589_reg_defaults),
+ .use_single_rw = true,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+
+static irqreturn_t ltc3589_isr(int irq, void *dev_id)
+{
+ struct ltc3589 *ltc3589 = dev_id;
+ unsigned int i, irqstat, event;
+
+ regmap_read(ltc3589->regmap, LTC3589_IRQSTAT, &irqstat);
+
+ if (irqstat & LTC3589_IRQSTAT_THERMAL_WARN) {
+ event = REGULATOR_EVENT_OVER_TEMP;
+ for (i = 0; i < LTC3589_NUM_REGULATORS; i++)
+ regulator_notifier_call_chain(ltc3589->regulators[i],
+ event, NULL);
+ }
+
+ if (irqstat & LTC3589_IRQSTAT_UNDERVOLT_WARN) {
+ event = REGULATOR_EVENT_UNDER_VOLTAGE;
+ for (i = 0; i < LTC3589_NUM_REGULATORS; i++)
+ regulator_notifier_call_chain(ltc3589->regulators[i],
+ event, NULL);
+ }
+
+ /* Clear warning condition */
+ regmap_write(ltc3589->regmap, LTC3589_CLIRQ, 0);
+
+ return IRQ_HANDLED;
+}
+
+static inline unsigned int ltc3589_scale(unsigned int uV, u32 r1, u32 r2)
+{
+ uint64_t tmp;
+ if (uV == 0)
+ return 0;
+ tmp = (uint64_t)uV * r1;
+ do_div(tmp, r2);
+ return uV + (unsigned int)tmp;
+}
+
+static void ltc3589_apply_fb_voltage_divider(struct ltc3589_regulator *rdesc)
+{
+ struct regulator_desc *desc = &rdesc->desc;
+
+ if (!rdesc->r1 || !rdesc->r2)
+ return;
+
+ desc->min_uV = ltc3589_scale(desc->min_uV, rdesc->r1, rdesc->r2);
+ desc->uV_step = ltc3589_scale(desc->uV_step, rdesc->r1, rdesc->r2);
+ desc->fixed_uV = ltc3589_scale(desc->fixed_uV, rdesc->r1, rdesc->r2);
+}
+
+static int ltc3589_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ltc3589_regulator *descs;
+ struct ltc3589 *ltc3589;
+ int i, ret;
+
+ ltc3589 = devm_kzalloc(dev, sizeof(*ltc3589), GFP_KERNEL);
+ if (!ltc3589)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ltc3589);
+ if (client->dev.of_node)
+ ltc3589->variant = (enum ltc3589_variant)
+ of_device_get_match_data(&client->dev);
+ else
+ ltc3589->variant = id->driver_data;
+ ltc3589->dev = dev;
+
+ descs = ltc3589->regulator_descs;
+ memcpy(descs, ltc3589_regulators, sizeof(ltc3589_regulators));
+ if (ltc3589->variant == LTC3589) {
+ descs[LTC3589_LDO3].desc.fixed_uV = 1800000;
+ descs[LTC3589_LDO4].desc.volt_table = ltc3589_ldo4;
+ } else {
+ descs[LTC3589_LDO3].desc.fixed_uV = 2800000;
+ descs[LTC3589_LDO4].desc.volt_table = ltc3589_12_ldo4;
+ }
+
+ ltc3589->regmap = devm_regmap_init_i2c(client, &ltc3589_regmap_config);
+ if (IS_ERR(ltc3589->regmap)) {
+ ret = PTR_ERR(ltc3589->regmap);
+ dev_err(dev, "failed to initialize regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = ltc3589_parse_regulators_dt(ltc3589);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < LTC3589_NUM_REGULATORS; i++) {
+ struct ltc3589_regulator *rdesc = &ltc3589->regulator_descs[i];
+ struct regulator_desc *desc = &rdesc->desc;
+ struct regulator_init_data *init_data;
+ struct regulator_config config = { };
+
+ init_data = match_init_data(i);
+
+ if (i < LTC3589_LDO3)
+ ltc3589_apply_fb_voltage_divider(rdesc);
+
+ config.dev = dev;
+ config.init_data = init_data;
+ config.driver_data = ltc3589;
+ config.of_node = match_of_node(i);
+
+ ltc3589->regulators[i] = devm_regulator_register(dev, desc,
+ &config);
+ if (IS_ERR(ltc3589->regulators[i])) {
+ ret = PTR_ERR(ltc3589->regulators[i]);
+ dev_err(dev, "failed to register regulator %s: %d\n",
+ desc->name, ret);
+ return ret;
+ }
+ }
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ ltc3589_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, ltc3589);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id ltc3589_i2c_id[] = {
+ { "ltc3589", LTC3589 },
+ { "ltc3589-1", LTC3589_1 },
+ { "ltc3589-2", LTC3589_2 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3589_i2c_id);
+
+static const struct of_device_id ltc3589_of_match[] = {
+ {
+ .compatible = "lltc,ltc3589",
+ .data = (void *)LTC3589,
+ },
+ {
+ .compatible = "lltc,ltc3589-1",
+ .data = (void *)LTC3589_1,
+ },
+ {
+ .compatible = "lltc,ltc3589-2",
+ .data = (void *)LTC3589_2,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ltc3589_of_match);
+
+static struct i2c_driver ltc3589_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ltc3589_of_match),
+ },
+ .probe = ltc3589_probe,
+ .id_table = ltc3589_i2c_id,
+};
+module_i2c_driver(ltc3589_driver);
+
+MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>");
+MODULE_DESCRIPTION("Regulator driver for Linear Technology LTC3589(-1,2)");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
new file mode 100644
index 000000000..9dec1609f
--- /dev/null
+++ b/drivers/regulator/ltc3676.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2016 Gateworks Corporation, 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 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.
+ *
+ */
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define DRIVER_NAME "ltc3676"
+
+/* LTC3676 Registers */
+#define LTC3676_BUCK1 0x01
+#define LTC3676_BUCK2 0x02
+#define LTC3676_BUCK3 0x03
+#define LTC3676_BUCK4 0x04
+#define LTC3676_LDOA 0x05
+#define LTC3676_LDOB 0x06
+#define LTC3676_SQD1 0x07
+#define LTC3676_SQD2 0x08
+#define LTC3676_CNTRL 0x09
+#define LTC3676_DVB1A 0x0A
+#define LTC3676_DVB1B 0x0B
+#define LTC3676_DVB2A 0x0C
+#define LTC3676_DVB2B 0x0D
+#define LTC3676_DVB3A 0x0E
+#define LTC3676_DVB3B 0x0F
+#define LTC3676_DVB4A 0x10
+#define LTC3676_DVB4B 0x11
+#define LTC3676_MSKIRQ 0x12
+#define LTC3676_MSKPG 0x13
+#define LTC3676_USER 0x14
+#define LTC3676_IRQSTAT 0x15
+#define LTC3676_PGSTATL 0x16
+#define LTC3676_PGSTATRT 0x17
+#define LTC3676_HRST 0x1E
+#define LTC3676_CLIRQ 0x1F
+
+#define LTC3676_DVBxA_REF_SELECT BIT(5)
+#define LTC3676_DVBxB_PGOOD_MASK BIT(5)
+
+#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3)
+#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4)
+#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5)
+#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6)
+#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7)
+
+enum ltc3676_reg {
+ LTC3676_SW1,
+ LTC3676_SW2,
+ LTC3676_SW3,
+ LTC3676_SW4,
+ LTC3676_LDO1,
+ LTC3676_LDO2,
+ LTC3676_LDO3,
+ LTC3676_LDO4,
+ LTC3676_NUM_REGULATORS,
+};
+
+struct ltc3676 {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_desc regulator_descs[LTC3676_NUM_REGULATORS];
+ struct regulator_dev *regulators[LTC3676_NUM_REGULATORS];
+};
+
+static int ltc3676_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct ltc3676 *ltc3676 = rdev_get_drvdata(rdev);
+ struct device *dev = ltc3676->dev;
+ int dcdc = rdev_get_id(rdev);
+ int sel;
+
+ dev_dbg(dev, "%s id=%d uV=%d\n", __func__, dcdc, uV);
+ sel = regulator_map_voltage_linear(rdev, uV, uV);
+ if (sel < 0)
+ return sel;
+
+ /* DVBB register follows right after the corresponding DVBA register */
+ return regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg + 1,
+ rdev->desc->vsel_mask, sel);
+}
+
+static int ltc3676_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct ltc3676 *ltc3676= rdev_get_drvdata(rdev);
+ struct device *dev = ltc3676->dev;
+ int mask, val;
+ int dcdc = rdev_get_id(rdev);
+
+ dev_dbg(dev, "%s id=%d mode=%d\n", __func__, dcdc, mode);
+
+ mask = LTC3676_DVBxA_REF_SELECT;
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY:
+ val = 0; /* select DVBxA */
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = LTC3676_DVBxA_REF_SELECT; /* select DVBxB */
+ break;
+ default:
+ dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n",
+ rdev->desc->name, mode);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg,
+ mask, val);
+}
+
+static int ltc3676_set_voltage_sel(struct regulator_dev *rdev, unsigned selector)
+{
+ struct ltc3676 *ltc3676 = rdev_get_drvdata(rdev);
+ struct device *dev = ltc3676->dev;
+ int ret, dcdc = rdev_get_id(rdev);
+
+ dev_dbg(dev, "%s id=%d selector=%d\n", __func__, dcdc, selector);
+
+ ret = regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg + 1,
+ LTC3676_DVBxB_PGOOD_MASK,
+ LTC3676_DVBxB_PGOOD_MASK);
+ if (ret)
+ return ret;
+
+ return regulator_set_voltage_sel_regmap(rdev, selector);
+}
+
+static inline unsigned int ltc3676_scale(unsigned int uV, u32 r1, u32 r2)
+{
+ uint64_t tmp;
+ if (uV == 0)
+ return 0;
+ tmp = (uint64_t)uV * r1;
+ do_div(tmp, r2);
+ return uV + (unsigned int)tmp;
+}
+
+static int ltc3676_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct ltc3676 *ltc3676 = config->driver_data;
+ struct regulator_desc *rdesc = &ltc3676->regulator_descs[desc->id];
+ u32 r[2];
+ int ret;
+
+ /* LDO3 has a fixed output */
+ if (desc->id == LTC3676_LDO3)
+ return 0;
+
+ ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider", r, 2);
+ if (ret) {
+ dev_err(ltc3676->dev, "Failed to parse voltage divider: %d\n",
+ ret);
+ return ret;
+ }
+
+ rdesc->min_uV = ltc3676_scale(desc->min_uV, r[0], r[1]);
+ rdesc->uV_step = ltc3676_scale(desc->uV_step, r[0], r[1]);
+ rdesc->fixed_uV = ltc3676_scale(desc->fixed_uV, r[0], r[1]);
+
+ return 0;
+}
+
+/* SW1, SW2, SW3, SW4 linear 0.8V-3.3V with scalar via R1/R2 feeback res */
+static const struct regulator_ops ltc3676_linear_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = ltc3676_set_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_suspend_voltage = ltc3676_set_suspend_voltage,
+ .set_suspend_mode = ltc3676_set_suspend_mode,
+};
+
+/* LDO1 always on fixed 0.8V-3.3V via scalar via R1/R2 feeback res */
+static const struct regulator_ops ltc3676_fixed_standby_regulator_ops = {
+};
+
+/* LDO2, LDO3 fixed (LDO2 has external scalar via R1/R2 feedback res) */
+static const struct regulator_ops ltc3676_fixed_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define LTC3676_REG(_id, _name, _ops, en_reg, en_bit, dvba_reg, dvb_mask) \
+ [LTC3676_ ## _id] = { \
+ .name = #_name, \
+ .of_match = of_match_ptr(#_name), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = ltc3676_of_parse_cb, \
+ .n_voltages = (dvb_mask) + 1, \
+ .min_uV = (dvba_reg) ? 412500 : 0, \
+ .uV_step = (dvba_reg) ? 12500 : 0, \
+ .ramp_delay = (dvba_reg) ? 800 : 0, \
+ .fixed_uV = (dvb_mask) ? 0 : 725000, \
+ .ops = &ltc3676_ ## _ops ## _regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = LTC3676_ ## _id, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = (dvba_reg), \
+ .vsel_mask = (dvb_mask), \
+ .enable_reg = (en_reg), \
+ .enable_mask = (1 << en_bit), \
+ }
+
+#define LTC3676_LINEAR_REG(_id, _name, _en, _dvba) \
+ LTC3676_REG(_id, _name, linear, \
+ LTC3676_ ## _en, 7, \
+ LTC3676_ ## _dvba, 0x1f)
+
+#define LTC3676_FIXED_REG(_id, _name, _en_reg, _en_bit) \
+ LTC3676_REG(_id, _name, fixed, LTC3676_ ## _en_reg, _en_bit, 0, 0)
+
+static struct regulator_desc ltc3676_regulators[LTC3676_NUM_REGULATORS] = {
+ LTC3676_LINEAR_REG(SW1, sw1, BUCK1, DVB1A),
+ LTC3676_LINEAR_REG(SW2, sw2, BUCK2, DVB2A),
+ LTC3676_LINEAR_REG(SW3, sw3, BUCK3, DVB3A),
+ LTC3676_LINEAR_REG(SW4, sw4, BUCK4, DVB4A),
+ LTC3676_REG(LDO1, ldo1, fixed_standby, 0, 0, 0, 0),
+ LTC3676_FIXED_REG(LDO2, ldo2, LDOA, 2),
+ LTC3676_FIXED_REG(LDO3, ldo3, LDOA, 5),
+ LTC3676_FIXED_REG(LDO4, ldo4, LDOB, 2),
+};
+
+static bool ltc3676_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_IRQSTAT:
+ case LTC3676_BUCK1:
+ case LTC3676_BUCK2:
+ case LTC3676_BUCK3:
+ case LTC3676_BUCK4:
+ case LTC3676_LDOA:
+ case LTC3676_LDOB:
+ case LTC3676_SQD1:
+ case LTC3676_SQD2:
+ case LTC3676_CNTRL:
+ case LTC3676_DVB1A:
+ case LTC3676_DVB1B:
+ case LTC3676_DVB2A:
+ case LTC3676_DVB2B:
+ case LTC3676_DVB3A:
+ case LTC3676_DVB3B:
+ case LTC3676_DVB4A:
+ case LTC3676_DVB4B:
+ case LTC3676_MSKIRQ:
+ case LTC3676_MSKPG:
+ case LTC3676_USER:
+ case LTC3676_HRST:
+ case LTC3676_CLIRQ:
+ return true;
+ }
+ return false;
+}
+
+static bool ltc3676_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_IRQSTAT:
+ case LTC3676_BUCK1:
+ case LTC3676_BUCK2:
+ case LTC3676_BUCK3:
+ case LTC3676_BUCK4:
+ case LTC3676_LDOA:
+ case LTC3676_LDOB:
+ case LTC3676_SQD1:
+ case LTC3676_SQD2:
+ case LTC3676_CNTRL:
+ case LTC3676_DVB1A:
+ case LTC3676_DVB1B:
+ case LTC3676_DVB2A:
+ case LTC3676_DVB2B:
+ case LTC3676_DVB3A:
+ case LTC3676_DVB3B:
+ case LTC3676_DVB4A:
+ case LTC3676_DVB4B:
+ case LTC3676_MSKIRQ:
+ case LTC3676_MSKPG:
+ case LTC3676_USER:
+ case LTC3676_HRST:
+ case LTC3676_CLIRQ:
+ return true;
+ }
+ return false;
+}
+
+static bool ltc3676_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_IRQSTAT:
+ case LTC3676_PGSTATL:
+ case LTC3676_PGSTATRT:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config ltc3676_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = ltc3676_writeable_reg,
+ .readable_reg = ltc3676_readable_reg,
+ .volatile_reg = ltc3676_volatile_reg,
+ .max_register = LTC3676_CLIRQ,
+ .use_single_rw = true,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t ltc3676_isr(int irq, void *dev_id)
+{
+ struct ltc3676 *ltc3676 = dev_id;
+ struct device *dev = ltc3676->dev;
+ unsigned int i, irqstat, event;
+
+ regmap_read(ltc3676->regmap, LTC3676_IRQSTAT, &irqstat);
+
+ dev_dbg(dev, "irq%d irqstat=0x%02x\n", irq, irqstat);
+ if (irqstat & LTC3676_IRQSTAT_THERMAL_WARN) {
+ dev_warn(dev, "Over-temperature Warning\n");
+ event = REGULATOR_EVENT_OVER_TEMP;
+ for (i = 0; i < LTC3676_NUM_REGULATORS; i++)
+ regulator_notifier_call_chain(ltc3676->regulators[i],
+ event, NULL);
+ }
+
+ if (irqstat & LTC3676_IRQSTAT_UNDERVOLT_WARN) {
+ dev_info(dev, "Undervoltage Warning\n");
+ event = REGULATOR_EVENT_UNDER_VOLTAGE;
+ for (i = 0; i < LTC3676_NUM_REGULATORS; i++)
+ regulator_notifier_call_chain(ltc3676->regulators[i],
+ event, NULL);
+ }
+
+ /* Clear warning condition */
+ regmap_write(ltc3676->regmap, LTC3676_CLIRQ, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int ltc3676_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct regulator_init_data *init_data = dev_get_platdata(dev);
+ struct regulator_desc *descs;
+ struct ltc3676 *ltc3676;
+ int i, ret;
+
+ ltc3676 = devm_kzalloc(dev, sizeof(*ltc3676), GFP_KERNEL);
+ if (!ltc3676)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ltc3676);
+ ltc3676->dev = dev;
+
+ descs = ltc3676->regulator_descs;
+ memcpy(descs, ltc3676_regulators, sizeof(ltc3676_regulators));
+ descs[LTC3676_LDO3].fixed_uV = 1800000; /* LDO3 is fixed 1.8V */
+
+ ltc3676->regmap = devm_regmap_init_i2c(client, &ltc3676_regmap_config);
+ if (IS_ERR(ltc3676->regmap)) {
+ ret = PTR_ERR(ltc3676->regmap);
+ dev_err(dev, "failed to initialize regmap: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < LTC3676_NUM_REGULATORS; i++) {
+ struct regulator_desc *desc = &ltc3676->regulator_descs[i];
+ struct regulator_config config = { };
+
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ config.dev = dev;
+ config.driver_data = ltc3676;
+
+ ltc3676->regulators[i] = devm_regulator_register(dev, desc,
+ &config);
+ if (IS_ERR(ltc3676->regulators[i])) {
+ ret = PTR_ERR(ltc3676->regulators[i]);
+ dev_err(dev, "failed to register regulator %s: %d\n",
+ desc->name, ret);
+ return ret;
+ }
+ }
+
+ regmap_write(ltc3676->regmap, LTC3676_CLIRQ, 0);
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ ltc3676_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, ltc3676);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id ltc3676_i2c_id[] = {
+ { "ltc3676" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id);
+
+static const struct of_device_id ltc3676_of_match[] = {
+ { .compatible = "lltc,ltc3676" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ltc3676_of_match);
+
+static struct i2c_driver ltc3676_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ltc3676_of_match),
+ },
+ .probe = ltc3676_regulator_probe,
+ .id_table = ltc3676_i2c_id,
+};
+module_i2c_driver(ltc3676_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("Regulator driver for Linear Technology LTC1376");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c
new file mode 100644
index 000000000..bc7f4751b
--- /dev/null
+++ b/drivers/regulator/max14577-regulator.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max14577.c - Regulator driver for the Maxim 14577/77836
+//
+// Copyright (C) 2013,2014 Samsung Electronics
+// Krzysztof Kozlowski <krzk@kernel.org>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/max14577.h>
+#include <linux/mfd/max14577-private.h>
+#include <linux/regulator/of_regulator.h>
+
+static int max14577_reg_is_enabled(struct regulator_dev *rdev)
+{
+ int rid = rdev_get_id(rdev);
+ struct regmap *rmap = rdev->regmap;
+ u8 reg_data;
+
+ switch (rid) {
+ case MAX14577_CHARGER:
+ max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, &reg_data);
+ if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0)
+ return 0;
+ max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
+ if ((reg_data & STATUS3_CGMBC_MASK) == 0)
+ return 0;
+ /* MBCHOSTEN and CGMBC are on */
+ return 1;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
+{
+ u8 reg_data;
+ struct regmap *rmap = rdev->regmap;
+ struct max14577 *max14577 = rdev_get_drvdata(rdev);
+ const struct maxim_charger_current *limits =
+ &maxim_charger_currents[max14577->dev_type];
+
+ if (rdev_get_id(rdev) != MAX14577_CHARGER)
+ return -EINVAL;
+
+ max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, &reg_data);
+
+ if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
+ return limits->min;
+
+ reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
+ CHGCTRL4_MBCICHWRCH_SHIFT);
+ return limits->high_start + reg_data * limits->high_step;
+}
+
+static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ u8 reg_data;
+ int ret;
+ struct max14577 *max14577 = rdev_get_drvdata(rdev);
+ const struct maxim_charger_current *limits =
+ &maxim_charger_currents[max14577->dev_type];
+
+ if (rdev_get_id(rdev) != MAX14577_CHARGER)
+ return -EINVAL;
+
+ ret = maxim_charger_calc_reg_current(limits, min_uA, max_uA, &reg_data);
+ if (ret)
+ return ret;
+
+ return max14577_update_reg(rdev->regmap, MAX14577_CHG_REG_CHG_CTRL4,
+ CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
+ reg_data);
+}
+
+static const struct regulator_ops max14577_safeout_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static const struct regulator_ops max14577_charger_ops = {
+ .is_enabled = max14577_reg_is_enabled,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_current_limit = max14577_reg_get_current_limit,
+ .set_current_limit = max14577_reg_set_current_limit,
+};
+
+#define MAX14577_SAFEOUT_REG { \
+ .name = "SAFEOUT", \
+ .of_match = of_match_ptr("SAFEOUT"), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX14577_SAFEOUT, \
+ .ops = &max14577_safeout_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE, \
+ .enable_reg = MAX14577_REG_CONTROL2, \
+ .enable_mask = CTRL2_SFOUTORD_MASK, \
+}
+#define MAX14577_CHARGER_REG { \
+ .name = "CHARGER", \
+ .of_match = of_match_ptr("CHARGER"), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX14577_CHARGER, \
+ .ops = &max14577_charger_ops, \
+ .type = REGULATOR_CURRENT, \
+ .owner = THIS_MODULE, \
+ .enable_reg = MAX14577_CHG_REG_CHG_CTRL2, \
+ .enable_mask = CHGCTRL2_MBCHOSTEN_MASK, \
+}
+
+static const struct regulator_desc max14577_supported_regulators[] = {
+ [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+ [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
+};
+
+static const struct regulator_ops max77836_ldo_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,
+ /* TODO: add .set_suspend_mode */
+};
+
+#define MAX77836_LDO_REG(num) { \
+ .name = "LDO" # num, \
+ .of_match = of_match_ptr("LDO" # num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77836_LDO ## num, \
+ .ops = &max77836_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, \
+ .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, \
+ .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, \
+ .enable_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+ .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK, \
+ .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+ .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK, \
+}
+
+static const struct regulator_desc max77836_supported_regulators[] = {
+ [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+ [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
+ [MAX77836_LDO1] = MAX77836_LDO_REG(1),
+ [MAX77836_LDO2] = MAX77836_LDO_REG(2),
+};
+
+#ifdef CONFIG_OF
+static struct of_regulator_match max14577_regulator_matches[] = {
+ { .name = "SAFEOUT", },
+ { .name = "CHARGER", },
+};
+
+static struct of_regulator_match max77836_regulator_matches[] = {
+ { .name = "SAFEOUT", },
+ { .name = "CHARGER", },
+ { .name = "LDO1", },
+ { .name = "LDO2", },
+};
+
+static inline struct regulator_init_data *match_init_data(int index,
+ enum maxim_device_type dev_type)
+{
+ switch (dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ return max77836_regulator_matches[index].init_data;
+
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ return max14577_regulator_matches[index].init_data;
+ }
+}
+
+static inline struct device_node *match_of_node(int index,
+ enum maxim_device_type dev_type)
+{
+ switch (dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ return max77836_regulator_matches[index].of_node;
+
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ return max14577_regulator_matches[index].of_node;
+ }
+}
+#else /* CONFIG_OF */
+static inline struct regulator_init_data *match_init_data(int index,
+ enum maxim_device_type dev_type)
+{
+ return NULL;
+}
+
+static inline struct device_node *match_of_node(int index,
+ enum maxim_device_type dev_type)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
+/**
+ * Registers for regulators of max77836 use different I2C slave addresses so
+ * different regmaps must be used for them.
+ *
+ * Returns proper regmap for accessing regulator passed by id.
+ */
+static struct regmap *max14577_get_regmap(struct max14577 *max14577,
+ int reg_id)
+{
+ switch (max14577->dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ switch (reg_id) {
+ case MAX77836_SAFEOUT ... MAX77836_CHARGER:
+ return max14577->regmap;
+ default:
+ /* MAX77836_LDO1 ... MAX77836_LDO2 */
+ return max14577->regmap_pmic;
+ }
+
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ return max14577->regmap;
+ }
+}
+
+static int max14577_regulator_probe(struct platform_device *pdev)
+{
+ struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent);
+ struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
+ int i, ret = 0;
+ struct regulator_config config = {};
+ const struct regulator_desc *supported_regulators;
+ unsigned int supported_regulators_size;
+ enum maxim_device_type dev_type = max14577->dev_type;
+
+ switch (dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ supported_regulators = max77836_supported_regulators;
+ supported_regulators_size = ARRAY_SIZE(max77836_supported_regulators);
+ break;
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ supported_regulators = max14577_supported_regulators;
+ supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators);
+ }
+
+ config.dev = max14577->dev;
+ config.driver_data = max14577;
+
+ for (i = 0; i < supported_regulators_size; i++) {
+ struct regulator_dev *regulator;
+ /*
+ * Index of supported_regulators[] is also the id and must
+ * match index of pdata->regulators[].
+ */
+ if (pdata && pdata->regulators) {
+ config.init_data = pdata->regulators[i].initdata;
+ config.of_node = pdata->regulators[i].of_node;
+ } else {
+ config.init_data = match_init_data(i, dev_type);
+ config.of_node = match_of_node(i, dev_type);
+ }
+ config.regmap = max14577_get_regmap(max14577,
+ supported_regulators[i].id);
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &supported_regulators[i], &config);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+ dev_err(&pdev->dev,
+ "Regulator init failed for %d/%s with error: %d\n",
+ i, supported_regulators[i].name, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static const struct platform_device_id max14577_regulator_id[] = {
+ { "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, },
+ { "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, max14577_regulator_id);
+
+static struct platform_driver max14577_regulator_driver = {
+ .driver = {
+ .name = "max14577-regulator",
+ },
+ .probe = max14577_regulator_probe,
+ .id_table = max14577_regulator_id,
+};
+
+static int __init max14577_regulator_init(void)
+{
+ BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
+ BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
+
+ BUILD_BUG_ON(MAX77836_REGULATOR_LDO_VOLTAGE_MIN +
+ (MAX77836_REGULATOR_LDO_VOLTAGE_STEP *
+ (MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM - 1)) !=
+ MAX77836_REGULATOR_LDO_VOLTAGE_MAX);
+
+ return platform_driver_register(&max14577_regulator_driver);
+}
+subsys_initcall(max14577_regulator_init);
+
+static void __exit max14577_regulator_exit(void)
+{
+ platform_driver_unregister(&max14577_regulator_driver);
+}
+module_exit(max14577_regulator_exit);
+
+MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
+MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max14577-regulator");
diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c
new file mode 100644
index 000000000..cc52779b5
--- /dev/null
+++ b/drivers/regulator/max1586.c
@@ -0,0 +1,329 @@
+/*
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+#include <linux/regulator/max1586.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
+
+#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;
+};
+
+/*
+ * 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 const 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 const 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 of_get_max1586_platform_data(struct device *dev,
+ struct max1586_platform_data *pdata)
+{
+ struct max1586_subdev_data *sub;
+ struct of_regulator_match rmatch[ARRAY_SIZE(max1586_reg)] = { };
+ struct device_node *np = dev->of_node;
+ int i, matched;
+
+ if (of_property_read_u32(np, "v3-gain",
+ &pdata->v3_gain) < 0) {
+ dev_err(dev, "%pOF has no 'v3-gain' property\n", np);
+ return -EINVAL;
+ }
+
+ np = of_get_child_by_name(np, "regulators");
+ if (!np) {
+ dev_err(dev, "missing 'regulators' subnode in DT\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rmatch); i++)
+ rmatch[i].name = max1586_reg[i].name;
+
+ matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch));
+ of_node_put(np);
+ /*
+ * If matched is 0, ie. neither Output_V3 nor Output_V6 have been found,
+ * return 0, which signals the normal situation where no subregulator is
+ * available. This is normal because the max1586 doesn't provide any
+ * readback support, so the subregulators can't report any status
+ * anyway. If matched < 0, return the error.
+ */
+ if (matched <= 0)
+ return matched;
+
+ pdata->subdevs = devm_kcalloc(dev,
+ matched,
+ sizeof(struct max1586_subdev_data),
+ GFP_KERNEL);
+ if (!pdata->subdevs)
+ return -ENOMEM;
+
+ pdata->num_subdevs = matched;
+ sub = pdata->subdevs;
+
+ for (i = 0; i < matched; i++) {
+ sub->id = i;
+ sub->name = rmatch[i].of_node->name;
+ sub->platform_data = rmatch[i].init_data;
+ sub++;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id max1586_of_match[] = {
+ { .compatible = "maxim,max1586", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max1586_of_match);
+
+static int max1586_pmic_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct max1586_platform_data *pdata, pdata_of;
+ struct regulator_config config = { };
+ struct max1586_data *max1586;
+ int i, id, ret;
+ const struct of_device_id *match;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (client->dev.of_node && !pdata) {
+ match = of_match_device(of_match_ptr(max1586_of_match),
+ &client->dev);
+ if (!match) {
+ dev_err(&client->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ ret = of_get_max1586_platform_data(&client->dev, &pdata_of);
+ if (ret < 0)
+ return ret;
+ pdata = &pdata_of;
+ }
+
+ max1586 = devm_kzalloc(&client->dev, sizeof(struct max1586_data),
+ 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;
+
+ for (i = 0; i < pdata->num_subdevs && i <= MAX1586_V6; i++) {
+ struct regulator_dev *rdev;
+
+ 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);
+ return -EINVAL;
+ }
+
+ 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 = devm_regulator_register(&client->dev,
+ &max1586_reg[id], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&client->dev, "failed to register %s\n",
+ max1586_reg[id].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ i2c_set_clientdata(client, max1586);
+ dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n");
+ 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,
+ .driver = {
+ .name = "max1586",
+ .of_match_table = of_match_ptr(max1586_of_match),
+ },
+ .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/max77620-regulator.c b/drivers/regulator/max77620-regulator.c
new file mode 100644
index 000000000..28baeac3a
--- /dev/null
+++ b/drivers/regulator/max77620-regulator.c
@@ -0,0 +1,907 @@
+/*
+ * Maxim MAX77620 Regulator driver
+ *
+ * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Mallikarjun Kasoju <mkasoju@nvidia.com>
+ * Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/mfd/max77620.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define max77620_rails(_name) "max77620-"#_name
+
+/* Power Mode */
+#define MAX77620_POWER_MODE_NORMAL 3
+#define MAX77620_POWER_MODE_LPM 2
+#define MAX77620_POWER_MODE_GLPM 1
+#define MAX77620_POWER_MODE_DISABLE 0
+
+/* SD Slew Rate */
+#define MAX77620_SD_SR_13_75 0
+#define MAX77620_SD_SR_27_5 1
+#define MAX77620_SD_SR_55 2
+#define MAX77620_SD_SR_100 3
+
+enum max77620_regulators {
+ MAX77620_REGULATOR_ID_SD0,
+ MAX77620_REGULATOR_ID_SD1,
+ MAX77620_REGULATOR_ID_SD2,
+ MAX77620_REGULATOR_ID_SD3,
+ MAX77620_REGULATOR_ID_SD4,
+ MAX77620_REGULATOR_ID_LDO0,
+ MAX77620_REGULATOR_ID_LDO1,
+ MAX77620_REGULATOR_ID_LDO2,
+ MAX77620_REGULATOR_ID_LDO3,
+ MAX77620_REGULATOR_ID_LDO4,
+ MAX77620_REGULATOR_ID_LDO5,
+ MAX77620_REGULATOR_ID_LDO6,
+ MAX77620_REGULATOR_ID_LDO7,
+ MAX77620_REGULATOR_ID_LDO8,
+ MAX77620_NUM_REGS,
+};
+
+/* Regulator types */
+enum max77620_regulator_type {
+ MAX77620_REGULATOR_TYPE_SD,
+ MAX77620_REGULATOR_TYPE_LDO_N,
+ MAX77620_REGULATOR_TYPE_LDO_P,
+};
+
+struct max77620_regulator_info {
+ u8 type;
+ u8 fps_addr;
+ u8 volt_addr;
+ u8 cfg_addr;
+ u8 power_mode_mask;
+ u8 power_mode_shift;
+ u8 remote_sense_addr;
+ u8 remote_sense_mask;
+ struct regulator_desc desc;
+};
+
+struct max77620_regulator_pdata {
+ int active_fps_src;
+ int active_fps_pd_slot;
+ int active_fps_pu_slot;
+ int suspend_fps_src;
+ int suspend_fps_pd_slot;
+ int suspend_fps_pu_slot;
+ int current_mode;
+ int power_ok;
+ int ramp_rate_setting;
+};
+
+struct max77620_regulator {
+ struct device *dev;
+ struct regmap *rmap;
+ struct max77620_regulator_info *rinfo[MAX77620_NUM_REGS];
+ struct max77620_regulator_pdata reg_pdata[MAX77620_NUM_REGS];
+ int enable_power_mode[MAX77620_NUM_REGS];
+ int current_power_mode[MAX77620_NUM_REGS];
+ int active_fps_src[MAX77620_NUM_REGS];
+};
+
+#define fps_src_name(fps_src) \
+ (fps_src == MAX77620_FPS_SRC_0 ? "FPS_SRC_0" : \
+ fps_src == MAX77620_FPS_SRC_1 ? "FPS_SRC_1" : \
+ fps_src == MAX77620_FPS_SRC_2 ? "FPS_SRC_2" : "FPS_SRC_NONE")
+
+static int max77620_regulator_get_fps_src(struct max77620_regulator *pmic,
+ int id)
+{
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pmic->rmap, rinfo->fps_addr, &val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x read failed %d\n",
+ rinfo->fps_addr, ret);
+ return ret;
+ }
+
+ return (val & MAX77620_FPS_SRC_MASK) >> MAX77620_FPS_SRC_SHIFT;
+}
+
+static int max77620_regulator_set_fps_src(struct max77620_regulator *pmic,
+ int fps_src, int id)
+{
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ unsigned int val;
+ int ret;
+
+ if (!rinfo)
+ return 0;
+
+ switch (fps_src) {
+ case MAX77620_FPS_SRC_0:
+ case MAX77620_FPS_SRC_1:
+ case MAX77620_FPS_SRC_2:
+ case MAX77620_FPS_SRC_NONE:
+ break;
+
+ case MAX77620_FPS_SRC_DEF:
+ ret = regmap_read(pmic->rmap, rinfo->fps_addr, &val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x read failed %d\n",
+ rinfo->fps_addr, ret);
+ return ret;
+ }
+ ret = (val & MAX77620_FPS_SRC_MASK) >> MAX77620_FPS_SRC_SHIFT;
+ pmic->active_fps_src[id] = ret;
+ return 0;
+
+ default:
+ dev_err(pmic->dev, "Invalid FPS %d for regulator %d\n",
+ fps_src, id);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(pmic->rmap, rinfo->fps_addr,
+ MAX77620_FPS_SRC_MASK,
+ fps_src << MAX77620_FPS_SRC_SHIFT);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x update failed %d\n",
+ rinfo->fps_addr, ret);
+ return ret;
+ }
+ pmic->active_fps_src[id] = fps_src;
+
+ return 0;
+}
+
+static int max77620_regulator_set_fps_slots(struct max77620_regulator *pmic,
+ int id, bool is_suspend)
+{
+ struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int pu = rpdata->active_fps_pu_slot;
+ int pd = rpdata->active_fps_pd_slot;
+ int ret = 0;
+
+ if (!rinfo)
+ return 0;
+
+ if (is_suspend) {
+ pu = rpdata->suspend_fps_pu_slot;
+ pd = rpdata->suspend_fps_pd_slot;
+ }
+
+ /* FPS power up period setting */
+ if (pu >= 0) {
+ val |= (pu << MAX77620_FPS_PU_PERIOD_SHIFT);
+ mask |= MAX77620_FPS_PU_PERIOD_MASK;
+ }
+
+ /* FPS power down period setting */
+ if (pd >= 0) {
+ val |= (pd << MAX77620_FPS_PD_PERIOD_SHIFT);
+ mask |= MAX77620_FPS_PD_PERIOD_MASK;
+ }
+
+ if (mask) {
+ ret = regmap_update_bits(pmic->rmap, rinfo->fps_addr,
+ mask, val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x update failed: %d\n",
+ rinfo->fps_addr, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int max77620_regulator_set_power_mode(struct max77620_regulator *pmic,
+ int power_mode, int id)
+{
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ u8 mask = rinfo->power_mode_mask;
+ u8 shift = rinfo->power_mode_shift;
+ u8 addr;
+ int ret;
+
+ switch (rinfo->type) {
+ case MAX77620_REGULATOR_TYPE_SD:
+ addr = rinfo->cfg_addr;
+ break;
+ default:
+ addr = rinfo->volt_addr;
+ break;
+ }
+
+ ret = regmap_update_bits(pmic->rmap, addr, mask, power_mode << shift);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Regulator %d mode set failed: %d\n",
+ id, ret);
+ return ret;
+ }
+ pmic->current_power_mode[id] = power_mode;
+
+ return ret;
+}
+
+static int max77620_regulator_get_power_mode(struct max77620_regulator *pmic,
+ int id)
+{
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ unsigned int val, addr;
+ u8 mask = rinfo->power_mode_mask;
+ u8 shift = rinfo->power_mode_shift;
+ int ret;
+
+ switch (rinfo->type) {
+ case MAX77620_REGULATOR_TYPE_SD:
+ addr = rinfo->cfg_addr;
+ break;
+ default:
+ addr = rinfo->volt_addr;
+ break;
+ }
+
+ ret = regmap_read(pmic->rmap, addr, &val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Regulator %d: Reg 0x%02x read failed: %d\n",
+ id, addr, ret);
+ return ret;
+ }
+
+ return (val & mask) >> shift;
+}
+
+static int max77620_read_slew_rate(struct max77620_regulator *pmic, int id)
+{
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ unsigned int rval;
+ int slew_rate;
+ int ret;
+
+ ret = regmap_read(pmic->rmap, rinfo->cfg_addr, &rval);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Register 0x%02x read failed: %d\n",
+ rinfo->cfg_addr, ret);
+ return ret;
+ }
+
+ switch (rinfo->type) {
+ case MAX77620_REGULATOR_TYPE_SD:
+ slew_rate = (rval >> MAX77620_SD_SR_SHIFT) & 0x3;
+ switch (slew_rate) {
+ case 0:
+ slew_rate = 13750;
+ break;
+ case 1:
+ slew_rate = 27500;
+ break;
+ case 2:
+ slew_rate = 55000;
+ break;
+ case 3:
+ slew_rate = 100000;
+ break;
+ }
+ rinfo->desc.ramp_delay = slew_rate;
+ break;
+ default:
+ slew_rate = rval & 0x1;
+ switch (slew_rate) {
+ case 0:
+ slew_rate = 100000;
+ break;
+ case 1:
+ slew_rate = 5000;
+ break;
+ }
+ rinfo->desc.ramp_delay = slew_rate;
+ break;
+ }
+
+ return 0;
+}
+
+static int max77620_set_slew_rate(struct max77620_regulator *pmic, int id,
+ int slew_rate)
+{
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ unsigned int val;
+ int ret;
+ u8 mask;
+
+ if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) {
+ if (slew_rate <= 13750)
+ val = 0;
+ else if (slew_rate <= 27500)
+ val = 1;
+ else if (slew_rate <= 55000)
+ val = 2;
+ else
+ val = 3;
+ val <<= MAX77620_SD_SR_SHIFT;
+ mask = MAX77620_SD_SR_MASK;
+ } else {
+ if (slew_rate <= 5000)
+ val = 1;
+ else
+ val = 0;
+ mask = MAX77620_LDO_SLEW_RATE_MASK;
+ }
+
+ ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr, mask, val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Regulator %d slew rate set failed: %d\n",
+ id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77620_config_power_ok(struct max77620_regulator *pmic, int id)
+{
+ struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ struct max77620_chip *chip = dev_get_drvdata(pmic->dev->parent);
+ u8 val, mask;
+ int ret;
+
+ switch (chip->chip_id) {
+ case MAX20024:
+ if (rpdata->power_ok >= 0) {
+ if (rinfo->type == MAX77620_REGULATOR_TYPE_SD)
+ mask = MAX20024_SD_CFG1_MPOK_MASK;
+ else
+ mask = MAX20024_LDO_CFG2_MPOK_MASK;
+
+ val = rpdata->power_ok ? mask : 0;
+
+ ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr,
+ mask, val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x update failed %d\n",
+ rinfo->cfg_addr, ret);
+ return ret;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int max77620_init_pmic(struct max77620_regulator *pmic, int id)
+{
+ struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
+ int ret;
+
+ max77620_config_power_ok(pmic, id);
+
+ /* Update power mode */
+ ret = max77620_regulator_get_power_mode(pmic, id);
+ if (ret < 0)
+ return ret;
+
+ pmic->current_power_mode[id] = ret;
+ pmic->enable_power_mode[id] = MAX77620_POWER_MODE_NORMAL;
+
+ if (rpdata->active_fps_src == MAX77620_FPS_SRC_DEF) {
+ ret = max77620_regulator_get_fps_src(pmic, id);
+ if (ret < 0)
+ return ret;
+ rpdata->active_fps_src = ret;
+ }
+
+ /* If rails are externally control of FPS then enable it always. */
+ if (rpdata->active_fps_src == MAX77620_FPS_SRC_NONE) {
+ ret = max77620_regulator_set_power_mode(pmic,
+ pmic->enable_power_mode[id], id);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (pmic->current_power_mode[id] !=
+ pmic->enable_power_mode[id]) {
+ ret = max77620_regulator_set_power_mode(pmic,
+ pmic->enable_power_mode[id], id);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ ret = max77620_regulator_set_fps_src(pmic, rpdata->active_fps_src, id);
+ if (ret < 0)
+ return ret;
+
+ ret = max77620_regulator_set_fps_slots(pmic, id, false);
+ if (ret < 0)
+ return ret;
+
+ if (rpdata->ramp_rate_setting) {
+ ret = max77620_set_slew_rate(pmic, id,
+ rpdata->ramp_rate_setting);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77620_regulator_enable(struct regulator_dev *rdev)
+{
+ struct max77620_regulator *pmic = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+
+ if (pmic->active_fps_src[id] != MAX77620_FPS_SRC_NONE)
+ return 0;
+
+ return max77620_regulator_set_power_mode(pmic,
+ pmic->enable_power_mode[id], id);
+}
+
+static int max77620_regulator_disable(struct regulator_dev *rdev)
+{
+ struct max77620_regulator *pmic = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+
+ if (pmic->active_fps_src[id] != MAX77620_FPS_SRC_NONE)
+ return 0;
+
+ return max77620_regulator_set_power_mode(pmic,
+ MAX77620_POWER_MODE_DISABLE, id);
+}
+
+static int max77620_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct max77620_regulator *pmic = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int ret = 1;
+
+ if (pmic->active_fps_src[id] != MAX77620_FPS_SRC_NONE)
+ return 1;
+
+ ret = max77620_regulator_get_power_mode(pmic, id);
+ if (ret < 0)
+ return ret;
+
+ if (ret != MAX77620_POWER_MODE_DISABLE)
+ return 1;
+
+ return 0;
+}
+
+static int max77620_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct max77620_regulator *pmic = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
+ bool fpwm = false;
+ int power_mode;
+ int ret;
+ u8 val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ fpwm = true;
+ power_mode = MAX77620_POWER_MODE_NORMAL;
+ break;
+
+ case REGULATOR_MODE_NORMAL:
+ power_mode = MAX77620_POWER_MODE_NORMAL;
+ break;
+
+ case REGULATOR_MODE_IDLE:
+ power_mode = MAX77620_POWER_MODE_LPM;
+ break;
+
+ default:
+ dev_err(pmic->dev, "Regulator %d mode %d is invalid\n",
+ id, mode);
+ return -EINVAL;
+ }
+
+ if (rinfo->type != MAX77620_REGULATOR_TYPE_SD)
+ goto skip_fpwm;
+
+ val = (fpwm) ? MAX77620_SD_FPWM_MASK : 0;
+ ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr,
+ MAX77620_SD_FPWM_MASK, val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x update failed: %d\n",
+ rinfo->cfg_addr, ret);
+ return ret;
+ }
+ rpdata->current_mode = mode;
+
+skip_fpwm:
+ ret = max77620_regulator_set_power_mode(pmic, power_mode, id);
+ if (ret < 0)
+ return ret;
+
+ pmic->enable_power_mode[id] = power_mode;
+
+ return 0;
+}
+
+static unsigned int max77620_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct max77620_regulator *pmic = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct max77620_regulator_info *rinfo = pmic->rinfo[id];
+ int fpwm = 0;
+ int ret;
+ int pm_mode, reg_mode;
+ unsigned int val;
+
+ ret = max77620_regulator_get_power_mode(pmic, id);
+ if (ret < 0)
+ return 0;
+
+ pm_mode = ret;
+
+ if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) {
+ ret = regmap_read(pmic->rmap, rinfo->cfg_addr, &val);
+ if (ret < 0) {
+ dev_err(pmic->dev, "Reg 0x%02x read failed: %d\n",
+ rinfo->cfg_addr, ret);
+ return ret;
+ }
+ fpwm = !!(val & MAX77620_SD_FPWM_MASK);
+ }
+
+ switch (pm_mode) {
+ case MAX77620_POWER_MODE_NORMAL:
+ case MAX77620_POWER_MODE_DISABLE:
+ if (fpwm)
+ reg_mode = REGULATOR_MODE_FAST;
+ else
+ reg_mode = REGULATOR_MODE_NORMAL;
+ break;
+ case MAX77620_POWER_MODE_LPM:
+ case MAX77620_POWER_MODE_GLPM:
+ reg_mode = REGULATOR_MODE_IDLE;
+ break;
+ default:
+ return 0;
+ }
+
+ return reg_mode;
+}
+
+static int max77620_regulator_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ struct max77620_regulator *pmic = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id];
+
+ /* Device specific ramp rate setting tells that platform has
+ * different ramp rate from advertised value. In this case,
+ * do not configure anything and just return success.
+ */
+ if (rpdata->ramp_rate_setting)
+ return 0;
+
+ return max77620_set_slew_rate(pmic, id, ramp_delay);
+}
+
+static int max77620_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct max77620_regulator *pmic = config->driver_data;
+ struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[desc->id];
+ u32 pval;
+ int ret;
+
+ ret = of_property_read_u32(np, "maxim,active-fps-source", &pval);
+ rpdata->active_fps_src = (!ret) ? pval : MAX77620_FPS_SRC_DEF;
+
+ ret = of_property_read_u32(np, "maxim,active-fps-power-up-slot", &pval);
+ rpdata->active_fps_pu_slot = (!ret) ? pval : -1;
+
+ ret = of_property_read_u32(
+ np, "maxim,active-fps-power-down-slot", &pval);
+ rpdata->active_fps_pd_slot = (!ret) ? pval : -1;
+
+ ret = of_property_read_u32(np, "maxim,suspend-fps-source", &pval);
+ rpdata->suspend_fps_src = (!ret) ? pval : -1;
+
+ ret = of_property_read_u32(
+ np, "maxim,suspend-fps-power-up-slot", &pval);
+ rpdata->suspend_fps_pu_slot = (!ret) ? pval : -1;
+
+ ret = of_property_read_u32(
+ np, "maxim,suspend-fps-power-down-slot", &pval);
+ rpdata->suspend_fps_pd_slot = (!ret) ? pval : -1;
+
+ ret = of_property_read_u32(np, "maxim,power-ok-control", &pval);
+ if (!ret)
+ rpdata->power_ok = pval;
+ else
+ rpdata->power_ok = -1;
+
+ ret = of_property_read_u32(np, "maxim,ramp-rate-setting", &pval);
+ rpdata->ramp_rate_setting = (!ret) ? pval : 0;
+
+ return max77620_init_pmic(pmic, desc->id);
+}
+
+static const struct regulator_ops max77620_regulator_ops = {
+ .is_enabled = max77620_regulator_is_enabled,
+ .enable = max77620_regulator_enable,
+ .disable = max77620_regulator_disable,
+ .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_mode = max77620_regulator_set_mode,
+ .get_mode = max77620_regulator_get_mode,
+ .set_ramp_delay = max77620_regulator_set_ramp_delay,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+#define MAX77620_SD_CNF2_ROVS_EN_NONE 0
+#define RAIL_SD(_id, _name, _sname, _volt_mask, _min_uV, _max_uV, \
+ _step_uV, _rs_add, _rs_mask) \
+ [MAX77620_REGULATOR_ID_##_id] = { \
+ .type = MAX77620_REGULATOR_TYPE_SD, \
+ .volt_addr = MAX77620_REG_##_id, \
+ .cfg_addr = MAX77620_REG_##_id##_CFG, \
+ .fps_addr = MAX77620_REG_FPS_##_id, \
+ .remote_sense_addr = _rs_add, \
+ .remote_sense_mask = MAX77620_SD_CNF2_ROVS_EN_##_rs_mask, \
+ .power_mode_mask = MAX77620_SD_POWER_MODE_MASK, \
+ .power_mode_shift = MAX77620_SD_POWER_MODE_SHIFT, \
+ .desc = { \
+ .name = max77620_rails(_name), \
+ .of_match = of_match_ptr(#_name), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = max77620_of_parse_cb, \
+ .supply_name = _sname, \
+ .id = MAX77620_REGULATOR_ID_##_id, \
+ .ops = &max77620_regulator_ops, \
+ .n_voltages = ((_max_uV - _min_uV) / _step_uV) + 1, \
+ .min_uV = _min_uV, \
+ .uV_step = _step_uV, \
+ .enable_time = 500, \
+ .vsel_mask = MAX77620_##_volt_mask##_VOLT_MASK, \
+ .vsel_reg = MAX77620_REG_##_id, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = MAX77620_SD_CFG1_ADE_ENABLE, \
+ .active_discharge_mask = MAX77620_SD_CFG1_ADE_MASK, \
+ .active_discharge_reg = MAX77620_REG_##_id##_CFG, \
+ .type = REGULATOR_VOLTAGE, \
+ }, \
+ }
+
+#define RAIL_LDO(_id, _name, _sname, _type, _min_uV, _max_uV, _step_uV) \
+ [MAX77620_REGULATOR_ID_##_id] = { \
+ .type = MAX77620_REGULATOR_TYPE_LDO_##_type, \
+ .volt_addr = MAX77620_REG_##_id##_CFG, \
+ .cfg_addr = MAX77620_REG_##_id##_CFG2, \
+ .fps_addr = MAX77620_REG_FPS_##_id, \
+ .remote_sense_addr = 0xFF, \
+ .power_mode_mask = MAX77620_LDO_POWER_MODE_MASK, \
+ .power_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, \
+ .desc = { \
+ .name = max77620_rails(_name), \
+ .of_match = of_match_ptr(#_name), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = max77620_of_parse_cb, \
+ .supply_name = _sname, \
+ .id = MAX77620_REGULATOR_ID_##_id, \
+ .ops = &max77620_regulator_ops, \
+ .n_voltages = ((_max_uV - _min_uV) / _step_uV) + 1, \
+ .min_uV = _min_uV, \
+ .uV_step = _step_uV, \
+ .enable_time = 500, \
+ .vsel_mask = MAX77620_LDO_VOLT_MASK, \
+ .vsel_reg = MAX77620_REG_##_id##_CFG, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = MAX77620_LDO_CFG2_ADE_ENABLE, \
+ .active_discharge_mask = MAX77620_LDO_CFG2_ADE_MASK, \
+ .active_discharge_reg = MAX77620_REG_##_id##_CFG2, \
+ .type = REGULATOR_VOLTAGE, \
+ }, \
+ }
+
+static struct max77620_regulator_info max77620_regs_info[MAX77620_NUM_REGS] = {
+ RAIL_SD(SD0, sd0, "in-sd0", SD0, 600000, 1400000, 12500, 0x22, SD0),
+ RAIL_SD(SD1, sd1, "in-sd1", SD1, 600000, 1550000, 12500, 0x22, SD1),
+ RAIL_SD(SD2, sd2, "in-sd2", SDX, 600000, 3787500, 12500, 0xFF, NONE),
+ RAIL_SD(SD3, sd3, "in-sd3", SDX, 600000, 3787500, 12500, 0xFF, NONE),
+
+ RAIL_LDO(LDO0, ldo0, "in-ldo0-1", N, 800000, 2375000, 25000),
+ RAIL_LDO(LDO1, ldo1, "in-ldo0-1", N, 800000, 2375000, 25000),
+ RAIL_LDO(LDO2, ldo2, "in-ldo2", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO3, ldo3, "in-ldo3-5", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO4, ldo4, "in-ldo4-6", P, 800000, 1587500, 12500),
+ RAIL_LDO(LDO5, ldo5, "in-ldo3-5", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO6, ldo6, "in-ldo4-6", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO7, ldo7, "in-ldo7-8", N, 800000, 3950000, 50000),
+ RAIL_LDO(LDO8, ldo8, "in-ldo7-8", N, 800000, 3950000, 50000),
+};
+
+static struct max77620_regulator_info max20024_regs_info[MAX77620_NUM_REGS] = {
+ RAIL_SD(SD0, sd0, "in-sd0", SD0, 800000, 1587500, 12500, 0x22, SD0),
+ RAIL_SD(SD1, sd1, "in-sd1", SD1, 600000, 3387500, 12500, 0x22, SD1),
+ RAIL_SD(SD2, sd2, "in-sd2", SDX, 600000, 3787500, 12500, 0xFF, NONE),
+ RAIL_SD(SD3, sd3, "in-sd3", SDX, 600000, 3787500, 12500, 0xFF, NONE),
+ RAIL_SD(SD4, sd4, "in-sd4", SDX, 600000, 3787500, 12500, 0xFF, NONE),
+
+ RAIL_LDO(LDO0, ldo0, "in-ldo0-1", N, 800000, 2375000, 25000),
+ RAIL_LDO(LDO1, ldo1, "in-ldo0-1", N, 800000, 2375000, 25000),
+ RAIL_LDO(LDO2, ldo2, "in-ldo2", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO3, ldo3, "in-ldo3-5", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO4, ldo4, "in-ldo4-6", P, 800000, 1587500, 12500),
+ RAIL_LDO(LDO5, ldo5, "in-ldo3-5", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO6, ldo6, "in-ldo4-6", P, 800000, 3950000, 50000),
+ RAIL_LDO(LDO7, ldo7, "in-ldo7-8", N, 800000, 3950000, 50000),
+ RAIL_LDO(LDO8, ldo8, "in-ldo7-8", N, 800000, 3950000, 50000),
+};
+
+static int max77620_regulator_probe(struct platform_device *pdev)
+{
+ struct max77620_chip *max77620_chip = dev_get_drvdata(pdev->dev.parent);
+ struct max77620_regulator_info *rinfo;
+ struct device *dev = &pdev->dev;
+ struct regulator_config config = { };
+ struct max77620_regulator *pmic;
+ int ret = 0;
+ int id;
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pmic);
+ pmic->dev = dev;
+ pmic->rmap = max77620_chip->rmap;
+ if (!dev->of_node)
+ dev->of_node = pdev->dev.parent->of_node;
+
+ switch (max77620_chip->chip_id) {
+ case MAX77620:
+ rinfo = max77620_regs_info;
+ break;
+ default:
+ rinfo = max20024_regs_info;
+ break;
+ }
+
+ config.regmap = pmic->rmap;
+ config.dev = dev;
+ config.driver_data = pmic;
+
+ /*
+ * Set of_node_reuse flag to prevent driver core from attempting to
+ * claim any pinmux resources already claimed by the parent device.
+ * Otherwise PMIC driver will fail to re-probe.
+ */
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+ for (id = 0; id < MAX77620_NUM_REGS; id++) {
+ struct regulator_dev *rdev;
+ struct regulator_desc *rdesc;
+
+ if ((max77620_chip->chip_id == MAX77620) &&
+ (id == MAX77620_REGULATOR_ID_SD4))
+ continue;
+
+ rdesc = &rinfo[id].desc;
+ pmic->rinfo[id] = &max77620_regs_info[id];
+ pmic->enable_power_mode[id] = MAX77620_POWER_MODE_NORMAL;
+ pmic->reg_pdata[id].active_fps_src = -1;
+ pmic->reg_pdata[id].active_fps_pd_slot = -1;
+ pmic->reg_pdata[id].active_fps_pu_slot = -1;
+ pmic->reg_pdata[id].suspend_fps_src = -1;
+ pmic->reg_pdata[id].suspend_fps_pd_slot = -1;
+ pmic->reg_pdata[id].suspend_fps_pu_slot = -1;
+ pmic->reg_pdata[id].power_ok = -1;
+ pmic->reg_pdata[id].ramp_rate_setting = -1;
+
+ ret = max77620_read_slew_rate(pmic, id);
+ if (ret < 0)
+ return ret;
+
+ rdev = devm_regulator_register(dev, rdesc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "Regulator registration %s failed: %d\n",
+ rdesc->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77620_regulator_suspend(struct device *dev)
+{
+ struct max77620_regulator *pmic = dev_get_drvdata(dev);
+ struct max77620_regulator_pdata *reg_pdata;
+ int id;
+
+ for (id = 0; id < MAX77620_NUM_REGS; id++) {
+ reg_pdata = &pmic->reg_pdata[id];
+
+ max77620_regulator_set_fps_slots(pmic, id, true);
+ if (reg_pdata->suspend_fps_src < 0)
+ continue;
+
+ max77620_regulator_set_fps_src(pmic, reg_pdata->suspend_fps_src,
+ id);
+ }
+
+ return 0;
+}
+
+static int max77620_regulator_resume(struct device *dev)
+{
+ struct max77620_regulator *pmic = dev_get_drvdata(dev);
+ struct max77620_regulator_pdata *reg_pdata;
+ int id;
+
+ for (id = 0; id < MAX77620_NUM_REGS; id++) {
+ reg_pdata = &pmic->reg_pdata[id];
+
+ max77620_config_power_ok(pmic, id);
+
+ max77620_regulator_set_fps_slots(pmic, id, false);
+ if (reg_pdata->active_fps_src < 0)
+ continue;
+ max77620_regulator_set_fps_src(pmic, reg_pdata->active_fps_src,
+ id);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max77620_regulator_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(max77620_regulator_suspend,
+ max77620_regulator_resume)
+};
+
+static const struct platform_device_id max77620_regulator_devtype[] = {
+ { .name = "max77620-pmic", },
+ { .name = "max20024-pmic", },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, max77620_regulator_devtype);
+
+static struct platform_driver max77620_regulator_driver = {
+ .probe = max77620_regulator_probe,
+ .id_table = max77620_regulator_devtype,
+ .driver = {
+ .name = "max77620-pmic",
+ .pm = &max77620_regulator_pm_ops,
+ },
+};
+
+module_platform_driver(max77620_regulator_driver);
+
+MODULE_DESCRIPTION("MAX77620/MAX20024 regulator driver");
+MODULE_AUTHOR("Mallikarjun Kasoju <mkasoju@nvidia.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c
new file mode 100644
index 000000000..bee060937
--- /dev/null
+++ b/drivers/regulator/max77686-regulator.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max77686.c - Regulator driver for the Maxim 77686
+//
+// Copyright (C) 2012 Samsung Electronics
+// Chiwoong Byun <woong.byun@samsung.com>
+// Jonghwa Lee <jonghwa3.lee@samsung.com>
+//
+// This driver is based on max8997.c
+
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+#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_BUCK_ENABLE_TIME 40 /* us */
+#define MAX77686_DVS_ENABLE_TIME 22 /* us */
+#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
+
+/*
+ * Value for configuring buck[89] and LDO{20,21,22} as GPIO control.
+ * It is the same as 'off' for other regulators.
+ */
+#define MAX77686_GPIO_CONTROL 0x0
+/*
+ * Values used for configuring LDOs and bucks.
+ * Forcing low power mode: LDO1, 3-5, 9, 13, 17-26
+ */
+#define MAX77686_LDO_LOWPOWER 0x1
+/*
+ * On/off controlled by PWRREQ:
+ * - LDO2, 6-8, 10-12, 14-16
+ * - buck[1234]
+ */
+#define MAX77686_OFF_PWRREQ 0x1
+/* Low power mode controlled by PWRREQ: All LDOs */
+#define MAX77686_LDO_LOWPOWER_PWRREQ 0x2
+/* Forcing low power mode: buck[234] */
+#define MAX77686_BUCK_LOWPOWER 0x2
+#define MAX77686_NORMAL 0x3
+
+#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 {
+ DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);
+
+ /* Array indexed by regulator id */
+ unsigned int opmode[MAX77686_REGULATORS];
+};
+
+static unsigned int max77686_get_opmode_shift(int id)
+{
+ switch (id) {
+ case MAX77686_BUCK1:
+ case MAX77686_BUCK5 ... MAX77686_BUCK9:
+ return 0;
+ case MAX77686_BUCK2 ... MAX77686_BUCK4:
+ return MAX77686_OPMODE_BUCK234_SHIFT;
+ default:
+ /* all LDOs */
+ return MAX77686_OPMODE_SHIFT;
+ }
+}
+
+/*
+ * When regulator is configured for GPIO control then it
+ * replaces "normal" mode. Any change from low power mode to normal
+ * should actually change to GPIO control.
+ * Map normal mode to proper value for such regulators.
+ */
+static unsigned int max77686_map_normal_mode(struct max77686_data *max77686,
+ int id)
+{
+ switch (id) {
+ case MAX77686_BUCK8:
+ case MAX77686_BUCK9:
+ case MAX77686_LDO20 ... MAX77686_LDO22:
+ if (test_bit(id, max77686->gpio_enabled))
+ return MAX77686_GPIO_CONTROL;
+ }
+
+ return MAX77686_NORMAL;
+}
+
+/* Some BUCKs and LDOs supports Normal[ON/OFF] mode during suspend */
+static int max77686_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int val, shift;
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ int ret, id = rdev_get_id(rdev);
+
+ shift = max77686_get_opmode_shift(id);
+ val = MAX77686_OFF_PWRREQ;
+
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val << shift);
+ 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 = MAX77686_LDO_LOWPOWER_PWRREQ;
+ break;
+ case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
+ val = max77686_map_normal_mode(max77686, id);
+ 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 << MAX77686_OPMODE_SHIFT);
+ 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, id = rdev_get_id(rdev);
+
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY: /* switch off */
+ val = MAX77686_OFF_PWRREQ;
+ break;
+ case REGULATOR_MODE_IDLE: /* ON in LP Mode */
+ val = MAX77686_LDO_LOWPOWER_PWRREQ;
+ break;
+ case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
+ val = max77686_map_normal_mode(max77686, id);
+ 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 << MAX77686_OPMODE_SHIFT);
+ if (ret)
+ return ret;
+
+ max77686->opmode[id] = val;
+ return 0;
+}
+
+static int max77686_enable(struct regulator_dev *rdev)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ unsigned int shift;
+ int id = rdev_get_id(rdev);
+
+ shift = max77686_get_opmode_shift(id);
+
+ if (max77686->opmode[id] == MAX77686_OFF_PWRREQ)
+ max77686->opmode[id] = max77686_map_normal_mode(max77686, id);
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ max77686->opmode[id] << shift);
+}
+
+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 int max77686_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct max77686_data *max77686 = config->driver_data;
+
+ switch (desc->id) {
+ case MAX77686_BUCK8:
+ case MAX77686_BUCK9:
+ case MAX77686_LDO20 ... MAX77686_LDO22:
+ config->ena_gpio = of_get_named_gpio(np,
+ "maxim,ena-gpios", 0);
+ config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config->ena_gpio_initialized = true;
+ break;
+ default:
+ return 0;
+ }
+
+ if (gpio_is_valid(config->ena_gpio)) {
+ set_bit(desc->id, max77686->gpio_enabled);
+
+ return regmap_update_bits(config->regmap, desc->enable_reg,
+ desc->enable_mask,
+ MAX77686_GPIO_CONTROL);
+ }
+
+ return 0;
+}
+
+static const 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 const 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,
+ .set_suspend_disable = max77686_set_suspend_disable,
+};
+
+static const 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_set_suspend_disable,
+};
+
+static const 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_set_suspend_disable,
+};
+
+#define regulator_desc_ldo(num) { \
+ .name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .of_parse_cb = max77686_of_parse_cb, \
+ .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, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .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, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .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, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .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, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .of_parse_cb = max77686_of_parse_cb, \
+ .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, \
+ .enable_time = MAX77686_BUCK_ENABLE_TIME, \
+ .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, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .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, \
+ .enable_time = MAX77686_BUCK_ENABLE_TIME, \
+ .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, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
+ .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, \
+ .enable_time = MAX77686_DVS_ENABLE_TIME, \
+ .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 const 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),
+};
+
+static int max77686_pmic_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max77686_data *max77686;
+ int i;
+ struct regulator_config config = { };
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data),
+ GFP_KERNEL);
+ if (!max77686)
+ return -ENOMEM;
+
+ config.dev = iodev->dev;
+ config.regmap = iodev->regmap;
+ config.driver_data = max77686;
+ platform_set_drvdata(pdev, max77686);
+
+ for (i = 0; i < MAX77686_REGULATORS; i++) {
+ struct regulator_dev *rdev;
+ int id = regulators[i].id;
+
+ max77686->opmode[id] = MAX77686_NORMAL;
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ int ret = PTR_ERR(rdev);
+ dev_err(&pdev->dev,
+ "regulator init failed for %d: %d\n", i, ret);
+ return ret;
+ }
+ }
+
+ 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",
+ },
+ .probe = max77686_pmic_probe,
+ .id_table = max77686_pmic_id,
+};
+
+module_platform_driver(max77686_pmic_driver);
+
+MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c
new file mode 100644
index 000000000..077ecbbfd
--- /dev/null
+++ b/drivers/regulator/max77693-regulator.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max77693.c - Regulator driver for the Maxim 77693 and 77843
+//
+// Copyright (C) 2013-2015 Samsung Electronics
+// Jonghwa Lee <jonghwa3.lee@samsung.com>
+// Krzysztof Kozlowski <krzk@kernel.org>
+//
+// This driver is based on max77686.c
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-common.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/mfd/max77843-private.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+
+/*
+ * ID for MAX77843 regulators.
+ * There is no need for such for MAX77693.
+ */
+enum max77843_regulator_type {
+ MAX77843_SAFEOUT1 = 0,
+ MAX77843_SAFEOUT2,
+ MAX77843_CHARGER,
+
+ MAX77843_NUM,
+};
+
+/* Register differences between chargers: MAX77693 and MAX77843 */
+struct chg_reg_data {
+ unsigned int linear_reg;
+ unsigned int linear_mask;
+ unsigned int uA_step;
+ unsigned int min_sel;
+};
+
+/*
+ * MAX77693 CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA
+ * 0x00, 0x01, 0x2, 0x03 = 60 mA
+ * 0x04 ~ 0x7E = (60 + (X - 3) * 20) mA
+ * Actually for MAX77693 the driver manipulates the maximum input current,
+ * not the fast charge current (output). This should be fixed.
+ *
+ * On MAX77843 the calculation formula is the same (except values).
+ * Fortunately it properly manipulates the fast charge current.
+ */
+static int max77693_chg_get_current_limit(struct regulator_dev *rdev)
+{
+ const struct chg_reg_data *reg_data = rdev_get_drvdata(rdev);
+ unsigned int chg_min_uA = rdev->constraints->min_uA;
+ unsigned int chg_max_uA = rdev->constraints->max_uA;
+ unsigned int reg, sel;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, reg_data->linear_reg, &reg);
+ if (ret < 0)
+ return ret;
+
+ sel = reg & reg_data->linear_mask;
+
+ /* the first four codes for charger current are all 60mA */
+ if (sel <= reg_data->min_sel)
+ sel = 0;
+ else
+ sel -= reg_data->min_sel;
+
+ val = chg_min_uA + reg_data->uA_step * sel;
+ if (val > chg_max_uA)
+ return -EINVAL;
+
+ return val;
+}
+
+static int max77693_chg_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ const struct chg_reg_data *reg_data = rdev_get_drvdata(rdev);
+ unsigned int chg_min_uA = rdev->constraints->min_uA;
+ int sel = 0;
+
+ while (chg_min_uA + reg_data->uA_step * sel < min_uA)
+ sel++;
+
+ if (chg_min_uA + reg_data->uA_step * sel > max_uA)
+ return -EINVAL;
+
+ /* the first four codes for charger current are all 60mA */
+ sel += reg_data->min_sel;
+
+ return regmap_write(rdev->regmap, reg_data->linear_reg, sel);
+}
+/* end of CHARGER regulator ops */
+
+/* Returns regmap suitable for given regulator on chosen device */
+static struct regmap *max77693_get_regmap(enum max77693_types type,
+ struct max77693_dev *max77693,
+ int reg_id)
+{
+ if (type == TYPE_MAX77693)
+ return max77693->regmap;
+
+ /* Else: TYPE_MAX77843 */
+ switch (reg_id) {
+ case MAX77843_SAFEOUT1:
+ case MAX77843_SAFEOUT2:
+ return max77693->regmap;
+ case MAX77843_CHARGER:
+ return max77693->regmap_chg;
+ default:
+ return max77693->regmap;
+ }
+}
+
+static const unsigned int max77693_safeout_table[] = {
+ 4850000,
+ 4900000,
+ 4950000,
+ 3300000,
+};
+
+static const struct regulator_ops max77693_safeout_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .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,
+};
+
+static const struct regulator_ops max77693_charger_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_current_limit = max77693_chg_get_current_limit,
+ .set_current_limit = max77693_chg_set_current_limit,
+};
+
+#define max77693_regulator_desc_esafeout(_num) { \
+ .name = "ESAFEOUT"#_num, \
+ .id = MAX77693_ESAFEOUT##_num, \
+ .of_match = of_match_ptr("ESAFEOUT"#_num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .n_voltages = 4, \
+ .ops = &max77693_safeout_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .volt_table = max77693_safeout_table, \
+ .vsel_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \
+ .vsel_mask = SAFEOUT_CTRL_SAFEOUT##_num##_MASK, \
+ .enable_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \
+ .enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \
+}
+
+static const struct regulator_desc max77693_supported_regulators[] = {
+ max77693_regulator_desc_esafeout(1),
+ max77693_regulator_desc_esafeout(2),
+ {
+ .name = "CHARGER",
+ .id = MAX77693_CHARGER,
+ .of_match = of_match_ptr("CHARGER"),
+ .regulators_node = of_match_ptr("regulators"),
+ .ops = &max77693_charger_ops,
+ .type = REGULATOR_CURRENT,
+ .owner = THIS_MODULE,
+ .enable_reg = MAX77693_CHG_REG_CHG_CNFG_00,
+ .enable_mask = CHG_CNFG_00_CHG_MASK |
+ CHG_CNFG_00_BUCK_MASK,
+ .enable_val = CHG_CNFG_00_CHG_MASK | CHG_CNFG_00_BUCK_MASK,
+ },
+};
+
+static const struct chg_reg_data max77693_chg_reg_data = {
+ .linear_reg = MAX77693_CHG_REG_CHG_CNFG_09,
+ .linear_mask = CHG_CNFG_09_CHGIN_ILIM_MASK,
+ .uA_step = 20000,
+ .min_sel = 3,
+};
+
+#define max77843_regulator_desc_esafeout(num) { \
+ .name = "SAFEOUT" # num, \
+ .id = MAX77843_SAFEOUT ## num, \
+ .ops = &max77693_safeout_ops, \
+ .of_match = of_match_ptr("SAFEOUT" # num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(max77693_safeout_table), \
+ .volt_table = max77693_safeout_table, \
+ .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \
+ .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \
+ .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \
+ .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \
+}
+
+static const struct regulator_desc max77843_supported_regulators[] = {
+ [MAX77843_SAFEOUT1] = max77843_regulator_desc_esafeout(1),
+ [MAX77843_SAFEOUT2] = max77843_regulator_desc_esafeout(2),
+ [MAX77843_CHARGER] = {
+ .name = "CHARGER",
+ .id = MAX77843_CHARGER,
+ .ops = &max77693_charger_ops,
+ .of_match = of_match_ptr("CHARGER"),
+ .regulators_node = of_match_ptr("regulators"),
+ .type = REGULATOR_CURRENT,
+ .owner = THIS_MODULE,
+ .enable_reg = MAX77843_CHG_REG_CHG_CNFG_00,
+ .enable_mask = MAX77843_CHG_MASK,
+ .enable_val = MAX77843_CHG_MASK,
+ },
+};
+
+static const struct chg_reg_data max77843_chg_reg_data = {
+ .linear_reg = MAX77843_CHG_REG_CHG_CNFG_02,
+ .linear_mask = MAX77843_CHG_FAST_CHG_CURRENT_MASK,
+ .uA_step = MAX77843_CHG_FAST_CHG_CURRENT_STEP,
+ .min_sel = 2,
+};
+
+static int max77693_pmic_probe(struct platform_device *pdev)
+{
+ enum max77693_types type = platform_get_device_id(pdev)->driver_data;
+ struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *regulators;
+ unsigned int regulators_size;
+ int i;
+ struct regulator_config config = { };
+
+ config.dev = iodev->dev;
+
+ switch (type) {
+ case TYPE_MAX77693:
+ regulators = max77693_supported_regulators;
+ regulators_size = ARRAY_SIZE(max77693_supported_regulators);
+ config.driver_data = (void *)&max77693_chg_reg_data;
+ break;
+ case TYPE_MAX77843:
+ regulators = max77843_supported_regulators;
+ regulators_size = ARRAY_SIZE(max77843_supported_regulators);
+ config.driver_data = (void *)&max77843_chg_reg_data;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported device type: %u\n", type);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < regulators_size; i++) {
+ struct regulator_dev *rdev;
+
+ config.regmap = max77693_get_regmap(type, iodev,
+ regulators[i].id);
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to initialize regulator-%d\n", i);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id max77693_pmic_id[] = {
+ { "max77693-pmic", TYPE_MAX77693 },
+ { "max77843-regulator", TYPE_MAX77843 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(platform, max77693_pmic_id);
+
+static struct platform_driver max77693_pmic_driver = {
+ .driver = {
+ .name = "max77693-pmic",
+ },
+ .probe = max77693_pmic_probe,
+ .id_table = max77693_pmic_id,
+};
+
+static int __init max77693_pmic_init(void)
+{
+ return platform_driver_register(&max77693_pmic_driver);
+}
+subsys_initcall(max77693_pmic_init);
+
+static void __exit max77693_pmic_cleanup(void)
+{
+ platform_driver_unregister(&max77693_pmic_driver);
+}
+module_exit(max77693_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77693/77843 regulator driver");
+MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
+MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/max77802-regulator.c b/drivers/regulator/max77802-regulator.c
new file mode 100644
index 000000000..c30cf5c9f
--- /dev/null
+++ b/drivers/regulator/max77802-regulator.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max77802.c - Regulator driver for the Maxim 77802
+//
+// Copyright (C) 2013-2014 Google, Inc
+// Simon Glass <sjg@chromium.org>
+//
+// Copyright (C) 2012 Samsung Electronics
+// Chiwoong Byun <woong.byun@samsung.com>
+// Jonghwa Lee <jonghwa3.lee@samsung.com>
+//
+// This driver is based on max8997.c
+
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <dt-bindings/regulator/maxim,max77802.h>
+
+/* Default ramp delay in case it is not manually set */
+#define MAX77802_RAMP_DELAY 100000 /* uV/us */
+
+#define MAX77802_OPMODE_SHIFT_LDO 6
+#define MAX77802_OPMODE_BUCK234_SHIFT 4
+#define MAX77802_OPMODE_MASK 0x3
+
+#define MAX77802_VSEL_MASK 0x3F
+#define MAX77802_DVS_VSEL_MASK 0xFF
+
+#define MAX77802_RAMP_RATE_MASK_2BIT 0xC0
+#define MAX77802_RAMP_RATE_SHIFT_2BIT 6
+#define MAX77802_RAMP_RATE_MASK_4BIT 0xF0
+#define MAX77802_RAMP_RATE_SHIFT_4BIT 4
+
+#define MAX77802_STATUS_OFF 0x0
+#define MAX77802_OFF_PWRREQ 0x1
+#define MAX77802_LP_PWRREQ 0x2
+
+/* MAX77802 has two register formats: 2-bit and 4-bit */
+static const unsigned int ramp_table_77802_2bit[] = {
+ 12500,
+ 25000,
+ 50000,
+ 100000,
+};
+
+static unsigned int ramp_table_77802_4bit[] = {
+ 1000, 2000, 3030, 4000,
+ 5000, 5880, 7140, 8330,
+ 9090, 10000, 11110, 12500,
+ 16670, 25000, 50000, 100000,
+};
+
+struct max77802_regulator_prv {
+ /* Array indexed by regulator id */
+ unsigned int opmode[MAX77802_REG_MAX];
+};
+
+static inline unsigned int max77802_map_mode(unsigned int mode)
+{
+ return mode == MAX77802_OPMODE_NORMAL ?
+ REGULATOR_MODE_NORMAL : REGULATOR_MODE_STANDBY;
+}
+
+static int max77802_get_opmode_shift(int id)
+{
+ if (id == MAX77802_BUCK1 || (id >= MAX77802_BUCK5 &&
+ id <= MAX77802_BUCK10))
+ return 0;
+
+ if (id >= MAX77802_BUCK2 && id <= MAX77802_BUCK4)
+ return MAX77802_OPMODE_BUCK234_SHIFT;
+
+ if (id >= MAX77802_LDO1 && id <= MAX77802_LDO35)
+ return MAX77802_OPMODE_SHIFT_LDO;
+
+ return -EINVAL;
+}
+
+/**
+ * max77802_set_suspend_disable - Disable the regulator during system suspend
+ * @rdev: regulator to mark as disabled
+ *
+ * All regulators expect LDO 1, 3, 20 and 21 support OFF by PWRREQ.
+ * Configure the regulator so the PMIC will turn it OFF during system suspend.
+ */
+static int max77802_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int val = MAX77802_OFF_PWRREQ;
+ struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int shift = max77802_get_opmode_shift(id);
+
+ max77802->opmode[id] = val;
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val << shift);
+}
+
+/*
+ * Some LDOs support Low Power Mode while the system is running.
+ *
+ * LDOs 1, 3, 20, 21.
+ */
+static int max77802_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ unsigned int val;
+ int shift = max77802_get_opmode_shift(id);
+
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY:
+ val = MAX77802_OPMODE_LP; /* ON in Low Power Mode */
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = MAX77802_OPMODE_NORMAL; /* ON in Normal Mode */
+ break;
+ default:
+ dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n",
+ rdev->desc->name, mode);
+ return -EINVAL;
+ }
+
+ max77802->opmode[id] = val;
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val << shift);
+}
+
+static unsigned max77802_get_mode(struct regulator_dev *rdev)
+{
+ struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+
+ return max77802_map_mode(max77802->opmode[id]);
+}
+
+/**
+ * max77802_set_suspend_mode - set regulator opmode when the system is suspended
+ * @rdev: regulator to change mode
+ * @mode: operating mode to be set
+ *
+ * Will set the operating mode for the regulators during system suspend.
+ * This function is valid for the three different enable control logics:
+ *
+ * Enable Control Logic1 by PWRREQ (BUCK 2-4 and LDOs 2, 4-19, 22-35)
+ * Enable Control Logic2 by PWRREQ (LDOs 1, 20, 21)
+ * Enable Control Logic3 by PWRREQ (LDO 3)
+ *
+ * If setting the regulator mode fails, the function only warns but does
+ * not return an error code to avoid the regulator core to stop setting
+ * the operating mode for the remaining regulators.
+ */
+static int max77802_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ unsigned int val;
+ int shift = max77802_get_opmode_shift(id);
+
+ /*
+ * If the regulator has been disabled for suspend
+ * then is invalid to try setting a suspend mode.
+ */
+ if (max77802->opmode[id] == MAX77802_OFF_PWRREQ) {
+ dev_warn(&rdev->dev, "%s: is disabled, mode: 0x%x not set\n",
+ rdev->desc->name, mode);
+ return 0;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY:
+ /*
+ * If the regulator opmode is normal then enable
+ * ON in Low Power Mode by PWRREQ. If the mode is
+ * already Low Power then no action is required.
+ */
+ if (max77802->opmode[id] == MAX77802_OPMODE_NORMAL)
+ val = MAX77802_LP_PWRREQ;
+ else
+ return 0;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ /*
+ * If the regulator operating mode is Low Power then
+ * normal is not a valid opmode in suspend. If the
+ * mode is already normal then no action is required.
+ */
+ if (max77802->opmode[id] == MAX77802_OPMODE_LP)
+ dev_warn(&rdev->dev, "%s: in Low Power: 0x%x invalid\n",
+ rdev->desc->name, mode);
+ return 0;
+ default:
+ dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n",
+ rdev->desc->name, mode);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val << shift);
+}
+
+static int max77802_enable(struct regulator_dev *rdev)
+{
+ struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ int shift = max77802_get_opmode_shift(id);
+
+ if (max77802->opmode[id] == MAX77802_OFF_PWRREQ)
+ max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ max77802->opmode[id] << shift);
+}
+
+static int max77802_find_ramp_value(struct regulator_dev *rdev,
+ const unsigned int limits[], int size,
+ unsigned int ramp_delay)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (ramp_delay <= limits[i])
+ return i;
+ }
+
+ /* Use maximum value for no ramp control */
+ dev_warn(&rdev->dev, "%s: ramp_delay: %d not supported, setting 100000\n",
+ rdev->desc->name, ramp_delay);
+ return size - 1;
+}
+
+/* Used for BUCKs 2-4 */
+static int max77802_set_ramp_delay_2bit(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ int id = rdev_get_id(rdev);
+ unsigned int ramp_value;
+
+ if (id > MAX77802_BUCK4) {
+ dev_warn(&rdev->dev,
+ "%s: regulator: ramp delay not supported\n",
+ rdev->desc->name);
+ return -EINVAL;
+ }
+ ramp_value = max77802_find_ramp_value(rdev, ramp_table_77802_2bit,
+ ARRAY_SIZE(ramp_table_77802_2bit), ramp_delay);
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ MAX77802_RAMP_RATE_MASK_2BIT,
+ ramp_value << MAX77802_RAMP_RATE_SHIFT_2BIT);
+}
+
+/* For BUCK1, 6 */
+static int max77802_set_ramp_delay_4bit(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ unsigned int ramp_value;
+
+ ramp_value = max77802_find_ramp_value(rdev, ramp_table_77802_4bit,
+ ARRAY_SIZE(ramp_table_77802_4bit), ramp_delay);
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ MAX77802_RAMP_RATE_MASK_4BIT,
+ ramp_value << MAX77802_RAMP_RATE_SHIFT_4BIT);
+}
+
+/*
+ * LDOs 2, 4-19, 22-35
+ */
+static const struct regulator_ops max77802_ldo_ops_logic1 = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77802_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 = max77802_set_suspend_disable,
+ .set_suspend_mode = max77802_set_suspend_mode,
+};
+
+/*
+ * LDOs 1, 20, 21, 3
+ */
+static const struct regulator_ops max77802_ldo_ops_logic2 = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77802_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_mode = max77802_set_mode,
+ .get_mode = max77802_get_mode,
+ .set_suspend_mode = max77802_set_suspend_mode,
+};
+
+/* BUCKS 1, 6 */
+static const struct regulator_ops max77802_buck_16_dvs_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77802_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 = max77802_set_ramp_delay_4bit,
+ .set_suspend_disable = max77802_set_suspend_disable,
+};
+
+/* BUCKs 2-4 */
+static const struct regulator_ops max77802_buck_234_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77802_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 = max77802_set_ramp_delay_2bit,
+ .set_suspend_disable = max77802_set_suspend_disable,
+ .set_suspend_mode = max77802_set_suspend_mode,
+};
+
+/* BUCKs 5, 7-10 */
+static const struct regulator_ops max77802_buck_dvs_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77802_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 = max77802_set_ramp_delay_2bit,
+ .set_suspend_disable = max77802_set_suspend_disable,
+};
+
+/* LDOs 3-7, 9-14, 18-26, 28, 29, 32-34 */
+#define regulator_77802_desc_p_ldo(num, supply, log) { \
+ .name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77802_LDO##num, \
+ .supply_name = "inl"#supply, \
+ .ops = &max77802_ldo_ops_logic##log, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 800000, \
+ .uV_step = 50000, \
+ .ramp_delay = MAX77802_RAMP_DELAY, \
+ .n_voltages = 1 << 6, \
+ .vsel_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \
+ .vsel_mask = MAX77802_VSEL_MASK, \
+ .enable_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \
+ .enable_mask = MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+ .of_map_mode = max77802_map_mode, \
+}
+
+/* LDOs 1, 2, 8, 15, 17, 27, 30, 35 */
+#define regulator_77802_desc_n_ldo(num, supply, log) { \
+ .name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77802_LDO##num, \
+ .supply_name = "inl"#supply, \
+ .ops = &max77802_ldo_ops_logic##log, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 800000, \
+ .uV_step = 25000, \
+ .ramp_delay = MAX77802_RAMP_DELAY, \
+ .n_voltages = 1 << 6, \
+ .vsel_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \
+ .vsel_mask = MAX77802_VSEL_MASK, \
+ .enable_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \
+ .enable_mask = MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+ .of_map_mode = max77802_map_mode, \
+}
+
+/* BUCKs 1, 6 */
+#define regulator_77802_desc_16_buck(num) { \
+ .name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77802_BUCK##num, \
+ .supply_name = "inb"#num, \
+ .ops = &max77802_buck_16_dvs_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 612500, \
+ .uV_step = 6250, \
+ .ramp_delay = MAX77802_RAMP_DELAY, \
+ .n_voltages = 1 << 8, \
+ .vsel_reg = MAX77802_REG_BUCK ## num ## DVS1, \
+ .vsel_mask = MAX77802_DVS_VSEL_MASK, \
+ .enable_reg = MAX77802_REG_BUCK ## num ## CTRL, \
+ .enable_mask = MAX77802_OPMODE_MASK, \
+ .of_map_mode = max77802_map_mode, \
+}
+
+/* BUCKS 2-4 */
+#define regulator_77802_desc_234_buck(num) { \
+ .name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77802_BUCK##num, \
+ .supply_name = "inb"#num, \
+ .ops = &max77802_buck_234_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 600000, \
+ .uV_step = 6250, \
+ .ramp_delay = MAX77802_RAMP_DELAY, \
+ .n_voltages = 0x91, \
+ .vsel_reg = MAX77802_REG_BUCK ## num ## DVS1, \
+ .vsel_mask = MAX77802_DVS_VSEL_MASK, \
+ .enable_reg = MAX77802_REG_BUCK ## num ## CTRL1, \
+ .enable_mask = MAX77802_OPMODE_MASK << \
+ MAX77802_OPMODE_BUCK234_SHIFT, \
+ .of_map_mode = max77802_map_mode, \
+}
+
+/* BUCK 5 */
+#define regulator_77802_desc_buck5(num) { \
+ .name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77802_BUCK##num, \
+ .supply_name = "inb"#num, \
+ .ops = &max77802_buck_dvs_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 750000, \
+ .uV_step = 50000, \
+ .ramp_delay = MAX77802_RAMP_DELAY, \
+ .n_voltages = 1 << 6, \
+ .vsel_reg = MAX77802_REG_BUCK5OUT, \
+ .vsel_mask = MAX77802_VSEL_MASK, \
+ .enable_reg = MAX77802_REG_BUCK5CTRL, \
+ .enable_mask = MAX77802_OPMODE_MASK, \
+ .of_map_mode = max77802_map_mode, \
+}
+
+/* BUCKs 7-10 */
+#define regulator_77802_desc_buck7_10(num) { \
+ .name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77802_BUCK##num, \
+ .supply_name = "inb"#num, \
+ .ops = &max77802_buck_dvs_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = 750000, \
+ .uV_step = 50000, \
+ .ramp_delay = MAX77802_RAMP_DELAY, \
+ .n_voltages = 1 << 6, \
+ .vsel_reg = MAX77802_REG_BUCK7OUT + (num - 7) * 3, \
+ .vsel_mask = MAX77802_VSEL_MASK, \
+ .enable_reg = MAX77802_REG_BUCK7CTRL + (num - 7) * 3, \
+ .enable_mask = MAX77802_OPMODE_MASK, \
+ .of_map_mode = max77802_map_mode, \
+}
+
+static const struct regulator_desc regulators[] = {
+ regulator_77802_desc_16_buck(1),
+ regulator_77802_desc_234_buck(2),
+ regulator_77802_desc_234_buck(3),
+ regulator_77802_desc_234_buck(4),
+ regulator_77802_desc_buck5(5),
+ regulator_77802_desc_16_buck(6),
+ regulator_77802_desc_buck7_10(7),
+ regulator_77802_desc_buck7_10(8),
+ regulator_77802_desc_buck7_10(9),
+ regulator_77802_desc_buck7_10(10),
+ regulator_77802_desc_n_ldo(1, 10, 2),
+ regulator_77802_desc_n_ldo(2, 10, 1),
+ regulator_77802_desc_p_ldo(3, 3, 2),
+ regulator_77802_desc_p_ldo(4, 6, 1),
+ regulator_77802_desc_p_ldo(5, 3, 1),
+ regulator_77802_desc_p_ldo(6, 3, 1),
+ regulator_77802_desc_p_ldo(7, 3, 1),
+ regulator_77802_desc_n_ldo(8, 1, 1),
+ regulator_77802_desc_p_ldo(9, 5, 1),
+ regulator_77802_desc_p_ldo(10, 4, 1),
+ regulator_77802_desc_p_ldo(11, 4, 1),
+ regulator_77802_desc_p_ldo(12, 9, 1),
+ regulator_77802_desc_p_ldo(13, 4, 1),
+ regulator_77802_desc_p_ldo(14, 4, 1),
+ regulator_77802_desc_n_ldo(15, 1, 1),
+ regulator_77802_desc_n_ldo(17, 2, 1),
+ regulator_77802_desc_p_ldo(18, 7, 1),
+ regulator_77802_desc_p_ldo(19, 5, 1),
+ regulator_77802_desc_p_ldo(20, 7, 2),
+ regulator_77802_desc_p_ldo(21, 6, 2),
+ regulator_77802_desc_p_ldo(23, 9, 1),
+ regulator_77802_desc_p_ldo(24, 6, 1),
+ regulator_77802_desc_p_ldo(25, 9, 1),
+ regulator_77802_desc_p_ldo(26, 9, 1),
+ regulator_77802_desc_n_ldo(27, 2, 1),
+ regulator_77802_desc_p_ldo(28, 7, 1),
+ regulator_77802_desc_p_ldo(29, 7, 1),
+ regulator_77802_desc_n_ldo(30, 2, 1),
+ regulator_77802_desc_p_ldo(32, 9, 1),
+ regulator_77802_desc_p_ldo(33, 6, 1),
+ regulator_77802_desc_p_ldo(34, 9, 1),
+ regulator_77802_desc_n_ldo(35, 2, 1),
+};
+
+static int max77802_pmic_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max77802_regulator_prv *max77802;
+ int i, val;
+ struct regulator_config config = { };
+
+ max77802 = devm_kzalloc(&pdev->dev,
+ sizeof(struct max77802_regulator_prv),
+ GFP_KERNEL);
+ if (!max77802)
+ return -ENOMEM;
+
+ config.dev = iodev->dev;
+ config.regmap = iodev->regmap;
+ config.driver_data = max77802;
+ platform_set_drvdata(pdev, max77802);
+
+ for (i = 0; i < MAX77802_REG_MAX; i++) {
+ struct regulator_dev *rdev;
+ int id = regulators[i].id;
+ int shift = max77802_get_opmode_shift(id);
+ int ret;
+
+ ret = regmap_read(iodev->regmap, regulators[i].enable_reg, &val);
+ if (ret < 0) {
+ dev_warn(&pdev->dev,
+ "cannot read current mode for %d\n", i);
+ val = MAX77802_OPMODE_NORMAL;
+ } else {
+ val = val >> shift & MAX77802_OPMODE_MASK;
+ }
+
+ /*
+ * If the regulator is disabled and the system warm rebooted,
+ * the hardware reports OFF as the regulator operating mode.
+ * Default to operating mode NORMAL in that case.
+ */
+ if (val == MAX77802_STATUS_OFF)
+ max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
+ else
+ max77802->opmode[id] = val;
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&pdev->dev,
+ "regulator init failed for %d: %d\n", i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id max77802_pmic_id[] = {
+ {"max77802-pmic", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, max77802_pmic_id);
+
+static struct platform_driver max77802_pmic_driver = {
+ .driver = {
+ .name = "max77802-pmic",
+ },
+ .probe = max77802_pmic_probe,
+ .id_table = max77802_pmic_id,
+};
+
+module_platform_driver(max77802_pmic_driver);
+
+MODULE_DESCRIPTION("MAXIM 77802 Regulator Driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c
new file mode 100644
index 000000000..81229579e
--- /dev/null
+++ b/drivers/regulator/max8649.c
@@ -0,0 +1,275 @@
+/*
+ * Regulators driver for Maxim max8649
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+#include <linux/regulator/max8649.h>
+#include <linux/regmap.h>
+
+#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 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 const 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 const 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 = dev_get_platdata(&client->dev);
+ struct max8649_regulator_info *info = NULL;
+ struct regulator_dev *regulator;
+ 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)
+ 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;
+
+ regulator = devm_regulator_register(&client->dev, &dcdc_desc,
+ &config);
+ if (IS_ERR(regulator)) {
+ dev_err(info->dev, "failed to register regulator %s\n",
+ dcdc_desc.name);
+ return PTR_ERR(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,
+ .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 <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
new file mode 100644
index 000000000..4cf6897a4
--- /dev/null
+++ b/drivers/regulator/max8660.c
@@ -0,0 +1,542 @@
+/*
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+#include <linux/regulator/max8660.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
+
+#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 */
+};
+
+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 const 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 const 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,
+ },
+};
+
+enum {
+ MAX8660 = 0,
+ MAX8661 = 1,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id max8660_dt_ids[] = {
+ { .compatible = "maxim,max8660", .data = (void *) MAX8660 },
+ { .compatible = "maxim,max8661", .data = (void *) MAX8661 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max8660_dt_ids);
+
+static int max8660_pdata_from_dt(struct device *dev,
+ struct device_node **of_node,
+ struct max8660_platform_data *pdata)
+{
+ int matched, i;
+ struct device_node *np;
+ struct max8660_subdev_data *sub;
+ struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)] = { };
+
+ np = of_get_child_by_name(dev->of_node, "regulators");
+ if (!np) {
+ dev_err(dev, "missing 'regulators' subnode in DT\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rmatch); i++)
+ rmatch[i].name = max8660_reg[i].name;
+
+ matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch));
+ of_node_put(np);
+ if (matched <= 0)
+ return matched;
+
+ pdata->subdevs = devm_kcalloc(dev,
+ matched,
+ sizeof(struct max8660_subdev_data),
+ GFP_KERNEL);
+ if (!pdata->subdevs)
+ return -ENOMEM;
+
+ pdata->num_subdevs = matched;
+ sub = pdata->subdevs;
+
+ for (i = 0; i < matched; i++) {
+ sub->id = i;
+ sub->name = rmatch[i].name;
+ sub->platform_data = rmatch[i].init_data;
+ of_node[i] = rmatch[i].of_node;
+ sub++;
+ }
+
+ return 0;
+}
+#else
+static inline int max8660_pdata_from_dt(struct device *dev,
+ struct device_node **of_node,
+ struct max8660_platform_data *pdata)
+{
+ return 0;
+}
+#endif
+
+static int max8660_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct device *dev = &client->dev;
+ struct max8660_platform_data pdata_of, *pdata = dev_get_platdata(dev);
+ struct regulator_config config = { };
+ struct max8660 *max8660;
+ int boot_on, i, id, ret = -EINVAL;
+ struct device_node *of_node[MAX8660_V_END];
+ unsigned long type;
+
+ if (dev->of_node && !pdata) {
+ const struct of_device_id *id;
+
+ id = of_match_device(of_match_ptr(max8660_dt_ids), dev);
+ if (!id)
+ return -ENODEV;
+
+ ret = max8660_pdata_from_dt(dev, of_node, &pdata_of);
+ if (ret < 0)
+ return ret;
+
+ pdata = &pdata_of;
+ type = (unsigned long) id->data;
+ } else {
+ type = i2c_id->driver_data;
+ memset(of_node, 0, sizeof(of_node));
+ }
+
+ if (pdata->num_subdevs > MAX8660_V_END) {
+ dev_err(dev, "Too many regulators found!\n");
+ return -EINVAL;
+ }
+
+ max8660 = devm_kzalloc(dev, sizeof(struct max8660), GFP_KERNEL);
+ if (!max8660)
+ return -ENOMEM;
+
+ max8660->client = client;
+
+ 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)
+ boot_on = false;
+ else
+ 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 (type == MAX8661) {
+ dev_err(dev, "Regulator not on this chip!\n");
+ return -EINVAL;
+ }
+
+ if (boot_on)
+ max8660->shadow_regs[MAX8660_OVER2] |= 4;
+ break;
+
+ default:
+ dev_err(dev, "invalid regulator %s\n",
+ pdata->subdevs[i].name);
+ return ret;
+ }
+ }
+
+ /* Finally register devices */
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ struct regulator_dev *rdev;
+
+ id = pdata->subdevs[i].id;
+
+ config.dev = dev;
+ config.init_data = pdata->subdevs[i].platform_data;
+ config.of_node = of_node[i];
+ config.driver_data = max8660;
+
+ rdev = devm_regulator_register(&client->dev,
+ &max8660_reg[id], &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&client->dev, "failed to register %s\n",
+ max8660_reg[id].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ i2c_set_clientdata(client, max8660);
+ return 0;
+}
+
+static const struct i2c_device_id max8660_id[] = {
+ { .name = "max8660", .driver_data = MAX8660 },
+ { .name = "max8661", .driver_data = MAX8661 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max8660_id);
+
+static struct i2c_driver max8660_driver = {
+ .probe = max8660_probe,
+ .driver = {
+ .name = "max8660",
+ },
+ .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 000000000..a8f2f0723
--- /dev/null
+++ b/drivers/regulator/max8907-regulator.c
@@ -0,0 +1,399 @@
+/*
+ * max8907-regulator.c -- support regulators in max8907
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
+ * 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 <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * 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 <linux/err.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#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];
+};
+
+#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 const 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 const 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 const 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 const struct regulator_ops max8907_out5v_hwctl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static const 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 = pdev->dev.parent->of_node;
+ if (!np)
+ return 0;
+
+ regulators = of_get_child_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)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pmic);
+
+ memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc));
+
+ /* Backwards compatibility with MAX8907B; SD1 uses different voltages */
+ ret = regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val);
+ if (ret)
+ return ret;
+
+ 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++) {
+ struct regulator_dev *rdev;
+
+ 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) {
+ ret = regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ &val);
+ if (ret)
+ return ret;
+
+ 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) {
+ ret = regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ &val);
+ if (ret)
+ return ret;
+
+ if ((val & (MAX8907_MASK_OUT5V_VINEN |
+ MAX8907_MASK_OUT5V_ENSRC)) !=
+ MAX8907_MASK_OUT5V_ENSRC)
+ pmic->desc[i].ops = &max8907_out5v_hwctl_ops;
+ }
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &pmic->desc[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "failed to register %s regulator\n",
+ pmic->desc[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver max8907_regulator_driver = {
+ .driver = {
+ .name = "max8907-regulator",
+ },
+ .probe = max8907_regulator_probe,
+};
+
+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 <jack.yoo@maxim-ic.com>");
+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 000000000..39b63ddef
--- /dev/null
+++ b/drivers/regulator/max8925-regulator.c
@@ -0,0 +1,346 @@
+/*
+ * Regulators driver for Maxim max8925
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/max8925.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+
+#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 i2c_client *i2c;
+
+ 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 const 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 const 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 = pdev->dev.parent->of_node;
+ if (!nproot)
+ return -ENODEV;
+ np = of_get_child_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 rcount;
+ 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 = dev_get_platdata(&pdev->dev);
+ 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;
+
+ config.dev = &pdev->dev;
+ config.driver_data = ri;
+
+ if (max8925_regulator_dt_init(pdev, &config, regulator_idx))
+ if (pdata)
+ config.init_data = pdata;
+
+ rdev = devm_regulator_register(&pdev->dev, &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 struct platform_driver max8925_regulator_driver = {
+ .driver = {
+ .name = "max8925-regulator",
+ },
+ .probe = max8925_regulator_probe,
+};
+
+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 <haojian.zhuang@marvell.com>");
+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 000000000..f1e77ed5d
--- /dev/null
+++ b/drivers/regulator/max8952.c
@@ -0,0 +1,353 @@
+/*
+ * max8952.c - Voltage and current regulation for the Maxim 8952
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@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.
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/max8952.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/* 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 max8952_platform_data *pdata;
+
+ 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 const 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 const 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)
+ 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);
+
+ 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, &regulator);
+ 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 = dev_get_platdata(&client->dev);
+ struct regulator_config config = { };
+ struct max8952_data *max8952;
+ struct regulator_dev *rdev;
+ struct gpio_desc *gpiod;
+ enum gpiod_flags gflags;
+
+ 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->pdata = pdata;
+
+ config.dev = &client->dev;
+ config.init_data = pdata->reg_data;
+ config.driver_data = max8952;
+ config.of_node = client->dev.of_node;
+
+ if (pdata->reg_data->constraints.boot_on)
+ gflags = GPIOD_OUT_HIGH;
+ else
+ gflags = GPIOD_OUT_LOW;
+ gpiod = devm_gpiod_get_optional(&client->dev,
+ "max8952,en",
+ gflags);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+ if (gpiod)
+ config.ena_gpiod = gpiod;
+
+ rdev = devm_regulator_register(&client->dev, &regulator, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&client->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)) {
+ unsigned long gpio_flags;
+
+ gpio_flags = max8952->vid0 ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ if (devm_gpio_request_one(&client->dev, pdata->gpio_vid0,
+ gpio_flags, "MAX8952 VID0"))
+ err = 1;
+
+ gpio_flags = max8952->vid1 ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ if (devm_gpio_request_one(&client->dev, pdata->gpio_vid1,
+ gpio_flags, "MAX8952 VID1"))
+ err = 2;
+ } else
+ err = 3;
+
+ if (err) {
+ dev_warn(&client->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(&client->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 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,
+ .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 <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c
new file mode 100644
index 000000000..7cd493ec6
--- /dev/null
+++ b/drivers/regulator/max8973-regulator.c
@@ -0,0 +1,852 @@
+/*
+ * max8973-regulator.c -- Maxim max8973
+ *
+ * Regulator driver for MAXIM 8973 DC-DC step-down switching regulator.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.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.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/max8973-regulator.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+/* 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
+#define MAX8973_RAMP_MASK 0x3
+
+/* MAX8973_CONTROL2 */
+#define MAX8973_WDTMR_ENABLE BIT(6)
+#define MAX8973_DISCH_ENBABLE BIT(5)
+#define MAX8973_FT_ENABLE BIT(4)
+#define MAX77621_T_JUNCTION_120 BIT(7)
+
+#define MAX8973_CKKADV_TRIP_MASK 0xC
+#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
+
+#define MAX77621_CHIPID_TJINT_S BIT(0)
+
+#define MAX77621_NORMAL_OPERATING_TEMP 100000
+#define MAX77621_TJINT_WARNING_TEMP_120 120000
+#define MAX77621_TJINT_WARNING_TEMP_140 140000
+
+enum device_id {
+ MAX8973,
+ MAX77621
+};
+
+/* Maxim 8973 chip information */
+struct max8973_chip {
+ struct device *dev;
+ struct regulator_desc desc;
+ 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;
+ struct regulator_ops ops;
+ enum device_id id;
+ int junction_temp_warning;
+ int irq;
+ struct thermal_zone_device *tz_device;
+};
+
+/*
+ * 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 (gpio_is_valid(max->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 (gpio_is_valid(max->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 int max8973_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int control;
+ int ret;
+
+ /* Set ramp delay */
+ if (ramp_delay <= 12000)
+ control = MAX8973_RAMP_12mV_PER_US;
+ else if (ramp_delay <= 25000)
+ control = MAX8973_RAMP_25mV_PER_US;
+ else if (ramp_delay <= 50000)
+ control = MAX8973_RAMP_50mV_PER_US;
+ else if (ramp_delay <= 200000)
+ control = MAX8973_RAMP_200mV_PER_US;
+ else
+ return -EINVAL;
+
+ ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1,
+ MAX8973_RAMP_MASK, control);
+ if (ret < 0)
+ dev_err(max->dev, "register %d update failed, %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+}
+
+static int max8973_set_current_limit(struct regulator_dev *rdev,
+ int min_ua, int max_ua)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret;
+
+ if (max_ua <= 9000000)
+ val = MAX8973_CKKADV_TRIP_75mV_PER_US;
+ else if (max_ua <= 12000000)
+ val = MAX8973_CKKADV_TRIP_150mV_PER_US;
+ else
+ val = MAX8973_CKKADV_TRIP_DISABLE;
+
+ ret = regmap_update_bits(max->regmap, MAX8973_CONTROL2,
+ MAX8973_CKKADV_TRIP_MASK, val);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d update failed: %d\n",
+ MAX8973_CONTROL2, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int max8973_get_current_limit(struct regulator_dev *rdev)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int control2;
+ int ret;
+
+ ret = regmap_read(max->regmap, MAX8973_CONTROL2, &control2);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d read failed: %d\n",
+ MAX8973_CONTROL2, ret);
+ return ret;
+ }
+ switch (control2 & MAX8973_CKKADV_TRIP_MASK) {
+ case MAX8973_CKKADV_TRIP_DISABLE:
+ return 15000000;
+ case MAX8973_CKKADV_TRIP_150mV_PER_US:
+ return 12000000;
+ case MAX8973_CKKADV_TRIP_75mV_PER_US:
+ return 9000000;
+ default:
+ break;
+ }
+ return 9000000;
+}
+
+static const 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,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = max8973_set_ramp_delay,
+};
+
+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;
+ unsigned int data;
+
+ ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d read failed, err = %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+ }
+ control1 = data & MAX8973_RAMP_MASK;
+ switch (control1) {
+ case MAX8973_RAMP_12mV_PER_US:
+ max->desc.ramp_delay = 12000;
+ break;
+ case MAX8973_RAMP_25mV_PER_US:
+ max->desc.ramp_delay = 25000;
+ break;
+ case MAX8973_RAMP_50mV_PER_US:
+ max->desc.ramp_delay = 50000;
+ break;
+ case MAX8973_RAMP_200mV_PER_US:
+ max->desc.ramp_delay = 200000;
+ break;
+ }
+
+ 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;
+ max->desc.enable_time = 20;
+ } else {
+ max->desc.enable_time = 240;
+ }
+
+ if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
+ control1 |= MAX8973_FREQSHIFT_9PER;
+
+ if ((pdata->junction_temp_warning == MAX77621_TJINT_WARNING_TEMP_120) &&
+ (max->id == MAX77621))
+ control2 |= MAX77621_T_JUNCTION_120;
+
+ 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 && (max->id == MAX8973)) {
+ 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 int max8973_thermal_read_temp(void *data, int *temp)
+{
+ struct max8973_chip *mchip = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(mchip->regmap, MAX8973_CHIPID1, &val);
+ if (ret < 0) {
+ dev_err(mchip->dev, "Failed to read register CHIPID1, %d", ret);
+ return ret;
+ }
+
+ /* +1 degC to trigger cool devive */
+ if (val & MAX77621_CHIPID_TJINT_S)
+ *temp = mchip->junction_temp_warning + 1000;
+ else
+ *temp = MAX77621_NORMAL_OPERATING_TEMP;
+
+ return 0;
+}
+
+static irqreturn_t max8973_thermal_irq(int irq, void *data)
+{
+ struct max8973_chip *mchip = data;
+
+ thermal_zone_device_update(mchip->tz_device,
+ THERMAL_EVENT_UNSPECIFIED);
+
+ return IRQ_HANDLED;
+}
+
+static const struct thermal_zone_of_device_ops max77621_tz_ops = {
+ .get_temp = max8973_thermal_read_temp,
+};
+
+static int max8973_thermal_init(struct max8973_chip *mchip)
+{
+ struct thermal_zone_device *tzd;
+ struct irq_data *irq_data;
+ unsigned long irq_flags = 0;
+ int ret;
+
+ if (mchip->id != MAX77621)
+ return 0;
+
+ tzd = devm_thermal_zone_of_sensor_register(mchip->dev, 0, mchip,
+ &max77621_tz_ops);
+ if (IS_ERR(tzd)) {
+ ret = PTR_ERR(tzd);
+ dev_err(mchip->dev, "Failed to register thermal sensor: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (mchip->irq <= 0)
+ return 0;
+
+ irq_data = irq_get_irq_data(mchip->irq);
+ if (irq_data)
+ irq_flags = irqd_get_trigger_type(irq_data);
+
+ ret = devm_request_threaded_irq(mchip->dev, mchip->irq, NULL,
+ max8973_thermal_irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_flags,
+ dev_name(mchip->dev), mchip);
+ if (ret < 0) {
+ dev_err(mchip->dev, "Failed to request irq %d, %d\n",
+ mchip->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct regmap_config max8973_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8973_CHIPID2,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static struct max8973_regulator_platform_data *max8973_parse_dt(
+ struct device *dev)
+{
+ struct max8973_regulator_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret;
+ u32 pval;
+ bool etr_enable;
+ bool etr_sensitivity_high;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->enable_ext_control = of_property_read_bool(np,
+ "maxim,externally-enable");
+ pdata->dvs_gpio = of_get_named_gpio(np, "maxim,dvs-gpio", 0);
+
+ ret = of_property_read_u32(np, "maxim,dvs-default-state", &pval);
+ if (!ret)
+ pdata->dvs_def_state = pval;
+
+ if (of_property_read_bool(np, "maxim,enable-remote-sense"))
+ pdata->control_flags |= MAX8973_CONTROL_REMOTE_SENSE_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-falling-slew-rate"))
+ pdata->control_flags |=
+ MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-active-discharge"))
+ pdata->control_flags |=
+ MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-frequency-shift"))
+ pdata->control_flags |= MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-bias-control"))
+ pdata->control_flags |= MAX8973_CONTROL_BIAS_ENABLE;
+
+ etr_enable = of_property_read_bool(np, "maxim,enable-etr");
+ etr_sensitivity_high = of_property_read_bool(np,
+ "maxim,enable-high-etr-sensitivity");
+ if (etr_sensitivity_high)
+ etr_enable = true;
+
+ if (etr_enable) {
+ if (etr_sensitivity_high)
+ pdata->control_flags |=
+ MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US;
+ else
+ pdata->control_flags |=
+ MAX8973_CONTROL_CLKADV_TRIP_150mV_PER_US;
+ } else {
+ pdata->control_flags |= MAX8973_CONTROL_CLKADV_TRIP_DISABLED;
+ }
+
+ pdata->junction_temp_warning = MAX77621_TJINT_WARNING_TEMP_140;
+ ret = of_property_read_u32(np, "junction-warn-millicelsius", &pval);
+ if (!ret && (pval <= MAX77621_TJINT_WARNING_TEMP_120))
+ pdata->junction_temp_warning = MAX77621_TJINT_WARNING_TEMP_120;
+
+ return pdata;
+}
+
+static const struct of_device_id of_max8973_match_tbl[] = {
+ { .compatible = "maxim,max8973", .data = (void *)MAX8973, },
+ { .compatible = "maxim,max77621", .data = (void *)MAX77621, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_max8973_match_tbl);
+
+static int max8973_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max8973_regulator_platform_data *pdata;
+ struct regulator_init_data *ridata;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct max8973_chip *max;
+ bool pdata_from_dt = false;
+ unsigned int chip_id;
+ struct gpio_desc *gpiod;
+ enum gpiod_flags gflags;
+ int ret;
+
+ pdata = dev_get_platdata(&client->dev);
+
+ if (!pdata && client->dev.of_node) {
+ pdata = max8973_parse_dt(&client->dev);
+ pdata_from_dt = true;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "No Platform data");
+ return -EIO;
+ }
+
+ if (pdata->dvs_gpio == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
+ if (!max)
+ 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;
+ }
+
+ if (client->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(of_max8973_match_tbl),
+ &client->dev);
+ if (!match)
+ return -ENODATA;
+ max->id = (u32)((uintptr_t)match->data);
+ } else {
+ max->id = id->driver_data;
+ }
+
+ ret = regmap_read(max->regmap, MAX8973_CHIPID1, &chip_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "register CHIPID1 read failed, %d", ret);
+ return ret;
+ }
+
+ dev_info(&client->dev, "CHIP-ID OTP: 0x%02x ID_M: 0x%02x\n",
+ (chip_id >> 4) & 0xF, (chip_id >> 1) & 0x7);
+
+ i2c_set_clientdata(client, max);
+ max->ops = max8973_dcdc_ops;
+ max->dev = &client->dev;
+ max->desc.name = id->name;
+ max->desc.id = 0;
+ max->desc.ops = &max->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;
+
+ max->dvs_gpio = (pdata->dvs_gpio) ? pdata->dvs_gpio : -EINVAL;
+ max->enable_external_control = pdata->enable_ext_control;
+ max->curr_gpio_val = pdata->dvs_def_state;
+ max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
+ max->junction_temp_warning = pdata->junction_temp_warning;
+
+ max->lru_index[0] = max->curr_vout_reg;
+
+ 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;
+ }
+
+ /*
+ * 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;
+ } else {
+ /*
+ * If there is no DVS GPIO, the VOUT register
+ * address is fixed.
+ */
+ max->ops.set_voltage_sel = regulator_set_voltage_sel_regmap;
+ max->ops.get_voltage_sel = regulator_get_voltage_sel_regmap;
+ max->desc.vsel_reg = max->curr_vout_reg;
+ max->desc.vsel_mask = MAX8973_VOUT_MASK;
+ }
+
+ if (pdata_from_dt)
+ pdata->reg_init_data = of_get_regulator_init_data(&client->dev,
+ client->dev.of_node, &max->desc);
+
+ ridata = pdata->reg_init_data;
+ switch (max->id) {
+ case MAX8973:
+ if (!pdata->enable_ext_control) {
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max->ops.enable = regulator_enable_regmap;
+ max->ops.disable = regulator_disable_regmap;
+ max->ops.is_enabled = regulator_is_enabled_regmap;
+ break;
+ }
+
+ if (ridata && (ridata->constraints.always_on ||
+ ridata->constraints.boot_on))
+ gflags = GPIOD_OUT_HIGH;
+ else
+ gflags = GPIOD_OUT_LOW;
+ gpiod = devm_gpiod_get_optional(&client->dev,
+ "maxim,enable",
+ gflags);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+ if (gpiod) {
+ config.ena_gpiod = gpiod;
+ max->enable_external_control = true;
+ }
+
+ break;
+
+ case MAX77621:
+ /*
+ * We do not let the core switch this regulator on/off,
+ * we just leave it on.
+ */
+ gpiod = devm_gpiod_get_optional(&client->dev,
+ "maxim,enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+ if (gpiod)
+ max->enable_external_control = true;
+
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max->ops.enable = regulator_enable_regmap;
+ max->ops.disable = regulator_disable_regmap;
+ max->ops.is_enabled = regulator_is_enabled_regmap;
+ max->ops.set_current_limit = max8973_set_current_limit;
+ max->ops.get_current_limit = max8973_get_current_limit;
+ break;
+ default:
+ break;
+ }
+
+ 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 = devm_regulator_register(&client->dev, &max->desc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(max->dev, "regulator register failed, err %d\n", ret);
+ return ret;
+ }
+
+ max8973_thermal_init(max);
+ return 0;
+}
+
+static const struct i2c_device_id max8973_id[] = {
+ {.name = "max8973", .driver_data = MAX8973},
+ {.name = "max77621", .driver_data = MAX77621},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, max8973_id);
+
+static struct i2c_driver max8973_i2c_driver = {
+ .driver = {
+ .name = "max8973",
+ .of_match_table = of_max8973_match_tbl,
+ },
+ .probe = max8973_probe,
+ .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 <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("MAX8973 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c
new file mode 100644
index 000000000..ad0c806b0
--- /dev/null
+++ b/drivers/regulator/max8997-regulator.c
@@ -0,0 +1,1224 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max8997.c - Regulator driver for the Maxim 8997/8966
+//
+// Copyright (C) 2011 Samsung Electronics
+// MyungJoo Ham <myungjoo.ham@samsung.com>
+//
+// This driver is based on max8998.c
+
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+#include <linux/regulator/of_regulator.h>
+
+struct max8997_data {
+ struct device *dev;
+ struct max8997_dev *iodev;
+ int num_regulators;
+ 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 < 0 || rid >= ARRAY_SIZE(reg_voltage_map))
+ 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, &reg, &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, &reg, &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, &reg, &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, &reg, &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, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ if (max_uV < 4000000 || min_uV > 4350000)
+ return -EINVAL;
+
+ if (min_uV <= 4000000)
+ 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, &reg, &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, &reg, &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, &reg, &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 = iodev->dev->of_node;
+ if (!pmic_np) {
+ dev_err(&pdev->dev, "could not find pmic sub-node\n");
+ return -ENODEV;
+ }
+
+ regulators_np = of_get_child_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_kcalloc(&pdev->dev,
+ pdata->num_regulators, sizeof(*rdata),
+ GFP_KERNEL);
+ if (!rdata) {
+ of_node_put(regulators_np);
+ 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,
+ &regulators[i]);
+ 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, 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;
+
+ 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)
+ return ret;
+
+ 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)
+ return ret;
+
+ 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)
+ return ret;
+
+ 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");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[0],
+ "MAX8997 SET1");
+ if (ret)
+ return ret;
+
+ ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[1],
+ "MAX8997 SET2");
+ if (ret)
+ return ret;
+
+ ret = devm_gpio_request(&pdev->dev, pdata->buck125_gpios[2],
+ "MAX8997 SET3");
+ if (ret)
+ return ret;
+
+ 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 = devm_regulator_register(&pdev->dev, &regulators[id],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(max8997->dev, "regulator init failed for %d\n",
+ id);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ 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",
+ },
+ .probe = max8997_pmic_probe,
+ .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 <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
new file mode 100644
index 000000000..271bb736f
--- /dev/null
+++ b/drivers/regulator/max8998.c
@@ -0,0 +1,903 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max8998.c - Voltage regulator driver for the Maxim 8998
+//
+// Copyright (C) 2009-2010 Samsung Electronics
+// Kyungmin Park <kyungmin.park@samsung.com>
+// Marek Szyprowski <m.szyprowski@samsung.com>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_data {
+ struct device *dev;
+ struct max8998_dev *iodev;
+ int num_regulators;
+ 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, &reg, &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, &reg, &shift);
+ if (ret)
+ return ret;
+
+ return max8998_update_reg(i2c, reg, 1<<shift, 1<<shift);
+}
+
+static int max8998_ldo_disable(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, &reg, &shift);
+ if (ret)
+ return ret;
+
+ return max8998_update_reg(i2c, reg, 0, 1<<shift);
+}
+
+static int max8998_get_voltage_register(struct regulator_dev *rdev,
+ int *_reg, int *_shift, int *_mask)
+{
+ int ldo = rdev_get_id(rdev);
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 0, mask = 0xff;
+
+ switch (ldo) {
+ case MAX8998_LDO2 ... MAX8998_LDO3:
+ reg = MAX8998_REG_LDO2_LDO3;
+ mask = 0xf;
+ if (ldo == MAX8998_LDO2)
+ shift = 4;
+ else
+ shift = 0;
+ break;
+ case MAX8998_LDO4 ... MAX8998_LDO7:
+ reg = MAX8998_REG_LDO4 + (ldo - MAX8998_LDO4);
+ break;
+ case MAX8998_LDO8 ... MAX8998_LDO9:
+ reg = MAX8998_REG_LDO8_LDO9;
+ mask = 0xf;
+ if (ldo == MAX8998_LDO8)
+ shift = 4;
+ else
+ shift = 0;
+ break;
+ case MAX8998_LDO10 ... MAX8998_LDO11:
+ reg = MAX8998_REG_LDO10_LDO11;
+ if (ldo == MAX8998_LDO10) {
+ shift = 5;
+ mask = 0x7;
+ } else {
+ shift = 0;
+ mask = 0x1f;
+ }
+ break;
+ case MAX8998_LDO12 ... MAX8998_LDO17:
+ reg = MAX8998_REG_LDO12 + (ldo - MAX8998_LDO12);
+ break;
+ case MAX8998_BUCK1:
+ reg = MAX8998_REG_BUCK1_VOLTAGE1 + max8998->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, &reg, &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, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ ret = max8998_update_reg(i2c, reg, selector<<shift, mask<<shift);
+
+ return ret;
+}
+
+static inline void buck1_gpio_set(int gpio1, int gpio2, int v)
+{
+ gpio_set_value(gpio1, v & 0x1);
+ gpio_set_value(gpio2, (v >> 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 = max8998->iodev->pdata;
+ 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, &reg, &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, &reg,
+ &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,
+ &reg, &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<<shift,
+ mask<<shift);
+ break;
+ }
+
+ return ret;
+}
+
+static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector,
+ unsigned int new_selector)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ struct i2c_client *i2c = max8998->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_dt_parse_dvs_gpio(struct max8998_dev *iodev,
+ struct max8998_platform_data *pdata,
+ struct device_node *pmic_np)
+{
+ int gpio;
+
+ gpio = of_get_named_gpio(pmic_np, "max8998,pmic-buck1-dvs-gpios", 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(iodev->dev, "invalid buck1 gpio[0]: %d\n", gpio);
+ return -EINVAL;
+ }
+ pdata->buck1_set1 = gpio;
+
+ gpio = of_get_named_gpio(pmic_np, "max8998,pmic-buck1-dvs-gpios", 1);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(iodev->dev, "invalid buck1 gpio[1]: %d\n", gpio);
+ return -EINVAL;
+ }
+ pdata->buck1_set2 = gpio;
+
+ gpio = of_get_named_gpio(pmic_np, "max8998,pmic-buck2-dvs-gpio", 0);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(iodev->dev, "invalid buck 2 gpio: %d\n", gpio);
+ return -EINVAL;
+ }
+ pdata->buck2_set3 = gpio;
+
+ return 0;
+}
+
+static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev,
+ struct max8998_platform_data *pdata)
+{
+ struct device_node *pmic_np = iodev->dev->of_node;
+ struct device_node *regulators_np, *reg_np;
+ struct max8998_regulator_data *rdata;
+ unsigned int i;
+ int ret;
+
+ regulators_np = of_get_child_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_kcalloc(iodev->dev,
+ pdata->num_regulators, sizeof(*rdata),
+ GFP_KERNEL);
+ if (!rdata) {
+ of_node_put(regulators_np);
+ return -ENOMEM;
+ }
+
+ pdata->regulators = rdata;
+ for (i = 0; i < ARRAY_SIZE(regulators); ++i) {
+ reg_np = of_get_child_by_name(regulators_np,
+ regulators[i].name);
+ if (!reg_np)
+ continue;
+
+ rdata->id = regulators[i].id;
+ rdata->initdata = of_get_regulator_init_data(iodev->dev,
+ reg_np,
+ &regulators[i]);
+ rdata->reg_node = reg_np;
+ ++rdata;
+ }
+ pdata->num_regulators = rdata - pdata->regulators;
+
+ of_node_put(reg_np);
+ of_node_put(regulators_np);
+
+ ret = max8998_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np);
+ if (ret)
+ return -EINVAL;
+
+ if (of_find_property(pmic_np, "max8998,pmic-buck-voltage-lock", NULL))
+ pdata->buck_voltage_lock = true;
+
+ ret = of_property_read_u32(pmic_np,
+ "max8998,pmic-buck1-default-dvs-idx",
+ &pdata->buck1_default_idx);
+ if (!ret && pdata->buck1_default_idx >= 4) {
+ pdata->buck1_default_idx = 0;
+ dev_warn(iodev->dev, "invalid value for default dvs index, using 0 instead\n");
+ }
+
+ ret = of_property_read_u32(pmic_np,
+ "max8998,pmic-buck2-default-dvs-idx",
+ &pdata->buck2_default_idx);
+ if (!ret && pdata->buck2_default_idx >= 2) {
+ pdata->buck2_default_idx = 0;
+ dev_warn(iodev->dev, "invalid value for default dvs index, using 0 instead\n");
+ }
+
+ ret = of_property_read_u32_array(pmic_np,
+ "max8998,pmic-buck1-dvs-voltage",
+ pdata->buck1_voltage,
+ ARRAY_SIZE(pdata->buck1_voltage));
+ if (ret) {
+ dev_err(iodev->dev, "buck1 voltages not specified\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(pmic_np,
+ "max8998,pmic-buck2-dvs-voltage",
+ pdata->buck2_voltage,
+ ARRAY_SIZE(pdata->buck2_voltage));
+ if (ret) {
+ dev_err(iodev->dev, "buck2 voltages not specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max8998_pmic_probe(struct platform_device *pdev)
+{
+ struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max8998_platform_data *pdata = iodev->pdata;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct max8998_data *max8998;
+ struct i2c_client *i2c;
+ int i, ret;
+ unsigned int v;
+
+ if (!pdata) {
+ dev_err(pdev->dev.parent, "No platform init data supplied\n");
+ return -ENODEV;
+ }
+
+ if (IS_ENABLED(CONFIG_OF) && iodev->dev->of_node) {
+ ret = max8998_pmic_dt_parse_pdata(iodev, pdata);
+ if (ret)
+ return ret;
+ }
+
+ max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_data),
+ GFP_KERNEL);
+ if (!max8998)
+ return -ENOMEM;
+
+ 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);
+ return -EIO;
+ }
+ /* 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);
+ return -EIO;
+ }
+
+ 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 values for BUCK1 registers */
+ for (v = 0; v < ARRAY_SIZE(pdata->buck1_voltage); ++v) {
+ i = 0;
+ while (buck12_voltage_map_desc.min +
+ buck12_voltage_map_desc.step*i
+ < pdata->buck1_voltage[v])
+ i++;
+
+ max8998->buck1_vol[v] = i;
+ ret = max8998_write_reg(i2c,
+ MAX8998_REG_BUCK1_VOLTAGE1 + v, i);
+ if (ret)
+ return ret;
+ }
+ }
+
+ 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);
+ return -EIO;
+ }
+ gpio_request(pdata->buck2_set3, "MAX8998 BUCK2_SET3");
+ gpio_direction_output(pdata->buck2_set3,
+ max8998->buck2_idx & 0x1);
+
+ /* Set predefined values for BUCK2 registers */
+ for (v = 0; v < ARRAY_SIZE(pdata->buck2_voltage); ++v) {
+ i = 0;
+ while (buck12_voltage_map_desc.min +
+ buck12_voltage_map_desc.step*i
+ < pdata->buck2_voltage[v])
+ i++;
+
+ max8998->buck2_vol[v] = i;
+ ret = max8998_write_reg(i2c,
+ MAX8998_REG_BUCK2_VOLTAGE1 + v, i);
+ if (ret)
+ return ret;
+ }
+ }
+
+ 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.of_node = pdata->regulators[i].reg_node;
+ config.init_data = pdata->regulators[i].initdata;
+ config.driver_data = max8998;
+
+ rdev = devm_regulator_register(&pdev->dev, &regulators[index],
+ &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(max8998->dev, "regulator %s init failed (%d)\n",
+ regulators[index].name, ret);
+ return ret;
+ }
+ }
+
+
+ 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",
+ },
+ .probe = max8998_pmic_probe,
+ .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 <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
new file mode 100644
index 000000000..8fd1adc9c
--- /dev/null
+++ b/drivers/regulator/mc13783-regulator.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Regulator Driver for Freescale MC13783 PMIC
+//
+// Copyright 2010 Yong Shen <yong.shen@linaro.org>
+// Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+// Copyright 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+
+#include <linux/mfd/mc13783.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#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, 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,
+ struct_size(priv, regulators, num_regulators),
+ 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] = devm_regulator_register(&pdev->dev, desc,
+ &config);
+ if (IS_ERR(priv->regulators[i])) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ mc13783_regulators[i].desc.name);
+ return PTR_ERR(priv->regulators[i]);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver mc13783_regulator_driver = {
+ .driver = {
+ .name = "mc13783-regulator",
+ },
+ .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 <s.hauer@pengutronix.de>");
+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 000000000..f3fba1cc1
--- /dev/null
+++ b/drivers/regulator/mc13892-regulator.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Regulator Driver for Freescale MC13892 PMIC
+//
+// Copyright 2010 Yong Shen <yong.shen@linaro.org>
+//
+// Based on draft driver from Arnaud Patard <arnaud.patard@rtp-net.org>
+
+#include <linux/mfd/mc13892.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#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 struct regulator_ops mc13892_vcam_ops;
+
+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,
+ struct_size(priv, regulators, num_regulators),
+ 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);
+
+ /* update mc13892_vcam ops */
+ memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops,
+ sizeof(struct regulator_ops));
+ mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode,
+ mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode,
+ mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops;
+
+ 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] = devm_regulator_register(&pdev->dev, desc,
+ &config);
+ if (IS_ERR(priv->regulators[i])) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ mc13892_regulators[i].desc.name);
+ return PTR_ERR(priv->regulators[i]);
+ }
+ }
+
+ return 0;
+
+err_unlock:
+ mc13xxx_unlock(mc13892);
+ return ret;
+}
+
+static struct platform_driver mc13892_regulator_driver = {
+ .driver = {
+ .name = "mc13892-regulator",
+ },
+ .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 <yong.shen@linaro.org>");
+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 000000000..da4fb9824
--- /dev/null
+++ b/drivers/regulator/mc13xxx-regulator-core.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Regulator Driver for Freescale MC13xxx PMIC
+//
+// Copyright 2010 Yong Shen <yong.shen@linaro.org>
+//
+// Based on mc13783 regulator driver :
+// Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+// Copyright 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+//
+// Regs infos taken from mc13xxx drivers from freescale and mc13xxx.pdf file
+// from freescale
+
+#include <linux/mfd/mc13xxx.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#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);
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ return mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg,
+ mc13xxx_regulators[id].enable_bit,
+ mc13xxx_regulators[id].enable_bit);
+}
+
+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);
+
+ dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+ return mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].reg,
+ mc13xxx_regulators[id].enable_bit, 0);
+}
+
+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;
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, mc13xxx_regulators[id].reg, &val);
+ 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);
+
+ return mc13xxx_reg_rmw(priv->mc13xxx, mc13xxx_regulators[id].vsel_reg,
+ mc13xxx_regulators[id].vsel_mask,
+ selector << mc13xxx_regulators[id].vsel_shift);
+}
+
+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);
+
+ ret = mc13xxx_reg_read(priv->mc13xxx,
+ mc13xxx_regulators[id].vsel_reg, &val);
+ 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;
+
+ if (!pdev->dev.parent->of_node)
+ return -ENODEV;
+
+ parent = of_get_child_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;
+
+ if (!pdev->dev.parent->of_node)
+ return NULL;
+
+ parent = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
+ if (!parent)
+ return NULL;
+
+ data = devm_kcalloc(&pdev->dev, priv->num_regulators, sizeof(*data),
+ 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,
+ &regulators[i].desc);
+ 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 <yong.shen@linaro.org>");
+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 000000000..2ab9bfd93
--- /dev/null
+++ b/drivers/regulator/mc13xxx.h
@@ -0,0 +1,112 @@
+/*
+ * mc13xxx.h - regulators for the Freescale mc13xxx PMIC
+ *
+ * Copyright (C) 2010 Yong Shen <yong.shen@linaro.org>
+ *
+ * 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 <linux/regulator/driver.h>
+
+struct mc13xxx_regulator {
+ struct regulator_desc desc;
+ int reg;
+ int enable_bit;
+ int vsel_reg;
+ int vsel_shift;
+ int vsel_mask;
+};
+
+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/mt6311-regulator.c b/drivers/regulator/mt6311-regulator.c
new file mode 100644
index 000000000..0495716fd
--- /dev/null
+++ b/drivers/regulator/mt6311-regulator.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Henry Chen <henryc.chen@mediatek.com>
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/mt6311.h>
+#include <linux/slab.h>
+#include "mt6311-regulator.h"
+
+static const struct regmap_config mt6311_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MT6311_FQMTR_CON4,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/* Default limits measured in millivolts and milliamps */
+#define MT6311_MIN_UV 600000
+#define MT6311_MAX_UV 1393750
+#define MT6311_STEP_UV 6250
+
+static const struct regulator_linear_range buck_volt_range[] = {
+ REGULATOR_LINEAR_RANGE(MT6311_MIN_UV, 0, 0x7f, MT6311_STEP_UV),
+};
+
+static const struct regulator_ops mt6311_buck_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_ops mt6311_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define MT6311_BUCK(_id) \
+{\
+ .name = #_id,\
+ .ops = &mt6311_buck_ops,\
+ .of_match = of_match_ptr(#_id),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .id = MT6311_ID_##_id,\
+ .n_voltages = (MT6311_MAX_UV - MT6311_MIN_UV) / MT6311_STEP_UV + 1,\
+ .min_uV = MT6311_MIN_UV,\
+ .uV_step = MT6311_STEP_UV,\
+ .owner = THIS_MODULE,\
+ .linear_ranges = buck_volt_range, \
+ .n_linear_ranges = ARRAY_SIZE(buck_volt_range), \
+ .enable_reg = MT6311_VDVFS11_CON9,\
+ .enable_mask = MT6311_PMIC_VDVFS11_EN_MASK,\
+ .vsel_reg = MT6311_VDVFS11_CON12,\
+ .vsel_mask = MT6311_PMIC_VDVFS11_VOSEL_MASK,\
+}
+
+#define MT6311_LDO(_id) \
+{\
+ .name = #_id,\
+ .ops = &mt6311_ldo_ops,\
+ .of_match = of_match_ptr(#_id),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .id = MT6311_ID_##_id,\
+ .owner = THIS_MODULE,\
+ .enable_reg = MT6311_LDO_CON3,\
+ .enable_mask = MT6311_PMIC_RG_VBIASN_EN_MASK,\
+}
+
+static const struct regulator_desc mt6311_regulators[] = {
+ MT6311_BUCK(VDVFS),
+ MT6311_LDO(VBIASN),
+};
+
+/*
+ * I2C driver interface functions
+ */
+static int mt6311_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ int i, ret;
+ unsigned int data;
+
+ regmap = devm_regmap_init_i2c(i2c, &mt6311_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_read(regmap, MT6311_SWCID, &data);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read DEVICE_ID reg: %d\n", ret);
+ return ret;
+ }
+
+ switch (data) {
+ case MT6311_E1_CID_CODE:
+ case MT6311_E2_CID_CODE:
+ case MT6311_E3_CID_CODE:
+ break;
+ default:
+ dev_err(&i2c->dev, "Unsupported device id = 0x%x.\n", data);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < MT6311_MAX_REGULATORS; i++) {
+ config.dev = &i2c->dev;
+ config.regmap = regmap;
+
+ rdev = devm_regulator_register(&i2c->dev,
+ &mt6311_regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&i2c->dev,
+ "Failed to register MT6311 regulator\n");
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id mt6311_i2c_id[] = {
+ {"mt6311", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mt6311_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt6311_dt_ids[] = {
+ { .compatible = "mediatek,mt6311-regulator",
+ .data = &mt6311_i2c_id[0] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6311_dt_ids);
+#endif
+
+static struct i2c_driver mt6311_regulator_driver = {
+ .driver = {
+ .name = "mt6311",
+ .of_match_table = of_match_ptr(mt6311_dt_ids),
+ },
+ .probe = mt6311_i2c_probe,
+ .id_table = mt6311_i2c_id,
+};
+
+module_i2c_driver(mt6311_regulator_driver);
+
+MODULE_AUTHOR("Henry Chen <henryc.chen@mediatek.com>");
+MODULE_DESCRIPTION("Regulator device driver for Mediatek MT6311");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/mt6311-regulator.h b/drivers/regulator/mt6311-regulator.h
new file mode 100644
index 000000000..5218db46a
--- /dev/null
+++ b/drivers/regulator/mt6311-regulator.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Henry Chen <henryc.chen@mediatek.com>
+ *
+ * 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.
+ */
+
+#ifndef __MT6311_REGULATOR_H__
+#define __MT6311_REGULATOR_H__
+
+#define MT6311_SWCID 0x01
+
+#define MT6311_TOP_INT_CON 0x18
+#define MT6311_TOP_INT_MON 0x19
+
+#define MT6311_VDVFS11_CON0 0x87
+#define MT6311_VDVFS11_CON7 0x88
+#define MT6311_VDVFS11_CON8 0x89
+#define MT6311_VDVFS11_CON9 0x8A
+#define MT6311_VDVFS11_CON10 0x8B
+#define MT6311_VDVFS11_CON11 0x8C
+#define MT6311_VDVFS11_CON12 0x8D
+#define MT6311_VDVFS11_CON13 0x8E
+#define MT6311_VDVFS11_CON14 0x8F
+#define MT6311_VDVFS11_CON15 0x90
+#define MT6311_VDVFS11_CON16 0x91
+#define MT6311_VDVFS11_CON17 0x92
+#define MT6311_VDVFS11_CON18 0x93
+#define MT6311_VDVFS11_CON19 0x94
+
+#define MT6311_LDO_CON0 0xCC
+#define MT6311_LDO_OCFB0 0xCD
+#define MT6311_LDO_CON2 0xCE
+#define MT6311_LDO_CON3 0xCF
+#define MT6311_LDO_CON4 0xD0
+#define MT6311_FQMTR_CON0 0xD1
+#define MT6311_FQMTR_CON1 0xD2
+#define MT6311_FQMTR_CON2 0xD3
+#define MT6311_FQMTR_CON3 0xD4
+#define MT6311_FQMTR_CON4 0xD5
+
+#define MT6311_PMIC_RG_INT_POL_MASK 0x1
+#define MT6311_PMIC_RG_INT_EN_MASK 0x2
+#define MT6311_PMIC_RG_BUCK_OC_INT_STATUS_MASK 0x10
+
+#define MT6311_PMIC_VDVFS11_EN_CTRL_MASK 0x1
+#define MT6311_PMIC_VDVFS11_VOSEL_CTRL_MASK 0x2
+#define MT6311_PMIC_VDVFS11_EN_SEL_MASK 0x3
+#define MT6311_PMIC_VDVFS11_VOSEL_SEL_MASK 0xc
+#define MT6311_PMIC_VDVFS11_EN_MASK 0x1
+#define MT6311_PMIC_VDVFS11_VOSEL_MASK 0x7F
+#define MT6311_PMIC_VDVFS11_VOSEL_ON_MASK 0x7F
+#define MT6311_PMIC_VDVFS11_VOSEL_SLEEP_MASK 0x7F
+#define MT6311_PMIC_NI_VDVFS11_VOSEL_MASK 0x7F
+
+#define MT6311_PMIC_RG_VBIASN_EN_MASK 0x1
+
+#endif
diff --git a/drivers/regulator/mt6323-regulator.c b/drivers/regulator/mt6323-regulator.c
new file mode 100644
index 000000000..b7b9670f0
--- /dev/null
+++ b/drivers/regulator/mt6323-regulator.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Chen Zhong <chen.zhong@mediatek.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6323/registers.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/mt6323-regulator.h>
+#include <linux/regulator/of_regulator.h>
+
+#define MT6323_LDO_MODE_NORMAL 0
+#define MT6323_LDO_MODE_LP 1
+
+/*
+ * MT6323 regulators' information
+ *
+ * @desc: standard fields of regulator description.
+ * @qi: Mask for query enable signal status of regulators
+ * @vselon_reg: Register sections for hardware control mode of bucks
+ * @vselctrl_reg: Register for controlling the buck control mode.
+ * @vselctrl_mask: Mask for query buck's voltage control mode.
+ */
+struct mt6323_regulator_info {
+ struct regulator_desc desc;
+ u32 qi;
+ u32 vselon_reg;
+ u32 vselctrl_reg;
+ u32 vselctrl_mask;
+ u32 modeset_reg;
+ u32 modeset_mask;
+};
+
+#define MT6323_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \
+ vosel, vosel_mask, voselon, vosel_ctrl) \
+[MT6323_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6323_volt_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6323_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = (max - min)/step + 1, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = vosel, \
+ .vsel_mask = vosel_mask, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(0), \
+ }, \
+ .qi = BIT(13), \
+ .vselon_reg = voselon, \
+ .vselctrl_reg = vosel_ctrl, \
+ .vselctrl_mask = BIT(1), \
+}
+
+#define MT6323_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \
+ vosel_mask, _modeset_reg, _modeset_mask) \
+[MT6323_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6323_volt_table_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6323_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(ldo_volt_table), \
+ .volt_table = ldo_volt_table, \
+ .vsel_reg = vosel, \
+ .vsel_mask = vosel_mask, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ }, \
+ .qi = BIT(15), \
+ .modeset_reg = _modeset_reg, \
+ .modeset_mask = _modeset_mask, \
+}
+
+#define MT6323_REG_FIXED(match, vreg, enreg, enbit, volt, \
+ _modeset_reg, _modeset_mask) \
+[MT6323_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6323_volt_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6323_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ .min_uV = volt, \
+ }, \
+ .qi = BIT(15), \
+ .modeset_reg = _modeset_reg, \
+ .modeset_mask = _modeset_mask, \
+}
+
+static const struct regulator_linear_range buck_volt_range1[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250),
+};
+
+static const struct regulator_linear_range buck_volt_range2[] = {
+ REGULATOR_LINEAR_RANGE(1400000, 0, 0x7f, 12500),
+};
+
+static const struct regulator_linear_range buck_volt_range3[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0, 0x3f, 50000),
+};
+
+static const u32 ldo_volt_table1[] = {
+ 3300000, 3400000, 3500000, 3600000,
+};
+
+static const u32 ldo_volt_table2[] = {
+ 1500000, 1800000, 2500000, 2800000,
+};
+
+static const u32 ldo_volt_table3[] = {
+ 1800000, 3300000,
+};
+
+static const u32 ldo_volt_table4[] = {
+ 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table5[] = {
+ 1200000, 1300000, 1500000, 1800000, 2000000, 2800000, 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table6[] = {
+ 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 2000000,
+};
+
+static const u32 ldo_volt_table7[] = {
+ 1200000, 1300000, 1500000, 1800000,
+};
+
+static const u32 ldo_volt_table8[] = {
+ 1800000, 3000000,
+};
+
+static const u32 ldo_volt_table9[] = {
+ 1200000, 1350000, 1500000, 1800000,
+};
+
+static const u32 ldo_volt_table10[] = {
+ 1200000, 1300000, 1500000, 1800000,
+};
+
+static int mt6323_get_status(struct regulator_dev *rdev)
+{
+ int ret;
+ u32 regval;
+ struct mt6323_regulator_info *info = rdev_get_drvdata(rdev);
+
+ ret = regmap_read(rdev->regmap, info->desc.enable_reg, &regval);
+ if (ret != 0) {
+ dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret);
+ return ret;
+ }
+
+ return (regval & info->qi) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF;
+}
+
+static int mt6323_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ int ret, val = 0;
+ struct mt6323_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (!info->modeset_mask) {
+ dev_err(&rdev->dev, "regulator %s doesn't support set_mode\n",
+ info->desc.name);
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY:
+ val = MT6323_LDO_MODE_LP;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = MT6323_LDO_MODE_NORMAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= ffs(info->modeset_mask) - 1;
+
+ ret = regmap_update_bits(rdev->regmap, info->modeset_reg,
+ info->modeset_mask, val);
+
+ return ret;
+}
+
+static unsigned int mt6323_ldo_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ unsigned int mode;
+ int ret;
+ struct mt6323_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (!info->modeset_mask) {
+ dev_err(&rdev->dev, "regulator %s doesn't support get_mode\n",
+ info->desc.name);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(rdev->regmap, info->modeset_reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= info->modeset_mask;
+ val >>= ffs(info->modeset_mask) - 1;
+
+ if (val & 0x1)
+ mode = REGULATOR_MODE_STANDBY;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static const struct regulator_ops mt6323_volt_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6323_get_status,
+};
+
+static const struct regulator_ops mt6323_volt_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6323_get_status,
+ .set_mode = mt6323_ldo_set_mode,
+ .get_mode = mt6323_ldo_get_mode,
+};
+
+static const struct regulator_ops mt6323_volt_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6323_get_status,
+ .set_mode = mt6323_ldo_set_mode,
+ .get_mode = mt6323_ldo_get_mode,
+};
+
+/* The array is indexed by id(MT6323_ID_XXX) */
+static struct mt6323_regulator_info mt6323_regulators[] = {
+ MT6323_BUCK("buck_vproc", VPROC, 700000, 1493750, 6250,
+ buck_volt_range1, MT6323_VPROC_CON7, MT6323_VPROC_CON9, 0x7f,
+ MT6323_VPROC_CON10, MT6323_VPROC_CON5),
+ MT6323_BUCK("buck_vsys", VSYS, 1400000, 2987500, 12500,
+ buck_volt_range2, MT6323_VSYS_CON7, MT6323_VSYS_CON9, 0x7f,
+ MT6323_VSYS_CON10, MT6323_VSYS_CON5),
+ MT6323_BUCK("buck_vpa", VPA, 500000, 3650000, 50000,
+ buck_volt_range3, MT6323_VPA_CON7, MT6323_VPA_CON9,
+ 0x3f, MT6323_VPA_CON10, MT6323_VPA_CON5),
+ MT6323_REG_FIXED("ldo_vtcxo", VTCXO, MT6323_ANALDO_CON1, 10, 2800000,
+ MT6323_ANALDO_CON1, 0x2),
+ MT6323_REG_FIXED("ldo_vcn28", VCN28, MT6323_ANALDO_CON19, 12, 2800000,
+ MT6323_ANALDO_CON20, 0x2),
+ MT6323_LDO("ldo_vcn33_bt", VCN33_BT, ldo_volt_table1,
+ MT6323_ANALDO_CON16, 7, MT6323_ANALDO_CON16, 0xC,
+ MT6323_ANALDO_CON21, 0x2),
+ MT6323_LDO("ldo_vcn33_wifi", VCN33_WIFI, ldo_volt_table1,
+ MT6323_ANALDO_CON17, 12, MT6323_ANALDO_CON16, 0xC,
+ MT6323_ANALDO_CON21, 0x2),
+ MT6323_REG_FIXED("ldo_va", VA, MT6323_ANALDO_CON2, 14, 2800000,
+ MT6323_ANALDO_CON2, 0x2),
+ MT6323_LDO("ldo_vcama", VCAMA, ldo_volt_table2,
+ MT6323_ANALDO_CON4, 15, MT6323_ANALDO_CON10, 0x60, -1, 0),
+ MT6323_REG_FIXED("ldo_vio28", VIO28, MT6323_DIGLDO_CON0, 14, 2800000,
+ MT6323_DIGLDO_CON0, 0x2),
+ MT6323_REG_FIXED("ldo_vusb", VUSB, MT6323_DIGLDO_CON2, 14, 3300000,
+ MT6323_DIGLDO_CON2, 0x2),
+ MT6323_LDO("ldo_vmc", VMC, ldo_volt_table3,
+ MT6323_DIGLDO_CON3, 12, MT6323_DIGLDO_CON24, 0x10,
+ MT6323_DIGLDO_CON3, 0x2),
+ MT6323_LDO("ldo_vmch", VMCH, ldo_volt_table4,
+ MT6323_DIGLDO_CON5, 14, MT6323_DIGLDO_CON26, 0x80,
+ MT6323_DIGLDO_CON5, 0x2),
+ MT6323_LDO("ldo_vemc3v3", VEMC3V3, ldo_volt_table4,
+ MT6323_DIGLDO_CON6, 14, MT6323_DIGLDO_CON27, 0x80,
+ MT6323_DIGLDO_CON6, 0x2),
+ MT6323_LDO("ldo_vgp1", VGP1, ldo_volt_table5,
+ MT6323_DIGLDO_CON7, 15, MT6323_DIGLDO_CON28, 0xE0,
+ MT6323_DIGLDO_CON7, 0x2),
+ MT6323_LDO("ldo_vgp2", VGP2, ldo_volt_table6,
+ MT6323_DIGLDO_CON8, 15, MT6323_DIGLDO_CON29, 0xE0,
+ MT6323_DIGLDO_CON8, 0x2),
+ MT6323_LDO("ldo_vgp3", VGP3, ldo_volt_table7,
+ MT6323_DIGLDO_CON9, 15, MT6323_DIGLDO_CON30, 0x60,
+ MT6323_DIGLDO_CON9, 0x2),
+ MT6323_REG_FIXED("ldo_vcn18", VCN18, MT6323_DIGLDO_CON11, 14, 1800000,
+ MT6323_DIGLDO_CON11, 0x2),
+ MT6323_LDO("ldo_vsim1", VSIM1, ldo_volt_table8,
+ MT6323_DIGLDO_CON13, 15, MT6323_DIGLDO_CON34, 0x20,
+ MT6323_DIGLDO_CON13, 0x2),
+ MT6323_LDO("ldo_vsim2", VSIM2, ldo_volt_table8,
+ MT6323_DIGLDO_CON14, 15, MT6323_DIGLDO_CON35, 0x20,
+ MT6323_DIGLDO_CON14, 0x2),
+ MT6323_REG_FIXED("ldo_vrtc", VRTC, MT6323_DIGLDO_CON15, 8, 2800000,
+ -1, 0),
+ MT6323_LDO("ldo_vcamaf", VCAMAF, ldo_volt_table5,
+ MT6323_DIGLDO_CON31, 15, MT6323_DIGLDO_CON32, 0xE0,
+ MT6323_DIGLDO_CON31, 0x2),
+ MT6323_LDO("ldo_vibr", VIBR, ldo_volt_table5,
+ MT6323_DIGLDO_CON39, 15, MT6323_DIGLDO_CON40, 0xE0,
+ MT6323_DIGLDO_CON39, 0x2),
+ MT6323_REG_FIXED("ldo_vrf18", VRF18, MT6323_DIGLDO_CON45, 15, 1825000,
+ MT6323_DIGLDO_CON45, 0x2),
+ MT6323_LDO("ldo_vm", VM, ldo_volt_table9,
+ MT6323_DIGLDO_CON47, 14, MT6323_DIGLDO_CON48, 0x30,
+ MT6323_DIGLDO_CON47, 0x2),
+ MT6323_REG_FIXED("ldo_vio18", VIO18, MT6323_DIGLDO_CON49, 14, 1800000,
+ MT6323_DIGLDO_CON49, 0x2),
+ MT6323_LDO("ldo_vcamd", VCAMD, ldo_volt_table10,
+ MT6323_DIGLDO_CON51, 14, MT6323_DIGLDO_CON52, 0x60,
+ MT6323_DIGLDO_CON51, 0x2),
+ MT6323_REG_FIXED("ldo_vcamio", VCAMIO, MT6323_DIGLDO_CON53, 14, 1800000,
+ MT6323_DIGLDO_CON53, 0x2),
+};
+
+static int mt6323_set_buck_vosel_reg(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6323 = dev_get_drvdata(pdev->dev.parent);
+ int i;
+ u32 regval;
+
+ for (i = 0; i < MT6323_MAX_REGULATOR; i++) {
+ if (mt6323_regulators[i].vselctrl_reg) {
+ if (regmap_read(mt6323->regmap,
+ mt6323_regulators[i].vselctrl_reg,
+ &regval) < 0) {
+ dev_err(&pdev->dev,
+ "Failed to read buck ctrl\n");
+ return -EIO;
+ }
+
+ if (regval & mt6323_regulators[i].vselctrl_mask) {
+ mt6323_regulators[i].desc.vsel_reg =
+ mt6323_regulators[i].vselon_reg;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mt6323_regulator_probe(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6323 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = {};
+ struct regulator_dev *rdev;
+ int i;
+ u32 reg_value;
+
+ /* Query buck controller to select activated voltage register part */
+ if (mt6323_set_buck_vosel_reg(pdev))
+ return -EIO;
+
+ /* Read PMIC chip revision to update constraints and voltage table */
+ if (regmap_read(mt6323->regmap, MT6323_CID, &reg_value) < 0) {
+ dev_err(&pdev->dev, "Failed to read Chip ID\n");
+ return -EIO;
+ }
+ dev_info(&pdev->dev, "Chip ID = 0x%x\n", reg_value);
+
+ for (i = 0; i < MT6323_MAX_REGULATOR; i++) {
+ config.dev = &pdev->dev;
+ config.driver_data = &mt6323_regulators[i];
+ config.regmap = mt6323->regmap;
+ rdev = devm_regulator_register(&pdev->dev,
+ &mt6323_regulators[i].desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ mt6323_regulators[i].desc.name);
+ return PTR_ERR(rdev);
+ }
+ }
+ return 0;
+}
+
+static const struct platform_device_id mt6323_platform_ids[] = {
+ {"mt6323-regulator", 0},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6323_platform_ids);
+
+static struct platform_driver mt6323_regulator_driver = {
+ .driver = {
+ .name = "mt6323-regulator",
+ },
+ .probe = mt6323_regulator_probe,
+ .id_table = mt6323_platform_ids,
+};
+
+module_platform_driver(mt6323_regulator_driver);
+
+MODULE_AUTHOR("Chen Zhong <chen.zhong@mediatek.com>");
+MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6323 PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/mt6380-regulator.c b/drivers/regulator/mt6380-regulator.c
new file mode 100644
index 000000000..127dd720c
--- /dev/null
+++ b/drivers/regulator/mt6380-regulator.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ * Author: Chenglin Xu <chenglin.xu@mediatek.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/mt6380-regulator.h>
+#include <linux/regulator/of_regulator.h>
+
+/* PMIC Registers */
+#define MT6380_ALDO_CON_0 0x0000
+#define MT6380_BTLDO_CON_0 0x0004
+#define MT6380_COMP_CON_0 0x0008
+#define MT6380_CPUBUCK_CON_0 0x000C
+#define MT6380_CPUBUCK_CON_1 0x0010
+#define MT6380_CPUBUCK_CON_2 0x0014
+#define MT6380_DDRLDO_CON_0 0x0018
+#define MT6380_MLDO_CON_0 0x001C
+#define MT6380_PALDO_CON_0 0x0020
+#define MT6380_PHYLDO_CON_0 0x0024
+#define MT6380_SIDO_CON_0 0x0028
+#define MT6380_SIDO_CON_1 0x002C
+#define MT6380_SIDO_CON_2 0x0030
+#define MT6380_SLDO_CON_0 0x0034
+#define MT6380_TLDO_CON_0 0x0038
+#define MT6380_STARTUP_CON_0 0x003C
+#define MT6380_STARTUP_CON_1 0x0040
+#define MT6380_SMPS_TOP_CON_0 0x0044
+#define MT6380_SMPS_TOP_CON_1 0x0048
+#define MT6380_ANA_CTRL_0 0x0050
+#define MT6380_ANA_CTRL_1 0x0054
+#define MT6380_ANA_CTRL_2 0x0058
+#define MT6380_ANA_CTRL_3 0x005C
+#define MT6380_ANA_CTRL_4 0x0060
+#define MT6380_SPK_CON9 0x0064
+#define MT6380_SPK_CON11 0x0068
+#define MT6380_SPK_CON12 0x006A
+#define MT6380_CLK_CTRL 0x0070
+#define MT6380_PINMUX_CTRL 0x0074
+#define MT6380_IO_CTRL 0x0078
+#define MT6380_SLP_MODE_CTRL_0 0x007C
+#define MT6380_SLP_MODE_CTRL_1 0x0080
+#define MT6380_SLP_MODE_CTRL_2 0x0084
+#define MT6380_SLP_MODE_CTRL_3 0x0088
+#define MT6380_SLP_MODE_CTRL_4 0x008C
+#define MT6380_SLP_MODE_CTRL_5 0x0090
+#define MT6380_SLP_MODE_CTRL_6 0x0094
+#define MT6380_SLP_MODE_CTRL_7 0x0098
+#define MT6380_SLP_MODE_CTRL_8 0x009C
+#define MT6380_FCAL_CTRL_0 0x00A0
+#define MT6380_FCAL_CTRL_1 0x00A4
+#define MT6380_LDO_CTRL_0 0x00A8
+#define MT6380_LDO_CTRL_1 0x00AC
+#define MT6380_LDO_CTRL_2 0x00B0
+#define MT6380_LDO_CTRL_3 0x00B4
+#define MT6380_LDO_CTRL_4 0x00B8
+#define MT6380_DEBUG_CTRL_0 0x00BC
+#define MT6380_EFU_CTRL_0 0x0200
+#define MT6380_EFU_CTRL_1 0x0201
+#define MT6380_EFU_CTRL_2 0x0202
+#define MT6380_EFU_CTRL_3 0x0203
+#define MT6380_EFU_CTRL_4 0x0204
+#define MT6380_EFU_CTRL_5 0x0205
+#define MT6380_EFU_CTRL_6 0x0206
+#define MT6380_EFU_CTRL_7 0x0207
+#define MT6380_EFU_CTRL_8 0x0208
+
+#define MT6380_REGULATOR_MODE_AUTO 0
+#define MT6380_REGULATOR_MODE_FORCE_PWM 1
+
+/*
+ * mt6380 regulators' information
+ *
+ * @desc: standard fields of regulator description
+ * @vselon_reg: Register sections for hardware control mode of bucks
+ * @modeset_reg: Register for controlling the buck/LDO control mode
+ * @modeset_mask: Mask for controlling the buck/LDO control mode
+ */
+struct mt6380_regulator_info {
+ struct regulator_desc desc;
+ u32 vselon_reg;
+ u32 modeset_reg;
+ u32 modeset_mask;
+};
+
+#define MT6380_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \
+ vosel, vosel_mask, enbit, voselon, _modeset_reg, \
+ _modeset_mask) \
+[MT6380_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6380_volt_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6380_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = vosel, \
+ .vsel_mask = vosel_mask, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ }, \
+ .vselon_reg = voselon, \
+ .modeset_reg = _modeset_reg, \
+ .modeset_mask = _modeset_mask, \
+}
+
+#define MT6380_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \
+ vosel_mask, _modeset_reg, _modeset_mask) \
+[MT6380_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6380_volt_table_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6380_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(ldo_volt_table), \
+ .volt_table = ldo_volt_table, \
+ .vsel_reg = vosel, \
+ .vsel_mask = vosel_mask, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ }, \
+ .modeset_reg = _modeset_reg, \
+ .modeset_mask = _modeset_mask, \
+}
+
+#define MT6380_REG_FIXED(match, vreg, enreg, enbit, volt, \
+ _modeset_reg, _modeset_mask) \
+[MT6380_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6380_volt_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6380_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ .min_uV = volt, \
+ }, \
+ .modeset_reg = _modeset_reg, \
+ .modeset_mask = _modeset_mask, \
+}
+
+static const struct regulator_linear_range buck_volt_range1[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 0xfe, 6250),
+};
+
+static const struct regulator_linear_range buck_volt_range2[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0, 0xfe, 6250),
+};
+
+static const struct regulator_linear_range buck_volt_range3[] = {
+ REGULATOR_LINEAR_RANGE(1200000, 0, 0x3c, 25000),
+};
+
+static const u32 ldo_volt_table1[] = {
+ 1400000, 1350000, 1300000, 1250000, 1200000, 1150000, 1100000, 1050000,
+};
+
+static const u32 ldo_volt_table2[] = {
+ 2200000, 3300000,
+};
+
+static const u32 ldo_volt_table3[] = {
+ 1240000, 1390000, 1540000, 1840000,
+};
+
+static const u32 ldo_volt_table4[] = {
+ 2200000, 3300000,
+};
+
+static int mt6380_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int ret, val = 0;
+ struct mt6380_regulator_info *info = rdev_get_drvdata(rdev);
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = MT6380_REGULATOR_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_FAST:
+ val = MT6380_REGULATOR_MODE_FORCE_PWM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= ffs(info->modeset_mask) - 1;
+
+ ret = regmap_update_bits(rdev->regmap, info->modeset_reg,
+ info->modeset_mask, val);
+
+ return ret;
+}
+
+static unsigned int mt6380_regulator_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ unsigned int mode;
+ int ret;
+ struct mt6380_regulator_info *info = rdev_get_drvdata(rdev);
+
+ ret = regmap_read(rdev->regmap, info->modeset_reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= info->modeset_mask;
+ val >>= ffs(info->modeset_mask) - 1;
+
+ switch (val) {
+ case MT6380_REGULATOR_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case MT6380_REGULATOR_MODE_FORCE_PWM:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mode;
+}
+
+static const struct regulator_ops mt6380_volt_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_mode = mt6380_regulator_set_mode,
+ .get_mode = mt6380_regulator_get_mode,
+};
+
+static const struct regulator_ops mt6380_volt_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_mode = mt6380_regulator_set_mode,
+ .get_mode = mt6380_regulator_get_mode,
+};
+
+static const struct regulator_ops mt6380_volt_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_mode = mt6380_regulator_set_mode,
+ .get_mode = mt6380_regulator_get_mode,
+};
+
+/* The array is indexed by id(MT6380_ID_XXX) */
+static struct mt6380_regulator_info mt6380_regulators[] = {
+ MT6380_BUCK("buck-vcore1", VCPU, 600000, 1393750, 6250,
+ buck_volt_range1, MT6380_ANA_CTRL_3, MT6380_ANA_CTRL_1,
+ 0xfe, 3, MT6380_ANA_CTRL_1,
+ MT6380_CPUBUCK_CON_0, 0x8000000),
+ MT6380_BUCK("buck-vcore", VCORE, 600000, 1393750, 6250,
+ buck_volt_range2, MT6380_ANA_CTRL_3, MT6380_ANA_CTRL_2,
+ 0xfe, 2, MT6380_ANA_CTRL_2, MT6380_SIDO_CON_0, 0x1000000),
+ MT6380_BUCK("buck-vrf", VRF, 1200000, 1575000, 25000,
+ buck_volt_range3, MT6380_ANA_CTRL_3, MT6380_SIDO_CON_0,
+ 0x78, 1, MT6380_SIDO_CON_0, MT6380_SIDO_CON_0, 0x8000),
+ MT6380_LDO("ldo-vm", VMLDO, ldo_volt_table1, MT6380_LDO_CTRL_0,
+ 1, MT6380_MLDO_CON_0, 0xE000, MT6380_ANA_CTRL_1, 0x4000000),
+ MT6380_LDO("ldo-va", VALDO, ldo_volt_table2, MT6380_LDO_CTRL_0,
+ 2, MT6380_ALDO_CON_0, 0x400, MT6380_ALDO_CON_0, 0x20),
+ MT6380_REG_FIXED("ldo-vphy", VPHYLDO, MT6380_LDO_CTRL_0, 7, 1800000,
+ MT6380_PHYLDO_CON_0, 0x80),
+ MT6380_LDO("ldo-vddr", VDDRLDO, ldo_volt_table3, MT6380_LDO_CTRL_0,
+ 8, MT6380_DDRLDO_CON_0, 0x3000, MT6380_DDRLDO_CON_0, 0x80),
+ MT6380_LDO("ldo-vt", VTLDO, ldo_volt_table4, MT6380_LDO_CTRL_0, 3,
+ MT6380_TLDO_CON_0, 0x400, MT6380_TLDO_CON_0, 0x20),
+};
+
+static int mt6380_regulator_probe(struct platform_device *pdev)
+{
+ struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ struct regulator_config config = {};
+ struct regulator_dev *rdev;
+ int i;
+
+ for (i = 0; i < MT6380_MAX_REGULATOR; i++) {
+ config.dev = &pdev->dev;
+ config.driver_data = &mt6380_regulators[i];
+ config.regmap = regmap;
+ rdev = devm_regulator_register(&pdev->dev,
+ &mt6380_regulators[i].desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ mt6380_regulators[i].desc.name);
+ return PTR_ERR(rdev);
+ }
+ }
+ return 0;
+}
+
+static const struct platform_device_id mt6380_platform_ids[] = {
+ {"mt6380-regulator", 0},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6380_platform_ids);
+
+static const struct of_device_id mt6380_of_match[] = {
+ { .compatible = "mediatek,mt6380-regulator", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt6380_of_match);
+
+static struct platform_driver mt6380_regulator_driver = {
+ .driver = {
+ .name = "mt6380-regulator",
+ .of_match_table = of_match_ptr(mt6380_of_match),
+ },
+ .probe = mt6380_regulator_probe,
+ .id_table = mt6380_platform_ids,
+};
+
+module_platform_driver(mt6380_regulator_driver);
+
+MODULE_AUTHOR("Chenglin Xu <chenglin.xu@mediatek.com>");
+MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6380 PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/mt6397-regulator.c b/drivers/regulator/mt6397-regulator.c
new file mode 100644
index 000000000..c6c6aa85e
--- /dev/null
+++ b/drivers/regulator/mt6397-regulator.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Flora Fu <flora.fu@mediatek.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6397/registers.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/mt6397-regulator.h>
+#include <linux/regulator/of_regulator.h>
+
+#define MT6397_BUCK_MODE_AUTO 0
+#define MT6397_BUCK_MODE_FORCE_PWM 1
+
+/*
+ * MT6397 regulators' information
+ *
+ * @desc: standard fields of regulator description.
+ * @qi: Mask for query enable signal status of regulators
+ * @vselon_reg: Register sections for hardware control mode of bucks
+ * @vselctrl_reg: Register for controlling the buck control mode.
+ * @vselctrl_mask: Mask for query buck's voltage control mode.
+ */
+struct mt6397_regulator_info {
+ struct regulator_desc desc;
+ u32 qi;
+ u32 vselon_reg;
+ u32 vselctrl_reg;
+ u32 vselctrl_mask;
+ u32 modeset_reg;
+ u32 modeset_mask;
+ u32 modeset_shift;
+};
+
+#define MT6397_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \
+ vosel, vosel_mask, voselon, vosel_ctrl, _modeset_reg, \
+ _modeset_shift) \
+[MT6397_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6397_volt_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6397_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = (max - min)/step + 1, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = vosel, \
+ .vsel_mask = vosel_mask, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(0), \
+ }, \
+ .qi = BIT(13), \
+ .vselon_reg = voselon, \
+ .vselctrl_reg = vosel_ctrl, \
+ .vselctrl_mask = BIT(1), \
+ .modeset_reg = _modeset_reg, \
+ .modeset_mask = BIT(_modeset_shift), \
+ .modeset_shift = _modeset_shift \
+}
+
+#define MT6397_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \
+ vosel_mask) \
+[MT6397_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6397_volt_table_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6397_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(ldo_volt_table), \
+ .volt_table = ldo_volt_table, \
+ .vsel_reg = vosel, \
+ .vsel_mask = vosel_mask, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ }, \
+ .qi = BIT(15), \
+}
+
+#define MT6397_REG_FIXED(match, vreg, enreg, enbit, volt) \
+[MT6397_ID_##vreg] = { \
+ .desc = { \
+ .name = #vreg, \
+ .of_match = of_match_ptr(match), \
+ .ops = &mt6397_volt_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = MT6397_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .enable_reg = enreg, \
+ .enable_mask = BIT(enbit), \
+ .min_uV = volt, \
+ }, \
+ .qi = BIT(15), \
+}
+
+static const struct regulator_linear_range buck_volt_range1[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250),
+};
+
+static const struct regulator_linear_range buck_volt_range2[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0, 0x7f, 6250),
+};
+
+static const struct regulator_linear_range buck_volt_range3[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0, 0x1f, 20000),
+};
+
+static const u32 ldo_volt_table1[] = {
+ 1500000, 1800000, 2500000, 2800000,
+};
+
+static const u32 ldo_volt_table2[] = {
+ 1800000, 3300000,
+};
+
+static const u32 ldo_volt_table3[] = {
+ 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table4[] = {
+ 1220000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table5[] = {
+ 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table5_v2[] = {
+ 1200000, 1000000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000,
+};
+
+static const u32 ldo_volt_table6[] = {
+ 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 2000000,
+};
+
+static const u32 ldo_volt_table7[] = {
+ 1300000, 1500000, 1800000, 2000000, 2500000, 2800000, 3000000, 3300000,
+};
+
+static int mt6397_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct mt6397_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret, val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = MT6397_BUCK_MODE_FORCE_PWM;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = MT6397_BUCK_MODE_AUTO;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_mode;
+ }
+
+ dev_dbg(&rdev->dev, "mt6397 buck set_mode %#x, %#x, %#x, %#x\n",
+ info->modeset_reg, info->modeset_mask,
+ info->modeset_shift, val);
+
+ val <<= info->modeset_shift;
+ ret = regmap_update_bits(rdev->regmap, info->modeset_reg,
+ info->modeset_mask, val);
+err_mode:
+ if (ret != 0) {
+ dev_err(&rdev->dev,
+ "Failed to set mt6397 buck mode: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static unsigned int mt6397_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct mt6397_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret, regval;
+
+ ret = regmap_read(rdev->regmap, info->modeset_reg, &regval);
+ if (ret != 0) {
+ dev_err(&rdev->dev,
+ "Failed to get mt6397 buck mode: %d\n", ret);
+ return ret;
+ }
+
+ switch ((regval & info->modeset_mask) >> info->modeset_shift) {
+ case MT6397_BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ case MT6397_BUCK_MODE_FORCE_PWM:
+ return REGULATOR_MODE_FAST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt6397_get_status(struct regulator_dev *rdev)
+{
+ int ret;
+ u32 regval;
+ struct mt6397_regulator_info *info = rdev_get_drvdata(rdev);
+
+ ret = regmap_read(rdev->regmap, info->desc.enable_reg, &regval);
+ if (ret != 0) {
+ dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret);
+ return ret;
+ }
+
+ return (regval & info->qi) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF;
+}
+
+static const struct regulator_ops mt6397_volt_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6397_get_status,
+ .set_mode = mt6397_regulator_set_mode,
+ .get_mode = mt6397_regulator_get_mode,
+};
+
+static const struct regulator_ops mt6397_volt_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6397_get_status,
+};
+
+static const struct regulator_ops mt6397_volt_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_status = mt6397_get_status,
+};
+
+/* The array is indexed by id(MT6397_ID_XXX) */
+static struct mt6397_regulator_info mt6397_regulators[] = {
+ MT6397_BUCK("buck_vpca15", VPCA15, 700000, 1493750, 6250,
+ buck_volt_range1, MT6397_VCA15_CON7, MT6397_VCA15_CON9, 0x7f,
+ MT6397_VCA15_CON10, MT6397_VCA15_CON5, MT6397_VCA15_CON2, 11),
+ MT6397_BUCK("buck_vpca7", VPCA7, 700000, 1493750, 6250,
+ buck_volt_range1, MT6397_VPCA7_CON7, MT6397_VPCA7_CON9, 0x7f,
+ MT6397_VPCA7_CON10, MT6397_VPCA7_CON5, MT6397_VPCA7_CON2, 8),
+ MT6397_BUCK("buck_vsramca15", VSRAMCA15, 700000, 1493750, 6250,
+ buck_volt_range1, MT6397_VSRMCA15_CON7, MT6397_VSRMCA15_CON9,
+ 0x7f, MT6397_VSRMCA15_CON10, MT6397_VSRMCA15_CON5,
+ MT6397_VSRMCA15_CON2, 8),
+ MT6397_BUCK("buck_vsramca7", VSRAMCA7, 700000, 1493750, 6250,
+ buck_volt_range1, MT6397_VSRMCA7_CON7, MT6397_VSRMCA7_CON9,
+ 0x7f, MT6397_VSRMCA7_CON10, MT6397_VSRMCA7_CON5,
+ MT6397_VSRMCA7_CON2, 8),
+ MT6397_BUCK("buck_vcore", VCORE, 700000, 1493750, 6250,
+ buck_volt_range1, MT6397_VCORE_CON7, MT6397_VCORE_CON9, 0x7f,
+ MT6397_VCORE_CON10, MT6397_VCORE_CON5, MT6397_VCORE_CON2, 8),
+ MT6397_BUCK("buck_vgpu", VGPU, 700000, 1493750, 6250, buck_volt_range1,
+ MT6397_VGPU_CON7, MT6397_VGPU_CON9, 0x7f,
+ MT6397_VGPU_CON10, MT6397_VGPU_CON5, MT6397_VGPU_CON2, 8),
+ MT6397_BUCK("buck_vdrm", VDRM, 800000, 1593750, 6250, buck_volt_range2,
+ MT6397_VDRM_CON7, MT6397_VDRM_CON9, 0x7f,
+ MT6397_VDRM_CON10, MT6397_VDRM_CON5, MT6397_VDRM_CON2, 8),
+ MT6397_BUCK("buck_vio18", VIO18, 1500000, 2120000, 20000,
+ buck_volt_range3, MT6397_VIO18_CON7, MT6397_VIO18_CON9, 0x1f,
+ MT6397_VIO18_CON10, MT6397_VIO18_CON5, MT6397_VIO18_CON2, 8),
+ MT6397_REG_FIXED("ldo_vtcxo", VTCXO, MT6397_ANALDO_CON0, 10, 2800000),
+ MT6397_REG_FIXED("ldo_va28", VA28, MT6397_ANALDO_CON1, 14, 2800000),
+ MT6397_LDO("ldo_vcama", VCAMA, ldo_volt_table1,
+ MT6397_ANALDO_CON2, 15, MT6397_ANALDO_CON6, 0xC0),
+ MT6397_REG_FIXED("ldo_vio28", VIO28, MT6397_DIGLDO_CON0, 14, 2800000),
+ MT6397_REG_FIXED("ldo_vusb", VUSB, MT6397_DIGLDO_CON1, 14, 3300000),
+ MT6397_LDO("ldo_vmc", VMC, ldo_volt_table2,
+ MT6397_DIGLDO_CON2, 12, MT6397_DIGLDO_CON29, 0x10),
+ MT6397_LDO("ldo_vmch", VMCH, ldo_volt_table3,
+ MT6397_DIGLDO_CON3, 14, MT6397_DIGLDO_CON17, 0x80),
+ MT6397_LDO("ldo_vemc3v3", VEMC3V3, ldo_volt_table3,
+ MT6397_DIGLDO_CON4, 14, MT6397_DIGLDO_CON18, 0x10),
+ MT6397_LDO("ldo_vgp1", VGP1, ldo_volt_table4,
+ MT6397_DIGLDO_CON5, 15, MT6397_DIGLDO_CON19, 0xE0),
+ MT6397_LDO("ldo_vgp2", VGP2, ldo_volt_table5,
+ MT6397_DIGLDO_CON6, 15, MT6397_DIGLDO_CON20, 0xE0),
+ MT6397_LDO("ldo_vgp3", VGP3, ldo_volt_table5,
+ MT6397_DIGLDO_CON7, 15, MT6397_DIGLDO_CON21, 0xE0),
+ MT6397_LDO("ldo_vgp4", VGP4, ldo_volt_table5,
+ MT6397_DIGLDO_CON8, 15, MT6397_DIGLDO_CON22, 0xE0),
+ MT6397_LDO("ldo_vgp5", VGP5, ldo_volt_table6,
+ MT6397_DIGLDO_CON9, 15, MT6397_DIGLDO_CON23, 0xE0),
+ MT6397_LDO("ldo_vgp6", VGP6, ldo_volt_table5,
+ MT6397_DIGLDO_CON10, 15, MT6397_DIGLDO_CON33, 0xE0),
+ MT6397_LDO("ldo_vibr", VIBR, ldo_volt_table7,
+ MT6397_DIGLDO_CON24, 15, MT6397_DIGLDO_CON25, 0xE00),
+};
+
+static int mt6397_set_buck_vosel_reg(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ int i;
+ u32 regval;
+
+ for (i = 0; i < MT6397_MAX_REGULATOR; i++) {
+ if (mt6397_regulators[i].vselctrl_reg) {
+ if (regmap_read(mt6397->regmap,
+ mt6397_regulators[i].vselctrl_reg,
+ &regval) < 0) {
+ dev_err(&pdev->dev,
+ "Failed to read buck ctrl\n");
+ return -EIO;
+ }
+
+ if (regval & mt6397_regulators[i].vselctrl_mask) {
+ mt6397_regulators[i].desc.vsel_reg =
+ mt6397_regulators[i].vselon_reg;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mt6397_regulator_probe(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = {};
+ struct regulator_dev *rdev;
+ int i;
+ u32 reg_value, version;
+
+ /* Query buck controller to select activated voltage register part */
+ if (mt6397_set_buck_vosel_reg(pdev))
+ return -EIO;
+
+ /* Read PMIC chip revision to update constraints and voltage table */
+ if (regmap_read(mt6397->regmap, MT6397_CID, &reg_value) < 0) {
+ dev_err(&pdev->dev, "Failed to read Chip ID\n");
+ return -EIO;
+ }
+ dev_info(&pdev->dev, "Chip ID = 0x%x\n", reg_value);
+
+ version = (reg_value & 0xFF);
+ switch (version) {
+ case MT6397_REGULATOR_ID91:
+ mt6397_regulators[MT6397_ID_VGP2].desc.volt_table =
+ ldo_volt_table5_v2;
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < MT6397_MAX_REGULATOR; i++) {
+ config.dev = &pdev->dev;
+ config.driver_data = &mt6397_regulators[i];
+ config.regmap = mt6397->regmap;
+ rdev = devm_regulator_register(&pdev->dev,
+ &mt6397_regulators[i].desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ mt6397_regulators[i].desc.name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id mt6397_platform_ids[] = {
+ {"mt6397-regulator", 0},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6397_platform_ids);
+
+static const struct of_device_id mt6397_of_match[] = {
+ { .compatible = "mediatek,mt6397-regulator", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt6397_of_match);
+
+static struct platform_driver mt6397_regulator_driver = {
+ .driver = {
+ .name = "mt6397-regulator",
+ .of_match_table = of_match_ptr(mt6397_of_match),
+ },
+ .probe = mt6397_regulator_probe,
+ .id_table = mt6397_platform_ids,
+};
+
+module_platform_driver(mt6397_regulator_driver);
+
+MODULE_AUTHOR("Flora Fu <flora.fu@mediatek.com>");
+MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6397 PMIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
new file mode 100644
index 000000000..b255590ae
--- /dev/null
+++ b/drivers/regulator/of_regulator.c
@@ -0,0 +1,586 @@
+/*
+ * OF helpers for regulator framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Rajendra Nayak <rnayak@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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#include "internal.h"
+
+static const char *const regulator_states[PM_SUSPEND_MAX + 1] = {
+ [PM_SUSPEND_MEM] = "regulator-state-mem",
+ [PM_SUSPEND_MAX] = "regulator-state-disk",
+};
+
+static void of_get_regulation_constraints(struct device_node *np,
+ struct regulator_init_data **init_data,
+ const struct regulator_desc *desc)
+{
+ struct regulation_constraints *constraints = &(*init_data)->constraints;
+ struct regulator_state *suspend_state;
+ struct device_node *suspend_np;
+ unsigned int mode;
+ int ret, i, len;
+ u32 pval;
+
+ constraints->name = of_get_property(np, "regulator-name", NULL);
+
+ if (!of_property_read_u32(np, "regulator-min-microvolt", &pval))
+ constraints->min_uV = pval;
+
+ if (!of_property_read_u32(np, "regulator-max-microvolt", &pval))
+ constraints->max_uV = pval;
+
+ /* Voltage change possible? */
+ if (constraints->min_uV != constraints->max_uV)
+ constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
+
+ /* Do we have a voltage range, if so try to apply it? */
+ if (constraints->min_uV && constraints->max_uV)
+ constraints->apply_uV = true;
+
+ if (!of_property_read_u32(np, "regulator-microvolt-offset", &pval))
+ constraints->uV_offset = pval;
+ if (!of_property_read_u32(np, "regulator-min-microamp", &pval))
+ constraints->min_uA = pval;
+ if (!of_property_read_u32(np, "regulator-max-microamp", &pval))
+ constraints->max_uA = pval;
+
+ if (!of_property_read_u32(np, "regulator-input-current-limit-microamp",
+ &pval))
+ constraints->ilim_uA = pval;
+
+ /* Current change possible? */
+ if (constraints->min_uA != constraints->max_uA)
+ constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
+
+ constraints->boot_on = of_property_read_bool(np, "regulator-boot-on");
+ constraints->always_on = of_property_read_bool(np, "regulator-always-on");
+ if (!constraints->always_on) /* status change should be possible. */
+ constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+
+ constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
+
+ if (of_property_read_bool(np, "regulator-allow-bypass"))
+ constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
+
+ if (of_property_read_bool(np, "regulator-allow-set-load"))
+ constraints->valid_ops_mask |= REGULATOR_CHANGE_DRMS;
+
+ ret = of_property_read_u32(np, "regulator-ramp-delay", &pval);
+ if (!ret) {
+ if (pval)
+ constraints->ramp_delay = pval;
+ else
+ constraints->ramp_disable = true;
+ }
+
+ ret = of_property_read_u32(np, "regulator-settling-time-us", &pval);
+ if (!ret)
+ constraints->settling_time = pval;
+
+ ret = of_property_read_u32(np, "regulator-settling-time-up-us", &pval);
+ if (!ret)
+ constraints->settling_time_up = pval;
+ if (constraints->settling_time_up && constraints->settling_time) {
+ pr_warn("%s: ambiguous configuration for settling time, ignoring 'regulator-settling-time-up-us'\n",
+ np->name);
+ constraints->settling_time_up = 0;
+ }
+
+ ret = of_property_read_u32(np, "regulator-settling-time-down-us",
+ &pval);
+ if (!ret)
+ constraints->settling_time_down = pval;
+ if (constraints->settling_time_down && constraints->settling_time) {
+ pr_warn("%s: ambiguous configuration for settling time, ignoring 'regulator-settling-time-down-us'\n",
+ np->name);
+ constraints->settling_time_down = 0;
+ }
+
+ ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval);
+ if (!ret)
+ constraints->enable_time = pval;
+
+ constraints->soft_start = of_property_read_bool(np,
+ "regulator-soft-start");
+ ret = of_property_read_u32(np, "regulator-active-discharge", &pval);
+ if (!ret) {
+ constraints->active_discharge =
+ (pval) ? REGULATOR_ACTIVE_DISCHARGE_ENABLE :
+ REGULATOR_ACTIVE_DISCHARGE_DISABLE;
+ }
+
+ if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
+ if (desc && desc->of_map_mode) {
+ mode = desc->of_map_mode(pval);
+ if (mode == REGULATOR_MODE_INVALID)
+ pr_err("%s: invalid mode %u\n", np->name, pval);
+ else
+ constraints->initial_mode = mode;
+ } else {
+ pr_warn("%s: mapping for mode %d not defined\n",
+ np->name, pval);
+ }
+ }
+
+ len = of_property_count_elems_of_size(np, "regulator-allowed-modes",
+ sizeof(u32));
+ if (len > 0) {
+ if (desc && desc->of_map_mode) {
+ for (i = 0; i < len; i++) {
+ ret = of_property_read_u32_index(np,
+ "regulator-allowed-modes", i, &pval);
+ if (ret) {
+ pr_err("%s: couldn't read allowed modes index %d, ret=%d\n",
+ np->name, i, ret);
+ break;
+ }
+ mode = desc->of_map_mode(pval);
+ if (mode == REGULATOR_MODE_INVALID)
+ pr_err("%s: invalid regulator-allowed-modes element %u\n",
+ np->name, pval);
+ else
+ constraints->valid_modes_mask |= mode;
+ }
+ if (constraints->valid_modes_mask)
+ constraints->valid_ops_mask
+ |= REGULATOR_CHANGE_MODE;
+ } else {
+ pr_warn("%s: mode mapping not defined\n", np->name);
+ }
+ }
+
+ if (!of_property_read_u32(np, "regulator-system-load", &pval))
+ constraints->system_load = pval;
+
+ if (!of_property_read_u32(np, "regulator-coupled-max-spread",
+ &pval))
+ constraints->max_spread = pval;
+
+ constraints->over_current_protection = of_property_read_bool(np,
+ "regulator-over-current-protection");
+
+ for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
+ switch (i) {
+ case PM_SUSPEND_MEM:
+ suspend_state = &constraints->state_mem;
+ break;
+ case PM_SUSPEND_MAX:
+ suspend_state = &constraints->state_disk;
+ break;
+ case PM_SUSPEND_ON:
+ case PM_SUSPEND_TO_IDLE:
+ case PM_SUSPEND_STANDBY:
+ default:
+ continue;
+ }
+
+ suspend_np = of_get_child_by_name(np, regulator_states[i]);
+ if (!suspend_np || !suspend_state)
+ continue;
+
+ if (!of_property_read_u32(suspend_np, "regulator-mode",
+ &pval)) {
+ if (desc && desc->of_map_mode) {
+ mode = desc->of_map_mode(pval);
+ if (mode == REGULATOR_MODE_INVALID)
+ pr_err("%s: invalid mode %u\n",
+ np->name, pval);
+ else
+ suspend_state->mode = mode;
+ } else {
+ pr_warn("%s: mapping for mode %d not defined\n",
+ np->name, pval);
+ }
+ }
+
+ if (of_property_read_bool(suspend_np,
+ "regulator-on-in-suspend"))
+ suspend_state->enabled = ENABLE_IN_SUSPEND;
+ else if (of_property_read_bool(suspend_np,
+ "regulator-off-in-suspend"))
+ suspend_state->enabled = DISABLE_IN_SUSPEND;
+
+ if (!of_property_read_u32(suspend_np,
+ "regulator-suspend-min-microvolt", &pval))
+ suspend_state->min_uV = pval;
+
+ if (!of_property_read_u32(suspend_np,
+ "regulator-suspend-max-microvolt", &pval))
+ suspend_state->max_uV = pval;
+
+ if (!of_property_read_u32(suspend_np,
+ "regulator-suspend-microvolt", &pval))
+ suspend_state->uV = pval;
+ else /* otherwise use min_uV as default suspend voltage */
+ suspend_state->uV = suspend_state->min_uV;
+
+ if (of_property_read_bool(suspend_np,
+ "regulator-changeable-in-suspend"))
+ suspend_state->changeable = true;
+
+ if (i == PM_SUSPEND_MEM)
+ constraints->initial_state = PM_SUSPEND_MEM;
+
+ of_node_put(suspend_np);
+ suspend_state = NULL;
+ suspend_np = NULL;
+ }
+}
+
+/**
+ * of_get_regulator_init_data - extract regulator_init_data structure info
+ * @dev: device requesting for regulator_init_data
+ * @node: regulator device node
+ * @desc: regulator description
+ *
+ * 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,
+ const struct regulator_desc *desc)
+{
+ 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, desc);
+ return init_data;
+}
+EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
+
+struct devm_of_regulator_matches {
+ struct of_regulator_match *matches;
+ unsigned int num_matches;
+};
+
+static void devm_of_regulator_put_matches(struct device *dev, void *res)
+{
+ struct devm_of_regulator_matches *devm_matches = res;
+ int i;
+
+ for (i = 0; i < devm_matches->num_matches; i++)
+ of_node_put(devm_matches->matches[i].of_node);
+}
+
+/**
+ * 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 and an additional of_node reference is taken for each matched
+ * regulator.
+ *
+ * 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;
+ struct devm_of_regulator_matches *devm_matches;
+
+ if (!dev || !node)
+ return -EINVAL;
+
+ devm_matches = devres_alloc(devm_of_regulator_put_matches,
+ sizeof(struct devm_of_regulator_matches),
+ GFP_KERNEL);
+ if (!devm_matches)
+ return -ENOMEM;
+
+ devm_matches->matches = matches;
+ devm_matches->num_matches = num_matches;
+
+ devres_add(dev, devm_matches);
+
+ 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,
+ match->desc);
+ if (!match->init_data) {
+ dev_err(dev,
+ "failed to parse DT for regulator %s\n",
+ child->name);
+ of_node_put(child);
+ return -EINVAL;
+ }
+ match->of_node = of_node_get(child);
+ count++;
+ break;
+ }
+ }
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(of_regulator_match);
+
+struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
+ const struct regulator_desc *desc,
+ struct regulator_config *config,
+ struct device_node **node)
+{
+ struct device_node *search, *child;
+ struct regulator_init_data *init_data = NULL;
+ const char *name;
+
+ if (!dev->of_node || !desc->of_match)
+ return NULL;
+
+ if (desc->regulators_node)
+ search = of_get_child_by_name(dev->of_node,
+ desc->regulators_node);
+ else
+ search = of_node_get(dev->of_node);
+
+ if (!search) {
+ dev_dbg(dev, "Failed to find regulator container node '%s'\n",
+ desc->regulators_node);
+ return NULL;
+ }
+
+ for_each_available_child_of_node(search, child) {
+ name = of_get_property(child, "regulator-compatible", NULL);
+ if (!name)
+ name = child->name;
+
+ if (strcmp(desc->of_match, name))
+ continue;
+
+ init_data = of_get_regulator_init_data(dev, child, desc);
+ if (!init_data) {
+ dev_err(dev,
+ "failed to parse DT for regulator %s\n",
+ child->name);
+ break;
+ }
+
+ if (desc->of_parse_cb) {
+ if (desc->of_parse_cb(child, desc, config)) {
+ dev_err(dev,
+ "driver callback failed to parse DT for regulator %s\n",
+ child->name);
+ init_data = NULL;
+ break;
+ }
+ }
+
+ of_node_get(child);
+ *node = child;
+ break;
+ }
+
+ of_node_put(search);
+
+ return init_data;
+}
+
+static int of_node_match(struct device *dev, const void *data)
+{
+ return dev->of_node == data;
+}
+
+struct regulator_dev *of_find_regulator_by_node(struct device_node *np)
+{
+ struct device *dev;
+
+ dev = class_find_device(&regulator_class, NULL, np, of_node_match);
+
+ return dev ? dev_to_rdev(dev) : NULL;
+}
+
+/*
+ * Returns number of regulators coupled with rdev.
+ */
+int of_get_n_coupled(struct regulator_dev *rdev)
+{
+ struct device_node *node = rdev->dev.of_node;
+ int n_phandles;
+
+ n_phandles = of_count_phandle_with_args(node,
+ "regulator-coupled-with",
+ NULL);
+
+ return (n_phandles > 0) ? n_phandles : 0;
+}
+
+/* Looks for "to_find" device_node in src's "regulator-coupled-with" property */
+static bool of_coupling_find_node(struct device_node *src,
+ struct device_node *to_find)
+{
+ int n_phandles, i;
+ bool found = false;
+
+ n_phandles = of_count_phandle_with_args(src,
+ "regulator-coupled-with",
+ NULL);
+
+ for (i = 0; i < n_phandles; i++) {
+ struct device_node *tmp = of_parse_phandle(src,
+ "regulator-coupled-with", i);
+
+ if (!tmp)
+ break;
+
+ /* found */
+ if (tmp == to_find)
+ found = true;
+
+ of_node_put(tmp);
+
+ if (found)
+ break;
+ }
+
+ return found;
+}
+
+/**
+ * of_check_coupling_data - Parse rdev's coupling properties and check data
+ * consistency
+ * @rdev - pointer to regulator_dev whose data is checked
+ *
+ * Function checks if all the following conditions are met:
+ * - rdev's max_spread is greater than 0
+ * - all coupled regulators have the same max_spread
+ * - all coupled regulators have the same number of regulator_dev phandles
+ * - all regulators are linked to each other
+ *
+ * Returns true if all conditions are met.
+ */
+bool of_check_coupling_data(struct regulator_dev *rdev)
+{
+ int max_spread = rdev->constraints->max_spread;
+ struct device_node *node = rdev->dev.of_node;
+ int n_phandles = of_get_n_coupled(rdev);
+ struct device_node *c_node;
+ int i;
+ bool ret = true;
+
+ if (max_spread <= 0) {
+ dev_err(&rdev->dev, "max_spread value invalid\n");
+ return false;
+ }
+
+ /* iterate over rdev's phandles */
+ for (i = 0; i < n_phandles; i++) {
+ int c_max_spread, c_n_phandles;
+
+ c_node = of_parse_phandle(node,
+ "regulator-coupled-with", i);
+
+ if (!c_node)
+ ret = false;
+
+ c_n_phandles = of_count_phandle_with_args(c_node,
+ "regulator-coupled-with",
+ NULL);
+
+ if (c_n_phandles != n_phandles) {
+ dev_err(&rdev->dev, "number of couped reg phandles mismatch\n");
+ ret = false;
+ goto clean;
+ }
+
+ if (of_property_read_u32(c_node, "regulator-coupled-max-spread",
+ &c_max_spread)) {
+ ret = false;
+ goto clean;
+ }
+
+ if (c_max_spread != max_spread) {
+ dev_err(&rdev->dev,
+ "coupled regulators max_spread mismatch\n");
+ ret = false;
+ goto clean;
+ }
+
+ if (!of_coupling_find_node(c_node, node)) {
+ dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n");
+ ret = false;
+ }
+
+clean:
+ of_node_put(c_node);
+ if (!ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * of_parse_coupled regulator - Get regulator_dev pointer from rdev's property
+ * @rdev: Pointer to regulator_dev, whose DTS is used as a source to parse
+ * "regulator-coupled-with" property
+ * @index: Index in phandles array
+ *
+ * Returns the regulator_dev pointer parsed from DTS. If it has not been yet
+ * registered, returns NULL
+ */
+struct regulator_dev *of_parse_coupled_regulator(struct regulator_dev *rdev,
+ int index)
+{
+ struct device_node *node = rdev->dev.of_node;
+ struct device_node *c_node;
+ struct regulator_dev *c_rdev;
+
+ c_node = of_parse_phandle(node, "regulator-coupled-with", index);
+ if (!c_node)
+ return NULL;
+
+ c_rdev = of_find_regulator_by_node(c_node);
+
+ of_node_put(c_node);
+
+ return c_rdev;
+}
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
new file mode 100644
index 000000000..c2cc392a2
--- /dev/null
+++ b/drivers/regulator/palmas-regulator.c
@@ -0,0 +1,1707 @@
+/*
+ * Driver for Regulator part of Palmas PMIC Chips
+ *
+ * Copyright 2011-2013 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Ian Lartey <ian@slimlogic.co.uk>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/mfd/palmas.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/regulator/of_regulator.h>
+
+static const struct regulator_linear_range smps_low_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(500000, 0x1, 0x6, 0),
+ REGULATOR_LINEAR_RANGE(510000, 0x7, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(1650000, 0x7A, 0x7f, 0),
+};
+
+static const struct regulator_linear_range smps_high_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x6, 0),
+ REGULATOR_LINEAR_RANGE(1020000, 0x7, 0x79, 20000),
+ REGULATOR_LINEAR_RANGE(3300000, 0x7A, 0x7f, 0),
+};
+
+static struct palmas_regs_info palmas_generic_regs_info[] = {
+ {
+ .name = "SMPS12",
+ .sname = "smps1-in",
+ .vsel_addr = PALMAS_SMPS12_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS12_CTRL,
+ .tstep_addr = PALMAS_SMPS12_TSTEP,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS12,
+ },
+ {
+ .name = "SMPS123",
+ .sname = "smps1-in",
+ .vsel_addr = PALMAS_SMPS12_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS12_CTRL,
+ .tstep_addr = PALMAS_SMPS12_TSTEP,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS12,
+ },
+ {
+ .name = "SMPS3",
+ .sname = "smps3-in",
+ .vsel_addr = PALMAS_SMPS3_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS3_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS3,
+ },
+ {
+ .name = "SMPS45",
+ .sname = "smps4-in",
+ .vsel_addr = PALMAS_SMPS45_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS45_CTRL,
+ .tstep_addr = PALMAS_SMPS45_TSTEP,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS45,
+ },
+ {
+ .name = "SMPS457",
+ .sname = "smps4-in",
+ .vsel_addr = PALMAS_SMPS45_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS45_CTRL,
+ .tstep_addr = PALMAS_SMPS45_TSTEP,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS45,
+ },
+ {
+ .name = "SMPS6",
+ .sname = "smps6-in",
+ .vsel_addr = PALMAS_SMPS6_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS6_CTRL,
+ .tstep_addr = PALMAS_SMPS6_TSTEP,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS6,
+ },
+ {
+ .name = "SMPS7",
+ .sname = "smps7-in",
+ .vsel_addr = PALMAS_SMPS7_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS7_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS7,
+ },
+ {
+ .name = "SMPS8",
+ .sname = "smps8-in",
+ .vsel_addr = PALMAS_SMPS8_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS8_CTRL,
+ .tstep_addr = PALMAS_SMPS8_TSTEP,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS8,
+ },
+ {
+ .name = "SMPS9",
+ .sname = "smps9-in",
+ .vsel_addr = PALMAS_SMPS9_VOLTAGE,
+ .ctrl_addr = PALMAS_SMPS9_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS9,
+ },
+ {
+ .name = "SMPS10_OUT2",
+ .sname = "smps10-in",
+ .ctrl_addr = PALMAS_SMPS10_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS10,
+ },
+ {
+ .name = "SMPS10_OUT1",
+ .sname = "smps10-out2",
+ .ctrl_addr = PALMAS_SMPS10_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS10,
+ },
+ {
+ .name = "LDO1",
+ .sname = "ldo1-in",
+ .vsel_addr = PALMAS_LDO1_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO1_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO1,
+ },
+ {
+ .name = "LDO2",
+ .sname = "ldo2-in",
+ .vsel_addr = PALMAS_LDO2_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO2_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO2,
+ },
+ {
+ .name = "LDO3",
+ .sname = "ldo3-in",
+ .vsel_addr = PALMAS_LDO3_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO3_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO3,
+ },
+ {
+ .name = "LDO4",
+ .sname = "ldo4-in",
+ .vsel_addr = PALMAS_LDO4_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO4_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO4,
+ },
+ {
+ .name = "LDO5",
+ .sname = "ldo5-in",
+ .vsel_addr = PALMAS_LDO5_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO5_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO5,
+ },
+ {
+ .name = "LDO6",
+ .sname = "ldo6-in",
+ .vsel_addr = PALMAS_LDO6_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO6_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO6,
+ },
+ {
+ .name = "LDO7",
+ .sname = "ldo7-in",
+ .vsel_addr = PALMAS_LDO7_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO7_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO7,
+ },
+ {
+ .name = "LDO8",
+ .sname = "ldo8-in",
+ .vsel_addr = PALMAS_LDO8_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO8_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO8,
+ },
+ {
+ .name = "LDO9",
+ .sname = "ldo9-in",
+ .vsel_addr = PALMAS_LDO9_VOLTAGE,
+ .ctrl_addr = PALMAS_LDO9_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO9,
+ },
+ {
+ .name = "LDOLN",
+ .sname = "ldoln-in",
+ .vsel_addr = PALMAS_LDOLN_VOLTAGE,
+ .ctrl_addr = PALMAS_LDOLN_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDOLN,
+ },
+ {
+ .name = "LDOUSB",
+ .sname = "ldousb-in",
+ .vsel_addr = PALMAS_LDOUSB_VOLTAGE,
+ .ctrl_addr = PALMAS_LDOUSB_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDOUSB,
+ },
+ {
+ .name = "REGEN1",
+ .ctrl_addr = PALMAS_REGEN1_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN1,
+ },
+ {
+ .name = "REGEN2",
+ .ctrl_addr = PALMAS_REGEN2_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN2,
+ },
+ {
+ .name = "REGEN3",
+ .ctrl_addr = PALMAS_REGEN3_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN3,
+ },
+ {
+ .name = "SYSEN1",
+ .ctrl_addr = PALMAS_SYSEN1_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SYSEN1,
+ },
+ {
+ .name = "SYSEN2",
+ .ctrl_addr = PALMAS_SYSEN2_CTRL,
+ .sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SYSEN2,
+ },
+};
+
+static struct palmas_regs_info tps65917_regs_info[] = {
+ {
+ .name = "SMPS1",
+ .sname = "smps1-in",
+ .vsel_addr = TPS65917_SMPS1_VOLTAGE,
+ .ctrl_addr = TPS65917_SMPS1_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_SMPS1,
+ },
+ {
+ .name = "SMPS2",
+ .sname = "smps2-in",
+ .vsel_addr = TPS65917_SMPS2_VOLTAGE,
+ .ctrl_addr = TPS65917_SMPS2_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_SMPS2,
+ },
+ {
+ .name = "SMPS3",
+ .sname = "smps3-in",
+ .vsel_addr = TPS65917_SMPS3_VOLTAGE,
+ .ctrl_addr = TPS65917_SMPS3_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_SMPS3,
+ },
+ {
+ .name = "SMPS4",
+ .sname = "smps4-in",
+ .vsel_addr = TPS65917_SMPS4_VOLTAGE,
+ .ctrl_addr = TPS65917_SMPS4_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_SMPS4,
+ },
+ {
+ .name = "SMPS5",
+ .sname = "smps5-in",
+ .vsel_addr = TPS65917_SMPS5_VOLTAGE,
+ .ctrl_addr = TPS65917_SMPS5_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_SMPS5,
+ },
+ {
+ .name = "SMPS12",
+ .sname = "smps1-in",
+ .vsel_addr = TPS65917_SMPS1_VOLTAGE,
+ .ctrl_addr = TPS65917_SMPS1_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_SMPS12,
+ },
+ {
+ .name = "LDO1",
+ .sname = "ldo1-in",
+ .vsel_addr = TPS65917_LDO1_VOLTAGE,
+ .ctrl_addr = TPS65917_LDO1_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_LDO1,
+ },
+ {
+ .name = "LDO2",
+ .sname = "ldo2-in",
+ .vsel_addr = TPS65917_LDO2_VOLTAGE,
+ .ctrl_addr = TPS65917_LDO2_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_LDO2,
+ },
+ {
+ .name = "LDO3",
+ .sname = "ldo3-in",
+ .vsel_addr = TPS65917_LDO3_VOLTAGE,
+ .ctrl_addr = TPS65917_LDO3_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_LDO3,
+ },
+ {
+ .name = "LDO4",
+ .sname = "ldo4-in",
+ .vsel_addr = TPS65917_LDO4_VOLTAGE,
+ .ctrl_addr = TPS65917_LDO4_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_LDO4,
+ },
+ {
+ .name = "LDO5",
+ .sname = "ldo5-in",
+ .vsel_addr = TPS65917_LDO5_VOLTAGE,
+ .ctrl_addr = TPS65917_LDO5_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_LDO5,
+ },
+ {
+ .name = "REGEN1",
+ .ctrl_addr = TPS65917_REGEN1_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_REGEN1,
+ },
+ {
+ .name = "REGEN2",
+ .ctrl_addr = TPS65917_REGEN2_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_REGEN2,
+ },
+ {
+ .name = "REGEN3",
+ .ctrl_addr = TPS65917_REGEN3_CTRL,
+ .sleep_id = TPS65917_EXTERNAL_REQSTR_ID_REGEN3,
+ },
+};
+
+#define EXTERNAL_REQUESTOR(_id, _offset, _pos) \
+ [PALMAS_EXTERNAL_REQSTR_ID_##_id] = { \
+ .id = PALMAS_EXTERNAL_REQSTR_ID_##_id, \
+ .reg_offset = _offset, \
+ .bit_pos = _pos, \
+ }
+
+static struct palmas_sleep_requestor_info palma_sleep_req_info[] = {
+ EXTERNAL_REQUESTOR(REGEN1, 0, 0),
+ EXTERNAL_REQUESTOR(REGEN2, 0, 1),
+ EXTERNAL_REQUESTOR(SYSEN1, 0, 2),
+ EXTERNAL_REQUESTOR(SYSEN2, 0, 3),
+ EXTERNAL_REQUESTOR(CLK32KG, 0, 4),
+ EXTERNAL_REQUESTOR(CLK32KGAUDIO, 0, 5),
+ EXTERNAL_REQUESTOR(REGEN3, 0, 6),
+ EXTERNAL_REQUESTOR(SMPS12, 1, 0),
+ EXTERNAL_REQUESTOR(SMPS3, 1, 1),
+ EXTERNAL_REQUESTOR(SMPS45, 1, 2),
+ EXTERNAL_REQUESTOR(SMPS6, 1, 3),
+ EXTERNAL_REQUESTOR(SMPS7, 1, 4),
+ EXTERNAL_REQUESTOR(SMPS8, 1, 5),
+ EXTERNAL_REQUESTOR(SMPS9, 1, 6),
+ EXTERNAL_REQUESTOR(SMPS10, 1, 7),
+ EXTERNAL_REQUESTOR(LDO1, 2, 0),
+ EXTERNAL_REQUESTOR(LDO2, 2, 1),
+ EXTERNAL_REQUESTOR(LDO3, 2, 2),
+ EXTERNAL_REQUESTOR(LDO4, 2, 3),
+ EXTERNAL_REQUESTOR(LDO5, 2, 4),
+ EXTERNAL_REQUESTOR(LDO6, 2, 5),
+ EXTERNAL_REQUESTOR(LDO7, 2, 6),
+ EXTERNAL_REQUESTOR(LDO8, 2, 7),
+ EXTERNAL_REQUESTOR(LDO9, 3, 0),
+ EXTERNAL_REQUESTOR(LDOLN, 3, 1),
+ EXTERNAL_REQUESTOR(LDOUSB, 3, 2),
+};
+
+#define EXTERNAL_REQUESTOR_TPS65917(_id, _offset, _pos) \
+ [TPS65917_EXTERNAL_REQSTR_ID_##_id] = { \
+ .id = TPS65917_EXTERNAL_REQSTR_ID_##_id, \
+ .reg_offset = _offset, \
+ .bit_pos = _pos, \
+ }
+
+static struct palmas_sleep_requestor_info tps65917_sleep_req_info[] = {
+ EXTERNAL_REQUESTOR_TPS65917(REGEN1, 0, 0),
+ EXTERNAL_REQUESTOR_TPS65917(REGEN2, 0, 1),
+ EXTERNAL_REQUESTOR_TPS65917(REGEN3, 0, 6),
+ EXTERNAL_REQUESTOR_TPS65917(SMPS1, 1, 0),
+ EXTERNAL_REQUESTOR_TPS65917(SMPS2, 1, 1),
+ EXTERNAL_REQUESTOR_TPS65917(SMPS3, 1, 2),
+ EXTERNAL_REQUESTOR_TPS65917(SMPS4, 1, 3),
+ EXTERNAL_REQUESTOR_TPS65917(SMPS5, 1, 4),
+ EXTERNAL_REQUESTOR_TPS65917(SMPS12, 1, 5),
+ EXTERNAL_REQUESTOR_TPS65917(LDO1, 2, 0),
+ EXTERNAL_REQUESTOR_TPS65917(LDO2, 2, 1),
+ EXTERNAL_REQUESTOR_TPS65917(LDO3, 2, 2),
+ EXTERNAL_REQUESTOR_TPS65917(LDO4, 2, 3),
+ EXTERNAL_REQUESTOR_TPS65917(LDO5, 2, 4),
+};
+
+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
+
+#define PALMAS_SMPS_NUM_VOLTAGES 122
+#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_set_mode_smps(struct regulator_dev *dev, unsigned int mode)
+{
+ int id = rdev_get_id(dev);
+ int ret;
+ struct palmas_pmic *pmic = rdev_get_drvdata(dev);
+ struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+ unsigned int reg;
+ bool rail_enable = true;
+
+ ret = palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, &reg);
+ if (ret)
+ return ret;
+
+ 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, rinfo->ctrl_addr, reg);
+
+ /* Switch the enable value to ensure this is used for enable */
+ pmic->desc[id].enable_val = pmic->current_reg_mode[id];
+
+ 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_smps_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ int id = rdev_get_id(rdev);
+ struct palmas_pmic *pmic = rdev_get_drvdata(rdev);
+ struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+ unsigned int reg = 0;
+ 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, rinfo->tstep_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 const struct regulator_ops palmas_ops_smps = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = palmas_smps_set_ramp_delay,
+};
+
+static const struct regulator_ops palmas_ops_ext_control_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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = palmas_smps_set_ramp_delay,
+};
+
+static const 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,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+};
+
+static const struct regulator_ops tps65917_ops_smps = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static const struct regulator_ops tps65917_ops_ext_control_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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static int palmas_is_enabled_ldo(struct regulator_dev *dev)
+{
+ int id = rdev_get_id(dev);
+ struct palmas_pmic *pmic = rdev_get_drvdata(dev);
+ struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+ unsigned int reg;
+
+ palmas_ldo_read(pmic->palmas, rinfo->ctrl_addr, &reg);
+
+ reg &= PALMAS_LDO1_CTRL_STATUS;
+
+ return !!(reg);
+}
+
+static const 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 const struct regulator_ops palmas_ops_ldo9 = {
+ .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,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+};
+
+static const struct regulator_ops palmas_ops_ext_control_ldo = {
+ .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 const struct regulator_ops palmas_ops_extreg = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+};
+
+static const struct regulator_ops palmas_ops_ext_control_extreg = {
+};
+
+static const struct regulator_ops tps65917_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,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static const struct regulator_ops tps65917_ops_ldo_1_2 = {
+ .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,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+};
+
+static int palmas_regulator_config_external(struct palmas *palmas, int id,
+ struct palmas_reg_init *reg_init)
+{
+ struct palmas_pmic_driver_data *ddata = palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+ int ret;
+
+ ret = palmas_ext_control_req_config(palmas, rinfo->sleep_id,
+ reg_init->roof_floor, true);
+ if (ret < 0)
+ dev_err(palmas->dev,
+ "Ext control config for regulator %d failed %d\n",
+ id, ret);
+ return ret;
+}
+
+/*
+ * 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;
+ int ret;
+ struct palmas_pmic_driver_data *ddata = palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+ unsigned int addr = rinfo->ctrl_addr;
+
+ ret = palmas_smps_read(palmas, addr, &reg);
+ if (ret)
+ return ret;
+
+ switch (id) {
+ case PALMAS_REG_SMPS10_OUT1:
+ case PALMAS_REG_SMPS10_OUT2:
+ 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 (rinfo->vsel_addr && reg_init->vsel) {
+
+ reg = reg_init->vsel;
+
+ ret = palmas_smps_write(palmas, rinfo->vsel_addr, reg);
+ if (ret)
+ return ret;
+ }
+
+ if (reg_init->roof_floor && (id != PALMAS_REG_SMPS10_OUT1) &&
+ (id != PALMAS_REG_SMPS10_OUT2)) {
+ /* Enable externally controlled regulator */
+ ret = palmas_smps_read(palmas, addr, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (!(reg & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK)) {
+ reg |= SMPS_CTRL_MODE_ON;
+ ret = palmas_smps_write(palmas, addr, reg);
+ if (ret < 0)
+ return ret;
+ }
+ return palmas_regulator_config_external(palmas, id, reg_init);
+ }
+ 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;
+ struct palmas_pmic_driver_data *ddata = palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+
+ addr = rinfo->ctrl_addr;
+
+ ret = palmas_ldo_read(palmas, addr, &reg);
+ 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;
+
+ if (reg_init->roof_floor) {
+ /* Enable externally controlled regulator */
+ ret = palmas_update_bits(palmas, PALMAS_LDO_BASE,
+ addr, PALMAS_LDO1_CTRL_MODE_ACTIVE,
+ PALMAS_LDO1_CTRL_MODE_ACTIVE);
+ if (ret < 0) {
+ dev_err(palmas->dev,
+ "LDO Register 0x%02x update failed %d\n",
+ addr, ret);
+ return ret;
+ }
+ return palmas_regulator_config_external(palmas, id, reg_init);
+ }
+ 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;
+ struct palmas_pmic_driver_data *ddata = palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
+
+ addr = rinfo->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;
+ }
+
+ if (reg_init->roof_floor) {
+ /* Enable externally controlled regulator */
+ ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
+ addr, PALMAS_REGEN1_CTRL_MODE_ACTIVE,
+ PALMAS_REGEN1_CTRL_MODE_ACTIVE);
+ if (ret < 0) {
+ dev_err(palmas->dev,
+ "Resource Register 0x%02x update failed %d\n",
+ addr, ret);
+ return ret;
+ }
+ return palmas_regulator_config_external(palmas, id, reg_init);
+ }
+ return 0;
+}
+
+static void palmas_enable_ldo8_track(struct palmas *palmas)
+{
+ unsigned int reg;
+ unsigned int addr;
+ int ret;
+ struct palmas_pmic_driver_data *ddata = palmas->pmic_ddata;
+ struct palmas_regs_info *rinfo;
+
+ rinfo = &ddata->palmas_regs_info[PALMAS_REG_LDO8];
+ addr = rinfo->ctrl_addr;
+
+ ret = palmas_ldo_read(palmas, addr, &reg);
+ 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 = rinfo->vsel_addr;
+ ret = palmas_ldo_read(palmas, addr, &reg);
+ 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 int palmas_ldo_registration(struct palmas_pmic *pmic,
+ struct palmas_pmic_driver_data *ddata,
+ struct palmas_pmic_platform_data *pdata,
+ const char *pdev_name,
+ struct regulator_config config)
+{
+ int id, ret;
+ struct regulator_dev *rdev;
+ struct palmas_reg_init *reg_init;
+ struct palmas_regs_info *rinfo;
+ struct regulator_desc *desc;
+
+ for (id = ddata->ldo_begin; id < ddata->max_reg; id++) {
+ if (pdata && pdata->reg_init[id])
+ reg_init = pdata->reg_init[id];
+ else
+ reg_init = NULL;
+
+ rinfo = &ddata->palmas_regs_info[id];
+ /* Miss out regulators which are not available due
+ * to alternate functions.
+ */
+
+ /* Register the regulators */
+ desc = &pmic->desc[id];
+ desc->name = rinfo->name;
+ desc->id = id;
+ desc->type = REGULATOR_VOLTAGE;
+ desc->owner = THIS_MODULE;
+
+ if (id < PALMAS_REG_REGEN1) {
+ desc->n_voltages = PALMAS_LDO_NUM_VOLTAGES;
+ if (reg_init && reg_init->roof_floor)
+ desc->ops = &palmas_ops_ext_control_ldo;
+ else
+ desc->ops = &palmas_ops_ldo;
+ desc->min_uV = 900000;
+ desc->uV_step = 50000;
+ desc->linear_min_sel = 1;
+ desc->enable_time = 500;
+ desc->vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+ rinfo->vsel_addr);
+ desc->vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK;
+ desc->enable_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+ rinfo->ctrl_addr);
+ desc->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(pmic->palmas);
+ desc->min_uV = 450000;
+ desc->uV_step = 25000;
+ }
+
+ /* LOD6 in vibrator mode will have enable time 2000us */
+ if (pdata && pdata->ldo6_vibrator &&
+ (id == PALMAS_REG_LDO6))
+ desc->enable_time = 2000;
+
+ if (id == PALMAS_REG_LDO9) {
+ desc->ops = &palmas_ops_ldo9;
+ desc->bypass_reg = desc->enable_reg;
+ desc->bypass_val_on =
+ PALMAS_LDO9_CTRL_LDO_BYPASS_EN;
+ desc->bypass_mask =
+ PALMAS_LDO9_CTRL_LDO_BYPASS_EN;
+ }
+ } else {
+ if (!ddata->has_regen3 && id == PALMAS_REG_REGEN3)
+ continue;
+
+ desc->n_voltages = 1;
+ if (reg_init && reg_init->roof_floor)
+ desc->ops = &palmas_ops_ext_control_extreg;
+ else
+ desc->ops = &palmas_ops_extreg;
+ desc->enable_reg =
+ PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE,
+ rinfo->ctrl_addr);
+ desc->enable_mask = PALMAS_REGEN1_CTRL_MODE_ACTIVE;
+ }
+
+ if (pdata)
+ config.init_data = pdata->reg_data[id];
+ else
+ config.init_data = NULL;
+
+ desc->supply_name = rinfo->sname;
+ config.of_node = ddata->palmas_matches[id].of_node;
+
+ rdev = devm_regulator_register(pmic->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(pmic->dev,
+ "failed to register %s regulator\n",
+ pdev_name);
+ return PTR_ERR(rdev);
+ }
+
+ /* 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 <= ddata->ldo_end)
+ ret = palmas_ldo_init(pmic->palmas, id,
+ reg_init);
+ else
+ ret = palmas_extreg_init(pmic->palmas,
+ id, reg_init);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tps65917_ldo_registration(struct palmas_pmic *pmic,
+ struct palmas_pmic_driver_data *ddata,
+ struct palmas_pmic_platform_data *pdata,
+ const char *pdev_name,
+ struct regulator_config config)
+{
+ int id, ret;
+ struct regulator_dev *rdev;
+ struct palmas_reg_init *reg_init;
+ struct palmas_regs_info *rinfo;
+ struct regulator_desc *desc;
+
+ for (id = ddata->ldo_begin; id < ddata->max_reg; id++) {
+ if (pdata && pdata->reg_init[id])
+ reg_init = pdata->reg_init[id];
+ else
+ reg_init = NULL;
+
+ /* Miss out regulators which are not available due
+ * to alternate functions.
+ */
+ rinfo = &ddata->palmas_regs_info[id];
+
+ /* Register the regulators */
+ desc = &pmic->desc[id];
+ desc->name = rinfo->name;
+ desc->id = id;
+ desc->type = REGULATOR_VOLTAGE;
+ desc->owner = THIS_MODULE;
+
+ if (id < TPS65917_REG_REGEN1) {
+ desc->n_voltages = PALMAS_LDO_NUM_VOLTAGES;
+ if (reg_init && reg_init->roof_floor)
+ desc->ops = &palmas_ops_ext_control_ldo;
+ else
+ desc->ops = &tps65917_ops_ldo;
+ desc->min_uV = 900000;
+ desc->uV_step = 50000;
+ desc->linear_min_sel = 1;
+ desc->enable_time = 500;
+ desc->vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+ rinfo->vsel_addr);
+ desc->vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK;
+ desc->enable_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+ rinfo->ctrl_addr);
+ desc->enable_mask = PALMAS_LDO1_CTRL_MODE_ACTIVE;
+ /*
+ * To be confirmed. Discussion on going with PMIC Team.
+ * It is of the order of ~60mV/uS.
+ */
+ desc->ramp_delay = 2500;
+ if (id == TPS65917_REG_LDO1 ||
+ id == TPS65917_REG_LDO2) {
+ desc->ops = &tps65917_ops_ldo_1_2;
+ desc->bypass_reg = desc->enable_reg;
+ desc->bypass_val_on =
+ TPS65917_LDO1_CTRL_BYPASS_EN;
+ desc->bypass_mask =
+ TPS65917_LDO1_CTRL_BYPASS_EN;
+ }
+ } else {
+ desc->n_voltages = 1;
+ if (reg_init && reg_init->roof_floor)
+ desc->ops = &palmas_ops_ext_control_extreg;
+ else
+ desc->ops = &palmas_ops_extreg;
+ desc->enable_reg =
+ PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE,
+ rinfo->ctrl_addr);
+ desc->enable_mask = PALMAS_REGEN1_CTRL_MODE_ACTIVE;
+ }
+
+ if (pdata)
+ config.init_data = pdata->reg_data[id];
+ else
+ config.init_data = NULL;
+
+ desc->supply_name = rinfo->sname;
+ config.of_node = ddata->palmas_matches[id].of_node;
+
+ rdev = devm_regulator_register(pmic->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(pmic->dev,
+ "failed to register %s regulator\n",
+ pdev_name);
+ return PTR_ERR(rdev);
+ }
+
+ /* 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 < TPS65917_REG_REGEN1)
+ ret = palmas_ldo_init(pmic->palmas,
+ id, reg_init);
+ else
+ ret = palmas_extreg_init(pmic->palmas,
+ id, reg_init);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int palmas_smps_registration(struct palmas_pmic *pmic,
+ struct palmas_pmic_driver_data *ddata,
+ struct palmas_pmic_platform_data *pdata,
+ const char *pdev_name,
+ struct regulator_config config)
+{
+ int id, ret;
+ unsigned int addr, reg;
+ struct regulator_dev *rdev;
+ struct palmas_reg_init *reg_init;
+ struct palmas_regs_info *rinfo;
+ struct regulator_desc *desc;
+
+ for (id = ddata->smps_start; id <= ddata->smps_end; 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;
+ case PALMAS_REG_SMPS10_OUT1:
+ case PALMAS_REG_SMPS10_OUT2:
+ if (!PALMAS_PMIC_HAS(pmic->palmas, SMPS10_BOOST))
+ continue;
+ }
+ rinfo = &ddata->palmas_regs_info[id];
+ desc = &pmic->desc[id];
+
+ if ((id == PALMAS_REG_SMPS6) || (id == PALMAS_REG_SMPS8))
+ ramp_delay_support = true;
+
+ if (ramp_delay_support) {
+ addr = rinfo->tstep_addr;
+ ret = palmas_smps_read(pmic->palmas, addr, &reg);
+ if (ret < 0) {
+ dev_err(pmic->dev,
+ "reading TSTEP reg failed: %d\n", ret);
+ return ret;
+ }
+ desc->ramp_delay = palmas_smps_ramp_delay[reg & 0x3];
+ pmic->ramp_delay[id] = desc->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(pmic->palmas, id, reg_init);
+ if (ret)
+ return ret;
+ } else {
+ reg_init = NULL;
+ }
+
+ /* Register the regulators */
+ desc->name = rinfo->name;
+ desc->id = id;
+
+ switch (id) {
+ case PALMAS_REG_SMPS10_OUT1:
+ case PALMAS_REG_SMPS10_OUT2:
+ desc->n_voltages = PALMAS_SMPS10_NUM_VOLTAGES;
+ desc->ops = &palmas_ops_smps10;
+ desc->vsel_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ PALMAS_SMPS10_CTRL);
+ desc->vsel_mask = SMPS10_VSEL;
+ desc->enable_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ PALMAS_SMPS10_CTRL);
+ if (id == PALMAS_REG_SMPS10_OUT1)
+ desc->enable_mask = SMPS10_SWITCH_EN;
+ else
+ desc->enable_mask = SMPS10_BOOST_EN;
+ desc->bypass_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ PALMAS_SMPS10_CTRL);
+ desc->bypass_val_on = SMPS10_BYPASS_EN;
+ desc->bypass_mask = SMPS10_BYPASS_EN;
+ desc->min_uV = 3750000;
+ desc->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 = rinfo->vsel_addr;
+ desc->n_linear_ranges = 3;
+
+ ret = palmas_smps_read(pmic->palmas, addr, &reg);
+ if (ret)
+ return ret;
+ if (reg & PALMAS_SMPS12_VOLTAGE_RANGE)
+ pmic->range[id] = 1;
+ if (pmic->range[id])
+ desc->linear_ranges = smps_high_ranges;
+ else
+ desc->linear_ranges = smps_low_ranges;
+
+ if (reg_init && reg_init->roof_floor)
+ desc->ops = &palmas_ops_ext_control_smps;
+ else
+ desc->ops = &palmas_ops_smps;
+ desc->n_voltages = PALMAS_SMPS_NUM_VOLTAGES;
+ desc->vsel_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ rinfo->vsel_addr);
+ desc->vsel_mask = PALMAS_SMPS12_VOLTAGE_VSEL_MASK;
+
+ /* Read the smps mode for later use. */
+ addr = rinfo->ctrl_addr;
+ ret = palmas_smps_read(pmic->palmas, addr, &reg);
+ if (ret)
+ return ret;
+ pmic->current_reg_mode[id] = reg &
+ PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
+
+ desc->enable_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ rinfo->ctrl_addr);
+ desc->enable_mask = PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
+ /* set_mode overrides this value */
+ desc->enable_val = SMPS_CTRL_MODE_ON;
+ }
+
+ desc->type = REGULATOR_VOLTAGE;
+ desc->owner = THIS_MODULE;
+
+ if (pdata)
+ config.init_data = pdata->reg_data[id];
+ else
+ config.init_data = NULL;
+
+ desc->supply_name = rinfo->sname;
+ config.of_node = ddata->palmas_matches[id].of_node;
+
+ rdev = devm_regulator_register(pmic->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(pmic->dev,
+ "failed to register %s regulator\n",
+ pdev_name);
+ return PTR_ERR(rdev);
+ }
+
+ /* Save regulator for cleanup */
+ pmic->rdev[id] = rdev;
+ }
+
+ return 0;
+}
+
+static int tps65917_smps_registration(struct palmas_pmic *pmic,
+ struct palmas_pmic_driver_data *ddata,
+ struct palmas_pmic_platform_data *pdata,
+ const char *pdev_name,
+ struct regulator_config config)
+{
+ int id, ret;
+ unsigned int addr, reg;
+ struct regulator_dev *rdev;
+ struct palmas_reg_init *reg_init;
+ struct palmas_regs_info *rinfo;
+ struct regulator_desc *desc;
+
+ for (id = ddata->smps_start; id <= ddata->smps_end; id++) {
+ /*
+ * Miss out regulators which are not available due
+ * to slaving configurations.
+ */
+ desc = &pmic->desc[id];
+ desc->n_linear_ranges = 3;
+ if ((id == TPS65917_REG_SMPS2 || id == TPS65917_REG_SMPS1) &&
+ pmic->smps12)
+ continue;
+
+ /* Initialise sleep/init values from platform data */
+ if (pdata && pdata->reg_init[id]) {
+ reg_init = pdata->reg_init[id];
+ ret = palmas_smps_init(pmic->palmas, id, reg_init);
+ if (ret)
+ return ret;
+ } else {
+ reg_init = NULL;
+ }
+ rinfo = &ddata->palmas_regs_info[id];
+
+ /* Register the regulators */
+ desc->name = rinfo->name;
+ desc->id = id;
+
+ /*
+ * 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 = rinfo->vsel_addr;
+
+ ret = palmas_smps_read(pmic->palmas, addr, &reg);
+ if (ret)
+ return ret;
+ if (reg & TPS65917_SMPS1_VOLTAGE_RANGE)
+ pmic->range[id] = 1;
+
+ if (pmic->range[id])
+ desc->linear_ranges = smps_high_ranges;
+ else
+ desc->linear_ranges = smps_low_ranges;
+
+ if (reg_init && reg_init->roof_floor)
+ desc->ops = &tps65917_ops_ext_control_smps;
+ else
+ desc->ops = &tps65917_ops_smps;
+ desc->n_voltages = PALMAS_SMPS_NUM_VOLTAGES;
+ desc->vsel_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ rinfo->vsel_addr);
+ desc->vsel_mask = PALMAS_SMPS12_VOLTAGE_VSEL_MASK;
+ desc->ramp_delay = 2500;
+
+ /* Read the smps mode for later use. */
+ addr = rinfo->ctrl_addr;
+ ret = palmas_smps_read(pmic->palmas, addr, &reg);
+ if (ret)
+ return ret;
+ pmic->current_reg_mode[id] = reg &
+ PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
+ desc->enable_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ rinfo->ctrl_addr);
+ desc->enable_mask = PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
+ /* set_mode overrides this value */
+ desc->enable_val = SMPS_CTRL_MODE_ON;
+
+ desc->type = REGULATOR_VOLTAGE;
+ desc->owner = THIS_MODULE;
+
+ if (pdata)
+ config.init_data = pdata->reg_data[id];
+ else
+ config.init_data = NULL;
+
+ desc->supply_name = rinfo->sname;
+ config.of_node = ddata->palmas_matches[id].of_node;
+
+ rdev = devm_regulator_register(pmic->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(pmic->dev,
+ "failed to register %s regulator\n",
+ pdev_name);
+ return PTR_ERR(rdev);
+ }
+
+ /* Save regulator for cleanup */
+ pmic->rdev[id] = rdev;
+ }
+
+ return 0;
+}
+
+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_out2", },
+ { .name = "smps10_out1", },
+ { .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 struct of_regulator_match tps65917_matches[] = {
+ { .name = "smps1", },
+ { .name = "smps2", },
+ { .name = "smps3", },
+ { .name = "smps4", },
+ { .name = "smps5", },
+ { .name = "smps12",},
+ { .name = "ldo1", },
+ { .name = "ldo2", },
+ { .name = "ldo3", },
+ { .name = "ldo4", },
+ { .name = "ldo5", },
+ { .name = "regen1", },
+ { .name = "regen2", },
+ { .name = "regen3", },
+ { .name = "sysen1", },
+ { .name = "sysen2", },
+};
+
+static struct palmas_pmic_driver_data palmas_ddata = {
+ .smps_start = PALMAS_REG_SMPS12,
+ .smps_end = PALMAS_REG_SMPS10_OUT1,
+ .ldo_begin = PALMAS_REG_LDO1,
+ .ldo_end = PALMAS_REG_LDOUSB,
+ .max_reg = PALMAS_NUM_REGS,
+ .has_regen3 = true,
+ .palmas_regs_info = palmas_generic_regs_info,
+ .palmas_matches = palmas_matches,
+ .sleep_req_info = palma_sleep_req_info,
+ .smps_register = palmas_smps_registration,
+ .ldo_register = palmas_ldo_registration,
+};
+
+static struct palmas_pmic_driver_data tps65917_ddata = {
+ .smps_start = TPS65917_REG_SMPS1,
+ .smps_end = TPS65917_REG_SMPS12,
+ .ldo_begin = TPS65917_REG_LDO1,
+ .ldo_end = TPS65917_REG_LDO5,
+ .max_reg = TPS65917_NUM_REGS,
+ .has_regen3 = true,
+ .palmas_regs_info = tps65917_regs_info,
+ .palmas_matches = tps65917_matches,
+ .sleep_req_info = tps65917_sleep_req_info,
+ .smps_register = tps65917_smps_registration,
+ .ldo_register = tps65917_ldo_registration,
+};
+
+static int palmas_dt_to_pdata(struct device *dev,
+ struct device_node *node,
+ struct palmas_pmic_platform_data *pdata,
+ struct palmas_pmic_driver_data *ddata)
+{
+ struct device_node *regulators;
+ u32 prop;
+ int idx, ret;
+
+ regulators = of_get_child_by_name(node, "regulators");
+ if (!regulators) {
+ dev_info(dev, "regulator node not found\n");
+ return 0;
+ }
+
+ ret = of_regulator_match(dev, regulators, ddata->palmas_matches,
+ ddata->max_reg);
+ of_node_put(regulators);
+ if (ret < 0) {
+ dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+ return 0;
+ }
+
+ for (idx = 0; idx < ddata->max_reg; idx++) {
+ struct of_regulator_match *match;
+ struct palmas_reg_init *rinit;
+ struct device_node *np;
+
+ match = &ddata->palmas_matches[idx];
+ np = match->of_node;
+
+ if (!match->init_data || !np)
+ continue;
+
+ rinit = devm_kzalloc(dev, sizeof(*rinit), GFP_KERNEL);
+ if (!rinit)
+ return -ENOMEM;
+
+ pdata->reg_data[idx] = match->init_data;
+ pdata->reg_init[idx] = rinit;
+
+ rinit->warm_reset = of_property_read_bool(np, "ti,warm-reset");
+ ret = of_property_read_u32(np, "ti,roof-floor", &prop);
+ /* EINVAL: Property not found */
+ if (ret != -EINVAL) {
+ int econtrol;
+
+ /* use default value, when no value is specified */
+ econtrol = PALMAS_EXT_CONTROL_NSLEEP;
+ if (!ret) {
+ switch (prop) {
+ case 1:
+ econtrol = PALMAS_EXT_CONTROL_ENABLE1;
+ break;
+ case 2:
+ econtrol = PALMAS_EXT_CONTROL_ENABLE2;
+ break;
+ case 3:
+ econtrol = PALMAS_EXT_CONTROL_NSLEEP;
+ break;
+ default:
+ WARN_ON(1);
+ dev_warn(dev,
+ "%s: Invalid roof-floor option: %u\n",
+ match->name, prop);
+ break;
+ }
+ }
+ rinit->roof_floor = econtrol;
+ }
+
+ ret = of_property_read_u32(np, "ti,mode-sleep", &prop);
+ if (!ret)
+ rinit->mode_sleep = prop;
+
+ ret = of_property_read_bool(np, "ti,smps-range");
+ if (ret)
+ rinit->vsel = PALMAS_SMPS12_VOLTAGE_RANGE;
+
+ if (idx == PALMAS_REG_LDO8)
+ pdata->enable_ldo8_tracking = of_property_read_bool(
+ np, "ti,enable-ldo8-tracking");
+ }
+
+ pdata->ldo6_vibrator = of_property_read_bool(node, "ti,ldo6-vibrator");
+
+ return 0;
+}
+
+static const struct of_device_id of_palmas_match_tbl[] = {
+ {
+ .compatible = "ti,palmas-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,twl6035-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,twl6036-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,twl6037-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,tps65913-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,tps65914-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,tps80036-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,tps659038-pmic",
+ .data = &palmas_ddata,
+ },
+ {
+ .compatible = "ti,tps65917-pmic",
+ .data = &tps65917_ddata,
+ },
+ { /* end */ }
+};
+
+static int palmas_regulators_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_pmic_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
+ struct palmas_pmic_driver_data *driver_data;
+ struct regulator_config config = { };
+ struct palmas_pmic *pmic;
+ const char *pdev_name;
+ const struct of_device_id *match;
+ int ret = 0;
+ unsigned int reg;
+
+ match = of_match_device(of_match_ptr(of_palmas_match_tbl), &pdev->dev);
+
+ if (!match)
+ return -ENODATA;
+
+ driver_data = (struct palmas_pmic_driver_data *)match->data;
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ if (of_device_is_compatible(node, "ti,tps659038-pmic")) {
+ palmas_generic_regs_info[PALMAS_REG_REGEN2].ctrl_addr =
+ TPS659038_REGEN2_CTRL;
+ palmas_ddata.has_regen3 = false;
+ }
+
+ pmic->dev = &pdev->dev;
+ pmic->palmas = palmas;
+ palmas->pmic = pmic;
+ platform_set_drvdata(pdev, pmic);
+ pmic->palmas->pmic_ddata = driver_data;
+
+ ret = palmas_dt_to_pdata(&pdev->dev, node, pdata, driver_data);
+ if (ret)
+ return ret;
+
+ ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, &reg);
+ if (ret)
+ return ret;
+
+ if (reg & PALMAS_SMPS_CTRL_SMPS12_SMPS123_EN) {
+ pmic->smps123 = 1;
+ pmic->smps12 = 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;
+ pdev_name = pdev->name;
+
+ ret = driver_data->smps_register(pmic, driver_data, pdata, pdev_name,
+ config);
+ if (ret)
+ return ret;
+
+ ret = driver_data->ldo_register(pmic, driver_data, pdata, pdev_name,
+ config);
+
+ return ret;
+}
+
+static struct platform_driver palmas_driver = {
+ .driver = {
+ .name = "palmas-pmic",
+ .of_match_table = of_palmas_match_tbl,
+ },
+ .probe = palmas_regulators_probe,
+};
+
+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 <gg@slimlogic.co.uk>");
+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/pbias-regulator.c b/drivers/regulator/pbias-regulator.c
new file mode 100644
index 000000000..92b41a6a4
--- /dev/null
+++ b/drivers/regulator/pbias-regulator.c
@@ -0,0 +1,262 @@
+/*
+ * pbias-regulator.c
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Balaji T K <balajitk@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 <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+struct pbias_reg_info {
+ u32 enable;
+ u32 enable_mask;
+ u32 disable_val;
+ u32 vmode;
+ unsigned int enable_time;
+ char *name;
+ const unsigned int *pbias_volt_table;
+ int n_voltages;
+};
+
+struct pbias_regulator_data {
+ struct regulator_desc desc;
+ void __iomem *pbias_addr;
+ struct regulator_dev *dev;
+ struct regmap *syscon;
+ const struct pbias_reg_info *info;
+ int voltage;
+};
+
+struct pbias_of_data {
+ unsigned int offset;
+};
+
+static const unsigned int pbias_volt_table_3_0V[] = {
+ 1800000,
+ 3000000
+};
+
+static const unsigned int pbias_volt_table_3_3V[] = {
+ 1800000,
+ 3300000
+};
+
+static const struct regulator_ops pbias_regulator_voltage_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct pbias_reg_info pbias_mmc_omap2430 = {
+ .enable = BIT(1),
+ .enable_mask = BIT(1),
+ .vmode = BIT(0),
+ .disable_val = 0,
+ .enable_time = 100,
+ .pbias_volt_table = pbias_volt_table_3_0V,
+ .n_voltages = 2,
+ .name = "pbias_mmc_omap2430"
+};
+
+static const struct pbias_reg_info pbias_sim_omap3 = {
+ .enable = BIT(9),
+ .enable_mask = BIT(9),
+ .vmode = BIT(8),
+ .enable_time = 100,
+ .pbias_volt_table = pbias_volt_table_3_0V,
+ .n_voltages = 2,
+ .name = "pbias_sim_omap3"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap4 = {
+ .enable = BIT(26) | BIT(22),
+ .enable_mask = BIT(26) | BIT(25) | BIT(22),
+ .disable_val = BIT(25),
+ .vmode = BIT(21),
+ .enable_time = 100,
+ .pbias_volt_table = pbias_volt_table_3_0V,
+ .n_voltages = 2,
+ .name = "pbias_mmc_omap4"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap5 = {
+ .enable = BIT(27) | BIT(26),
+ .enable_mask = BIT(27) | BIT(25) | BIT(26),
+ .disable_val = BIT(25),
+ .vmode = BIT(21),
+ .enable_time = 100,
+ .pbias_volt_table = pbias_volt_table_3_3V,
+ .n_voltages = 2,
+ .name = "pbias_mmc_omap5"
+};
+
+static struct of_regulator_match pbias_matches[] = {
+ { .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430},
+ { .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
+ { .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
+ { .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
+};
+#define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches)
+
+/* Offset from SCM general area (and syscon) base */
+
+static const struct pbias_of_data pbias_of_data_omap2 = {
+ .offset = 0x230,
+};
+
+static const struct pbias_of_data pbias_of_data_omap3 = {
+ .offset = 0x2b0,
+};
+
+static const struct pbias_of_data pbias_of_data_omap4 = {
+ .offset = 0x60,
+};
+
+static const struct pbias_of_data pbias_of_data_omap5 = {
+ .offset = 0x60,
+};
+
+static const struct pbias_of_data pbias_of_data_dra7 = {
+ .offset = 0xe00,
+};
+
+static const struct of_device_id pbias_of_match[] = {
+ { .compatible = "ti,pbias-omap", },
+ { .compatible = "ti,pbias-omap2", .data = &pbias_of_data_omap2, },
+ { .compatible = "ti,pbias-omap3", .data = &pbias_of_data_omap3, },
+ { .compatible = "ti,pbias-omap4", .data = &pbias_of_data_omap4, },
+ { .compatible = "ti,pbias-omap5", .data = &pbias_of_data_omap5, },
+ { .compatible = "ti,pbias-dra7", .data = &pbias_of_data_dra7, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pbias_of_match);
+
+static int pbias_regulator_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pbias_regulator_data *drvdata;
+ struct resource *res;
+ struct regulator_config cfg = { };
+ struct regmap *syscon;
+ const struct pbias_reg_info *info;
+ int ret = 0;
+ int count, idx, data_idx = 0;
+ const struct of_device_id *match;
+ const struct pbias_of_data *data;
+ unsigned int offset;
+
+ count = of_regulator_match(&pdev->dev, np, pbias_matches,
+ PBIAS_NUM_REGS);
+ if (count < 0)
+ return count;
+
+ drvdata = devm_kcalloc(&pdev->dev,
+ count, sizeof(struct pbias_regulator_data),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(syscon))
+ return PTR_ERR(syscon);
+
+ match = of_match_device(of_match_ptr(pbias_of_match), &pdev->dev);
+ if (match && match->data) {
+ data = match->data;
+ offset = data->offset;
+ } else {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ offset = res->start;
+ dev_WARN(&pdev->dev,
+ "using legacy dt data for pbias offset\n");
+ }
+
+ cfg.regmap = syscon;
+ cfg.dev = &pdev->dev;
+
+ for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) {
+ if (!pbias_matches[idx].init_data ||
+ !pbias_matches[idx].of_node)
+ continue;
+
+ info = pbias_matches[idx].driver_data;
+ if (!info)
+ return -ENODEV;
+
+ drvdata[data_idx].syscon = syscon;
+ drvdata[data_idx].info = info;
+ drvdata[data_idx].desc.name = info->name;
+ drvdata[data_idx].desc.owner = THIS_MODULE;
+ drvdata[data_idx].desc.type = REGULATOR_VOLTAGE;
+ drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops;
+ drvdata[data_idx].desc.volt_table = info->pbias_volt_table;
+ drvdata[data_idx].desc.n_voltages = info->n_voltages;
+ drvdata[data_idx].desc.enable_time = info->enable_time;
+ drvdata[data_idx].desc.vsel_reg = offset;
+ drvdata[data_idx].desc.vsel_mask = info->vmode;
+ drvdata[data_idx].desc.enable_reg = offset;
+ drvdata[data_idx].desc.enable_mask = info->enable_mask;
+ drvdata[data_idx].desc.enable_val = info->enable;
+ drvdata[data_idx].desc.disable_val = info->disable_val;
+
+ cfg.init_data = pbias_matches[idx].init_data;
+ cfg.driver_data = &drvdata[data_idx];
+ cfg.of_node = pbias_matches[idx].of_node;
+
+ drvdata[data_idx].dev = devm_regulator_register(&pdev->dev,
+ &drvdata[data_idx].desc, &cfg);
+ if (IS_ERR(drvdata[data_idx].dev)) {
+ ret = PTR_ERR(drvdata[data_idx].dev);
+ dev_err(&pdev->dev,
+ "Failed to register regulator: %d\n", ret);
+ goto err_regulator;
+ }
+ data_idx++;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+err_regulator:
+ return ret;
+}
+
+static struct platform_driver pbias_regulator_driver = {
+ .probe = pbias_regulator_probe,
+ .driver = {
+ .name = "pbias-regulator",
+ .of_match_table = of_match_ptr(pbias_of_match),
+ },
+};
+
+module_platform_driver(pbias_regulator_driver);
+
+MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
+MODULE_DESCRIPTION("pbias voltage regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pbias-regulator");
diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c
new file mode 100644
index 000000000..79cb971a6
--- /dev/null
+++ b/drivers/regulator/pcap-regulator.c
@@ -0,0 +1,281 @@
+/*
+ * PCAP2 Regulator Driver
+ *
+ * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/ezx-pcap.h>
+
+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 const 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 = dev_get_platdata(&pdev->dev);
+ config.driver_data = pcap;
+
+ rdev = devm_regulator_register(&pdev->dev, &pcap_regulators[pdev->id],
+ &config);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static struct platform_driver pcap_regulator_driver = {
+ .driver = {
+ .name = "pcap-regulator",
+ },
+ .probe = pcap_regulator_probe,
+};
+
+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 <drwyrm@gmail.com>");
+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 000000000..762e18447
--- /dev/null
+++ b/drivers/regulator/pcf50633-regulator.c
@@ -0,0 +1,128 @@
+/* NXP PCF50633 PMIC Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/pmic.h>
+
+#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 const 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 = dev_get_platdata(&pdev->dev);
+ config.driver_data = pcf;
+ config.regmap = pcf->regmap;
+
+ rdev = devm_regulator_register(&pdev->dev, &regulators[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 struct platform_driver pcf50633_regulator_driver = {
+ .driver = {
+ .name = "pcf50633-regulator",
+ },
+ .probe = pcf50633_regulator_probe,
+};
+
+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 <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-regulator");
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c
new file mode 100644
index 000000000..8b1940110
--- /dev/null
+++ b/drivers/regulator/pfuze100-regulator.c
@@ -0,0 +1,800 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/pfuze100.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+#define PFUZE_FLAG_DISABLE_SW BIT(1)
+
+#define PFUZE_NUMREGS 128
+#define PFUZE100_VOL_OFFSET 0
+#define PFUZE100_STANDBY_OFFSET 1
+#define PFUZE100_MODE_OFFSET 3
+#define PFUZE100_CONF_OFFSET 4
+
+#define PFUZE100_DEVICEID 0x0
+#define PFUZE100_REVID 0x3
+#define PFUZE100_FABID 0x4
+
+#define PFUZE100_COINVOL 0x1a
+#define PFUZE100_SW1ABVOL 0x20
+#define PFUZE100_SW1CVOL 0x2e
+#define PFUZE100_SW2VOL 0x35
+#define PFUZE100_SW3AVOL 0x3c
+#define PFUZE100_SW3BVOL 0x43
+#define PFUZE100_SW4VOL 0x4a
+#define PFUZE100_SWBSTCON1 0x66
+#define PFUZE100_VREFDDRCON 0x6a
+#define PFUZE100_VSNVSVOL 0x6b
+#define PFUZE100_VGEN1VOL 0x6c
+#define PFUZE100_VGEN2VOL 0x6d
+#define PFUZE100_VGEN3VOL 0x6e
+#define PFUZE100_VGEN4VOL 0x6f
+#define PFUZE100_VGEN5VOL 0x70
+#define PFUZE100_VGEN6VOL 0x71
+
+enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, };
+
+struct pfuze_regulator {
+ struct regulator_desc desc;
+ unsigned char stby_reg;
+ unsigned char stby_mask;
+ bool sw_reg;
+};
+
+struct pfuze_chip {
+ int chip_id;
+ int flags;
+ struct regmap *regmap;
+ struct device *dev;
+ struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR];
+ struct regulator_dev *regulators[PFUZE100_MAX_REGULATOR];
+ struct pfuze_regulator *pfuze_regulators;
+};
+
+static const int pfuze100_swbst[] = {
+ 5000000, 5050000, 5100000, 5150000,
+};
+
+static const int pfuze100_vsnvs[] = {
+ 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000,
+};
+
+static const int pfuze100_coin[] = {
+ 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000,
+};
+
+static const int pfuze3000_sw1a[] = {
+ 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, 1800000, 3300000,
+};
+
+static const int pfuze3000_sw2lo[] = {
+ 1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000,
+};
+
+static const int pfuze3000_sw2hi[] = {
+ 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000,
+};
+
+static const struct i2c_device_id pfuze_device_id[] = {
+ {.name = "pfuze100", .driver_data = PFUZE100},
+ {.name = "pfuze200", .driver_data = PFUZE200},
+ {.name = "pfuze3000", .driver_data = PFUZE3000},
+ {.name = "pfuze3001", .driver_data = PFUZE3001},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
+
+static const struct of_device_id pfuze_dt_ids[] = {
+ { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100},
+ { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
+ { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000},
+ { .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001},
+ { }
+};
+MODULE_DEVICE_TABLE(of, pfuze_dt_ids);
+
+static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ bool reg_has_ramp_delay;
+ unsigned int ramp_bits;
+ int ret;
+
+ switch (pfuze100->chip_id) {
+ case PFUZE3001:
+ /* no dynamic voltage scaling for PF3001 */
+ reg_has_ramp_delay = false;
+ break;
+ case PFUZE3000:
+ reg_has_ramp_delay = (id < PFUZE3000_SWBST);
+ break;
+ case PFUZE200:
+ reg_has_ramp_delay = (id < PFUZE200_SWBST);
+ break;
+ case PFUZE100:
+ default:
+ reg_has_ramp_delay = (id < PFUZE100_SWBST);
+ break;
+ }
+
+ if (reg_has_ramp_delay) {
+ ramp_delay = 12500 / ramp_delay;
+ ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3);
+ ret = regmap_update_bits(pfuze100->regmap,
+ rdev->desc->vsel_reg + 4,
+ 0xc0, ramp_bits << 6);
+ if (ret < 0)
+ dev_err(pfuze100->dev, "ramp failed, err %d\n", ret);
+ } else {
+ ret = -EACCES;
+ }
+
+ return ret;
+}
+
+static const struct regulator_ops pfuze100_ldo_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static const struct regulator_ops pfuze100_fixed_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static const struct regulator_ops pfuze100_sw_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = pfuze100_set_ramp_delay,
+};
+
+static const struct regulator_ops pfuze100_sw_disable_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = pfuze100_set_ramp_delay,
+};
+
+static const struct regulator_ops pfuze100_swb_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .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,
+
+};
+
+static const struct regulator_ops pfuze3000_sw_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .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,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = pfuze100_set_ramp_delay,
+
+};
+
+#define PFUZE100_FIXED_REG(_chip, _name, base, voltage) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = 1, \
+ .ops = &pfuze100_fixed_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (voltage), \
+ .enable_reg = (base), \
+ .enable_mask = 0x10, \
+ }, \
+ }
+
+#define PFUZE100_SW_REG(_chip, _name, base, min, max, step) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name,\
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &pfuze100_sw_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = (base) + PFUZE100_MODE_OFFSET, \
+ .enable_mask = 0xf, \
+ }, \
+ .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
+ .stby_mask = 0x3f, \
+ .sw_reg = true, \
+ }
+
+#define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pfuze100_swb_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base), \
+ .vsel_mask = (mask), \
+ .enable_reg = (base), \
+ .enable_mask = 0x48, \
+ }, \
+ }
+
+#define PFUZE100_VGEN_REG(_chip, _name, base, min, max, step) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &pfuze100_ldo_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base), \
+ .vsel_mask = 0xf, \
+ .enable_reg = (base), \
+ .enable_mask = 0x10, \
+ }, \
+ .stby_reg = (base), \
+ .stby_mask = 0x20, \
+ }
+
+#define PFUZE100_COIN_REG(_chip, _name, base, mask, voltages) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pfuze100_swb_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base), \
+ .vsel_mask = (mask), \
+ .enable_reg = (base), \
+ .enable_mask = 0x8, \
+ }, \
+ }
+
+#define PFUZE3000_VCC_REG(_chip, _name, base, min, max, step) { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &pfuze100_ldo_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base), \
+ .vsel_mask = 0x3, \
+ .enable_reg = (base), \
+ .enable_mask = 0x10, \
+ }, \
+ .stby_reg = (base), \
+ .stby_mask = 0x20, \
+}
+
+/* No linar case for the some switches of PFUZE3000 */
+#define PFUZE3000_SW_REG(_chip, _name, base, mask, voltages) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pfuze3000_sw_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
+ .vsel_mask = (mask), \
+ .enable_reg = (base) + PFUZE100_MODE_OFFSET, \
+ .enable_mask = 0xf, \
+ .enable_val = 0x8, \
+ .enable_time = 500, \
+ }, \
+ .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
+ .stby_mask = (mask), \
+ .sw_reg = true, \
+ }
+
+#define PFUZE3000_SW3_REG(_chip, _name, base, min, max, step) { \
+ .desc = { \
+ .name = #_name,\
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &pfuze100_sw_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
+ .vsel_mask = 0xf, \
+ }, \
+ .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
+ .stby_mask = 0xf, \
+}
+
+/* PFUZE100 */
+static struct pfuze_regulator pfuze100_regulators[] = {
+ PFUZE100_SW_REG(PFUZE100, SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000),
+ PFUZE100_SW_REG(PFUZE100, SW1C, PFUZE100_SW1CVOL, 300000, 1875000, 25000),
+ PFUZE100_SW_REG(PFUZE100, SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(PFUZE100, SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(PFUZE100, SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(PFUZE100, SW4, PFUZE100_SW4VOL, 400000, 1975000, 25000),
+ PFUZE100_SWB_REG(PFUZE100, SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst),
+ PFUZE100_SWB_REG(PFUZE100, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
+ PFUZE100_FIXED_REG(PFUZE100, VREFDDR, PFUZE100_VREFDDRCON, 750000),
+ PFUZE100_VGEN_REG(PFUZE100, VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000),
+ PFUZE100_VGEN_REG(PFUZE100, VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
+ PFUZE100_VGEN_REG(PFUZE100, VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE100, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE100, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE100, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+};
+
+static struct pfuze_regulator pfuze200_regulators[] = {
+ PFUZE100_SW_REG(PFUZE200, SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000),
+ PFUZE100_SW_REG(PFUZE200, SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(PFUZE200, SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(PFUZE200, SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000),
+ PFUZE100_SWB_REG(PFUZE200, SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst),
+ PFUZE100_SWB_REG(PFUZE200, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
+ PFUZE100_FIXED_REG(PFUZE200, VREFDDR, PFUZE100_VREFDDRCON, 750000),
+ PFUZE100_VGEN_REG(PFUZE200, VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000),
+ PFUZE100_VGEN_REG(PFUZE200, VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
+ PFUZE100_VGEN_REG(PFUZE200, VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE200, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE200, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+ PFUZE100_COIN_REG(PFUZE200, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin),
+};
+
+static struct pfuze_regulator pfuze3000_regulators[] = {
+ PFUZE3000_SW_REG(PFUZE3000, SW1A, PFUZE100_SW1ABVOL, 0x1f, pfuze3000_sw1a),
+ PFUZE100_SW_REG(PFUZE3000, SW1B, PFUZE100_SW1CVOL, 700000, 1475000, 25000),
+ PFUZE3000_SW_REG(PFUZE3000, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo),
+ PFUZE3000_SW3_REG(PFUZE3000, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000),
+ PFUZE100_SWB_REG(PFUZE3000, SWBST, PFUZE100_SWBSTCON1, 0x3, pfuze100_swbst),
+ PFUZE100_SWB_REG(PFUZE3000, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
+ PFUZE100_FIXED_REG(PFUZE3000, VREFDDR, PFUZE100_VREFDDRCON, 750000),
+ PFUZE100_VGEN_REG(PFUZE3000, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE3000, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
+ PFUZE3000_VCC_REG(PFUZE3000, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000),
+ PFUZE3000_VCC_REG(PFUZE3000, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000),
+ PFUZE100_VGEN_REG(PFUZE3000, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+};
+
+static struct pfuze_regulator pfuze3001_regulators[] = {
+ PFUZE3000_SW_REG(PFUZE3001, SW1, PFUZE100_SW1ABVOL, 0x1f, pfuze3000_sw1a),
+ PFUZE3000_SW_REG(PFUZE3001, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo),
+ PFUZE3000_SW3_REG(PFUZE3001, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000),
+ PFUZE100_SWB_REG(PFUZE3001, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
+ PFUZE100_VGEN_REG(PFUZE3001, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE3001, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
+ PFUZE3000_VCC_REG(PFUZE3001, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000),
+ PFUZE3000_VCC_REG(PFUZE3001, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000),
+ PFUZE100_VGEN_REG(PFUZE3001, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+};
+
+#ifdef CONFIG_OF
+/* PFUZE100 */
+static struct of_regulator_match pfuze100_matches[] = {
+ { .name = "sw1ab", },
+ { .name = "sw1c", },
+ { .name = "sw2", },
+ { .name = "sw3a", },
+ { .name = "sw3b", },
+ { .name = "sw4", },
+ { .name = "swbst", },
+ { .name = "vsnvs", },
+ { .name = "vrefddr", },
+ { .name = "vgen1", },
+ { .name = "vgen2", },
+ { .name = "vgen3", },
+ { .name = "vgen4", },
+ { .name = "vgen5", },
+ { .name = "vgen6", },
+};
+
+/* PFUZE200 */
+static struct of_regulator_match pfuze200_matches[] = {
+
+ { .name = "sw1ab", },
+ { .name = "sw2", },
+ { .name = "sw3a", },
+ { .name = "sw3b", },
+ { .name = "swbst", },
+ { .name = "vsnvs", },
+ { .name = "vrefddr", },
+ { .name = "vgen1", },
+ { .name = "vgen2", },
+ { .name = "vgen3", },
+ { .name = "vgen4", },
+ { .name = "vgen5", },
+ { .name = "vgen6", },
+ { .name = "coin", },
+};
+
+/* PFUZE3000 */
+static struct of_regulator_match pfuze3000_matches[] = {
+
+ { .name = "sw1a", },
+ { .name = "sw1b", },
+ { .name = "sw2", },
+ { .name = "sw3", },
+ { .name = "swbst", },
+ { .name = "vsnvs", },
+ { .name = "vrefddr", },
+ { .name = "vldo1", },
+ { .name = "vldo2", },
+ { .name = "vccsd", },
+ { .name = "v33", },
+ { .name = "vldo3", },
+ { .name = "vldo4", },
+};
+
+/* PFUZE3001 */
+static struct of_regulator_match pfuze3001_matches[] = {
+
+ { .name = "sw1", },
+ { .name = "sw2", },
+ { .name = "sw3", },
+ { .name = "vsnvs", },
+ { .name = "vldo1", },
+ { .name = "vldo2", },
+ { .name = "vccsd", },
+ { .name = "v33", },
+ { .name = "vldo3", },
+ { .name = "vldo4", },
+};
+
+static struct of_regulator_match *pfuze_matches;
+
+static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
+{
+ struct device *dev = chip->dev;
+ struct device_node *np, *parent;
+ int ret;
+
+ np = of_node_get(dev->of_node);
+ if (!np)
+ return -EINVAL;
+
+ if (of_property_read_bool(np, "fsl,pfuze-support-disable-sw"))
+ chip->flags |= PFUZE_FLAG_DISABLE_SW;
+
+ parent = of_get_child_by_name(np, "regulators");
+ if (!parent) {
+ dev_err(dev, "regulators node not found\n");
+ of_node_put(np);
+ return -EINVAL;
+ }
+
+ switch (chip->chip_id) {
+ case PFUZE3001:
+ pfuze_matches = pfuze3001_matches;
+ ret = of_regulator_match(dev, parent, pfuze3001_matches,
+ ARRAY_SIZE(pfuze3001_matches));
+ break;
+ case PFUZE3000:
+ pfuze_matches = pfuze3000_matches;
+ ret = of_regulator_match(dev, parent, pfuze3000_matches,
+ ARRAY_SIZE(pfuze3000_matches));
+ break;
+ case PFUZE200:
+ pfuze_matches = pfuze200_matches;
+ ret = of_regulator_match(dev, parent, pfuze200_matches,
+ ARRAY_SIZE(pfuze200_matches));
+ break;
+
+ case PFUZE100:
+ default:
+ pfuze_matches = pfuze100_matches;
+ ret = of_regulator_match(dev, parent, pfuze100_matches,
+ ARRAY_SIZE(pfuze100_matches));
+ break;
+ }
+
+ of_node_put(parent);
+ of_node_put(np);
+ if (ret < 0) {
+ dev_err(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 pfuze_matches[index].init_data;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return pfuze_matches[index].of_node;
+}
+#else
+static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
+{
+ 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 pfuze_identify(struct pfuze_chip *pfuze_chip)
+{
+ unsigned int value;
+ int ret;
+
+ ret = regmap_read(pfuze_chip->regmap, PFUZE100_DEVICEID, &value);
+ if (ret)
+ return ret;
+
+ if (((value & 0x0f) == 0x8) && (pfuze_chip->chip_id == PFUZE100)) {
+ /*
+ * Freescale misprogrammed 1-3% of parts prior to week 8 of 2013
+ * as ID=8 in PFUZE100
+ */
+ dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8");
+ } else if ((value & 0x0f) != pfuze_chip->chip_id &&
+ (value & 0xf0) >> 4 != pfuze_chip->chip_id &&
+ (value != pfuze_chip->chip_id)) {
+ /* device id NOT match with your setting */
+ dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(pfuze_chip->regmap, PFUZE100_REVID, &value);
+ if (ret)
+ return ret;
+ dev_info(pfuze_chip->dev,
+ "Full layer: %x, Metal layer: %x\n",
+ (value & 0xf0) >> 4, value & 0x0f);
+
+ ret = regmap_read(pfuze_chip->regmap, PFUZE100_FABID, &value);
+ if (ret)
+ return ret;
+ dev_info(pfuze_chip->dev, "FAB: %x, FIN: %x\n",
+ (value & 0xc) >> 2, value & 0x3);
+
+ return 0;
+}
+
+static const struct regmap_config pfuze_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PFUZE_NUMREGS - 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int pfuze100_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pfuze_chip *pfuze_chip;
+ struct pfuze_regulator_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct regulator_config config = { };
+ int i, ret;
+ const struct of_device_id *match;
+ u32 regulator_num;
+ u32 sw_check_start, sw_check_end, sw_hi = 0x40;
+
+ pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip),
+ GFP_KERNEL);
+ if (!pfuze_chip)
+ return -ENOMEM;
+
+ if (client->dev.of_node) {
+ match = of_match_device(of_match_ptr(pfuze_dt_ids),
+ &client->dev);
+ if (!match) {
+ dev_err(&client->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ pfuze_chip->chip_id = (int)(long)match->data;
+ } else if (id) {
+ pfuze_chip->chip_id = id->driver_data;
+ } else {
+ dev_err(&client->dev, "No dts match or id table match found\n");
+ return -ENODEV;
+ }
+
+ i2c_set_clientdata(client, pfuze_chip);
+ pfuze_chip->dev = &client->dev;
+
+ pfuze_chip->regmap = devm_regmap_init_i2c(client, &pfuze_regmap_config);
+ if (IS_ERR(pfuze_chip->regmap)) {
+ ret = PTR_ERR(pfuze_chip->regmap);
+ dev_err(&client->dev,
+ "regmap allocation failed with err %d\n", ret);
+ return ret;
+ }
+
+ ret = pfuze_identify(pfuze_chip);
+ if (ret) {
+ dev_err(&client->dev, "unrecognized pfuze chip ID!\n");
+ return ret;
+ }
+
+ /* use the right regulators after identify the right device */
+ switch (pfuze_chip->chip_id) {
+ case PFUZE3001:
+ pfuze_chip->pfuze_regulators = pfuze3001_regulators;
+ regulator_num = ARRAY_SIZE(pfuze3001_regulators);
+ sw_check_start = PFUZE3001_SW2;
+ sw_check_end = PFUZE3001_SW2;
+ sw_hi = 1 << 3;
+ break;
+ case PFUZE3000:
+ pfuze_chip->pfuze_regulators = pfuze3000_regulators;
+ regulator_num = ARRAY_SIZE(pfuze3000_regulators);
+ sw_check_start = PFUZE3000_SW2;
+ sw_check_end = PFUZE3000_SW2;
+ sw_hi = 1 << 3;
+ break;
+ case PFUZE200:
+ pfuze_chip->pfuze_regulators = pfuze200_regulators;
+ regulator_num = ARRAY_SIZE(pfuze200_regulators);
+ sw_check_start = PFUZE200_SW2;
+ sw_check_end = PFUZE200_SW3B;
+ break;
+ case PFUZE100:
+ default:
+ pfuze_chip->pfuze_regulators = pfuze100_regulators;
+ regulator_num = ARRAY_SIZE(pfuze100_regulators);
+ sw_check_start = PFUZE100_SW2;
+ sw_check_end = PFUZE100_SW4;
+ break;
+ }
+ dev_info(&client->dev, "pfuze%s found.\n",
+ (pfuze_chip->chip_id == PFUZE100) ? "100" :
+ (((pfuze_chip->chip_id == PFUZE200) ? "200" :
+ ((pfuze_chip->chip_id == PFUZE3000) ? "3000" : "3001"))));
+
+ memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators,
+ sizeof(pfuze_chip->regulator_descs));
+
+ ret = pfuze_parse_regulators_dt(pfuze_chip);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < regulator_num; i++) {
+ struct regulator_init_data *init_data;
+ struct regulator_desc *desc;
+ int val;
+
+ desc = &pfuze_chip->regulator_descs[i].desc;
+
+ if (pdata)
+ init_data = pdata->init_data[i];
+ else
+ init_data = match_init_data(i);
+
+ /* SW2~SW4 high bit check and modify the voltage value table */
+ if (i >= sw_check_start && i <= sw_check_end) {
+ ret = regmap_read(pfuze_chip->regmap,
+ desc->vsel_reg, &val);
+ if (ret) {
+ dev_err(&client->dev, "Fails to read from the register.\n");
+ return ret;
+ }
+
+ if (val & sw_hi) {
+ if (pfuze_chip->chip_id == PFUZE3000 ||
+ pfuze_chip->chip_id == PFUZE3001) {
+ desc->volt_table = pfuze3000_sw2hi;
+ desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi);
+ } else {
+ desc->min_uV = 800000;
+ desc->uV_step = 50000;
+ desc->n_voltages = 51;
+ }
+ }
+ }
+
+ /*
+ * Allow SW regulators to turn off. Checking it trough a flag is
+ * a workaround to keep the backward compatibility with existing
+ * old dtb's which may relay on the fact that we didn't disable
+ * the switched regulator till yet.
+ */
+ if (pfuze_chip->flags & PFUZE_FLAG_DISABLE_SW) {
+ if (pfuze_chip->chip_id == PFUZE100 ||
+ pfuze_chip->chip_id == PFUZE200) {
+ if (pfuze_chip->regulator_descs[i].sw_reg) {
+ desc->ops = &pfuze100_sw_disable_regulator_ops;
+ desc->enable_val = 0x8;
+ desc->disable_val = 0x0;
+ desc->enable_time = 500;
+ }
+ }
+ }
+
+ config.dev = &client->dev;
+ config.init_data = init_data;
+ config.driver_data = pfuze_chip;
+ config.of_node = match_of_node(i);
+
+ pfuze_chip->regulators[i] =
+ devm_regulator_register(&client->dev, desc, &config);
+ if (IS_ERR(pfuze_chip->regulators[i])) {
+ dev_err(&client->dev, "register regulator%s failed\n",
+ pfuze_chip->pfuze_regulators[i].desc.name);
+ return PTR_ERR(pfuze_chip->regulators[i]);
+ }
+ }
+
+ return 0;
+}
+
+static struct i2c_driver pfuze_driver = {
+ .id_table = pfuze_device_id,
+ .driver = {
+ .name = "pfuze100-regulator",
+ .of_match_table = pfuze_dt_ids,
+ },
+ .probe = pfuze100_regulator_probe,
+};
+module_i2c_driver(pfuze_driver);
+
+MODULE_AUTHOR("Robin Gong <b38343@freescale.com>");
+MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000/3001 PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c
new file mode 100644
index 000000000..1f2d81805
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.c
@@ -0,0 +1,434 @@
+/*
+ * pv88060-regulator.c - Regulator device driver for PV88060
+ * Copyright (C) 2015 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include "pv88060-regulator.h"
+
+#define PV88060_MAX_REGULATORS 14
+
+/* PV88060 REGULATOR IDs */
+enum {
+ /* BUCKs */
+ PV88060_ID_BUCK1,
+
+ /* LDOs */
+ PV88060_ID_LDO1,
+ PV88060_ID_LDO2,
+ PV88060_ID_LDO3,
+ PV88060_ID_LDO4,
+ PV88060_ID_LDO5,
+ PV88060_ID_LDO6,
+ PV88060_ID_LDO7,
+
+ /* SWTs */
+ PV88060_ID_SW1,
+ PV88060_ID_SW2,
+ PV88060_ID_SW3,
+ PV88060_ID_SW4,
+ PV88060_ID_SW5,
+ PV88060_ID_SW6,
+};
+
+struct pv88060_regulator {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned n_current_limits;
+ const int *current_limits;
+ unsigned int limit_mask;
+ unsigned int conf; /* buck configuration register */
+};
+
+struct pv88060 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[PV88060_MAX_REGULATORS];
+};
+
+static const struct regmap_config pv88060_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88060_buck1_limits[] = {
+ 1496000, 2393000, 3291000, 4189000
+};
+
+static unsigned int pv88060_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret, mode = 0;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ switch (data & PV88060_BUCK_MODE_MASK) {
+ case PV88060_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case PV88060_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case PV88060_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ }
+
+ return mode;
+}
+
+static int pv88060_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = PV88060_BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = PV88060_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = PV88060_BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, info->conf,
+ PV88060_BUCK_MODE_MASK, val);
+}
+
+static int pv88060_set_current_limit(struct regulator_dev *rdev, int min,
+ int max)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* search for closest to maximum */
+ for (i = info->n_current_limits - 1; i >= 0; i--) {
+ if (min <= info->current_limits[i]
+ && max >= info->current_limits[i]) {
+ return regmap_update_bits(rdev->regmap,
+ info->conf,
+ info->limit_mask,
+ i << PV88060_BUCK_ILIM_SHIFT);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int pv88060_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ data = (data & info->limit_mask) >> PV88060_BUCK_ILIM_SHIFT;
+ return info->current_limits[data];
+}
+
+static const struct regulator_ops pv88060_buck_ops = {
+ .get_mode = pv88060_buck_get_mode,
+ .set_mode = pv88060_buck_set_mode,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = pv88060_set_current_limit,
+ .get_current_limit = pv88060_get_current_limit,
+};
+
+static const struct regulator_ops pv88060_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88060_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88060_buck_ops,\
+ .min_uV = min,\
+ .uV_step = step,\
+ .n_voltages = ((max) - (min))/(step) + 1,\
+ .enable_reg = PV88060_REG_##regl_name##_CONF0,\
+ .enable_mask = PV88060_BUCK_EN, \
+ .vsel_reg = PV88060_REG_##regl_name##_CONF0,\
+ .vsel_mask = PV88060_VBUCK_MASK,\
+ },\
+ .current_limits = limits_array,\
+ .n_current_limits = ARRAY_SIZE(limits_array),\
+ .limit_mask = PV88060_BUCK_ILIM_MASK, \
+ .conf = PV88060_REG_##regl_name##_CONF1,\
+}
+
+#define PV88060_LDO(chip, regl_name, min, step, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88060_ldo_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = (step) ? ((max - min) / step + 1) : 1, \
+ .enable_reg = PV88060_REG_##regl_name##_CONF, \
+ .enable_mask = PV88060_LDO_EN, \
+ .vsel_reg = PV88060_REG_##regl_name##_CONF, \
+ .vsel_mask = PV88060_VLDO_MASK, \
+ },\
+}
+
+#define PV88060_SW(chip, regl_name, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88060_ldo_ops,\
+ .min_uV = max,\
+ .uV_step = 0,\
+ .n_voltages = 1,\
+ .enable_reg = PV88060_REG_##regl_name##_CONF,\
+ .enable_mask = PV88060_SW_EN,\
+ },\
+}
+
+static const struct pv88060_regulator pv88060_regulator_info[] = {
+ PV88060_BUCK(PV88060, BUCK1, 2800000, 12500, 4387500,
+ pv88060_buck1_limits),
+ PV88060_LDO(PV88060, LDO1, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO2, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO3, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO4, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO5, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO6, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO7, 1200000, 50000, 3350000),
+ PV88060_SW(PV88060, SW1, 5000000),
+ PV88060_SW(PV88060, SW2, 5000000),
+ PV88060_SW(PV88060, SW3, 5000000),
+ PV88060_SW(PV88060, SW4, 5000000),
+ PV88060_SW(PV88060, SW5, 5000000),
+ PV88060_SW(PV88060, SW6, 5000000),
+};
+
+static irqreturn_t pv88060_irq_handler(int irq, void *data)
+{
+ struct pv88060 *chip = data;
+ int i, reg_val, err, ret = IRQ_NONE;
+
+ err = regmap_read(chip->regmap, PV88060_REG_EVENT_A, &reg_val);
+ if (err < 0)
+ goto error_i2c;
+
+ if (reg_val & PV88060_E_VDD_FLT) {
+ for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ }
+ }
+
+ err = regmap_write(chip->regmap, PV88060_REG_EVENT_A,
+ PV88060_E_VDD_FLT);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (reg_val & PV88060_E_OVER_TEMP) {
+ for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_OVER_TEMP,
+ NULL);
+ }
+ }
+
+ err = regmap_write(chip->regmap, PV88060_REG_EVENT_A,
+ PV88060_E_OVER_TEMP);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(chip->dev, "I2C error : %d\n", err);
+ return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88060_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+ struct pv88060 *chip;
+ struct regulator_config config = { };
+ int error, i, ret = 0;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88060), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &i2c->dev;
+ chip->regmap = devm_regmap_init_i2c(i2c, &pv88060_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ if (i2c->irq != 0) {
+ ret = regmap_write(chip->regmap, PV88060_REG_MASK_A, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask A reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, PV88060_REG_MASK_B, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask B reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, PV88060_REG_MASK_C, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask C reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ pv88060_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "pv88060", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to request IRQ: %d\n",
+ i2c->irq);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap, PV88060_REG_MASK_A,
+ PV88060_M_VDD_FLT | PV88060_M_OVER_TEMP, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to update mask reg: %d\n", ret);
+ return ret;
+ }
+
+ } else {
+ dev_warn(chip->dev, "No IRQ configured\n");
+ }
+
+ config.dev = chip->dev;
+ config.regmap = chip->regmap;
+
+ for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ config.driver_data = (void *)&pv88060_regulator_info[i];
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ &pv88060_regulator_info[i].desc, &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev,
+ "Failed to register PV88060 regulator\n");
+ return PTR_ERR(chip->rdev[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id pv88060_i2c_id[] = {
+ {"pv88060", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pv88060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88060_dt_ids[] = {
+ { .compatible = "pvs,pv88060", .data = &pv88060_i2c_id[0] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pv88060_dt_ids);
+#endif
+
+static struct i2c_driver pv88060_regulator_driver = {
+ .driver = {
+ .name = "pv88060",
+ .of_match_table = of_match_ptr(pv88060_dt_ids),
+ },
+ .probe = pv88060_i2c_probe,
+ .id_table = pv88060_i2c_id,
+};
+
+module_i2c_driver(pv88060_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88060");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88060-regulator.h b/drivers/regulator/pv88060-regulator.h
new file mode 100644
index 000000000..02ca9203a
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.h
@@ -0,0 +1,69 @@
+/*
+ * pv88060-regulator.h - Regulator definitions for PV88060
+ * Copyright (C) 2015 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __PV88060_REGISTERS_H__
+#define __PV88060_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define PV88060_REG_EVENT_A 0x04
+#define PV88060_REG_MASK_A 0x08
+#define PV88060_REG_MASK_B 0x09
+#define PV88060_REG_MASK_C 0x0A
+
+/* Regulator Registers */
+#define PV88060_REG_BUCK1_CONF0 0x1B
+#define PV88060_REG_BUCK1_CONF1 0x1C
+#define PV88060_REG_LDO1_CONF 0x1D
+#define PV88060_REG_LDO2_CONF 0x1E
+#define PV88060_REG_LDO3_CONF 0x1F
+#define PV88060_REG_LDO4_CONF 0x20
+#define PV88060_REG_LDO5_CONF 0x21
+#define PV88060_REG_LDO6_CONF 0x22
+#define PV88060_REG_LDO7_CONF 0x23
+
+#define PV88060_REG_SW1_CONF 0x3B
+#define PV88060_REG_SW2_CONF 0x3C
+#define PV88060_REG_SW3_CONF 0x3D
+#define PV88060_REG_SW4_CONF 0x3E
+#define PV88060_REG_SW5_CONF 0x3F
+#define PV88060_REG_SW6_CONF 0x40
+
+/* PV88060_REG_EVENT_A (addr=0x04) */
+#define PV88060_E_VDD_FLT 0x01
+#define PV88060_E_OVER_TEMP 0x02
+
+/* PV88060_REG_MASK_A (addr=0x08) */
+#define PV88060_M_VDD_FLT 0x01
+#define PV88060_M_OVER_TEMP 0x02
+
+/* PV88060_REG_BUCK1_CONF0 (addr=0x1B) */
+#define PV88060_BUCK_EN 0x80
+#define PV88060_VBUCK_MASK 0x7F
+/* PV88060_REG_LDO1/2/3/4/5/6/7_CONT */
+#define PV88060_LDO_EN 0x40
+#define PV88060_VLDO_MASK 0x3F
+/* PV88060_REG_SW1/2/3/4/5_CONF */
+#define PV88060_SW_EN 0x80
+
+/* PV88060_REG_BUCK1_CONF1 (addr=0x1C) */
+#define PV88060_BUCK_ILIM_SHIFT 2
+#define PV88060_BUCK_ILIM_MASK 0x0C
+#define PV88060_BUCK_MODE_SHIFT 0
+#define PV88060_BUCK_MODE_MASK 0x03
+#define PV88060_BUCK_MODE_SLEEP 0x00
+#define PV88060_BUCK_MODE_AUTO 0x01
+#define PV88060_BUCK_MODE_SYNC 0x02
+
+#endif /* __PV88060_REGISTERS_H__ */
diff --git a/drivers/regulator/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c
new file mode 100644
index 000000000..6770e4de2
--- /dev/null
+++ b/drivers/regulator/pv88080-regulator.c
@@ -0,0 +1,621 @@
+/*
+ * pv88080-regulator.c - Regulator device driver for PV88080
+ * Copyright (C) 2016 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include "pv88080-regulator.h"
+
+#define PV88080_MAX_REGULATORS 4
+
+/* PV88080 REGULATOR IDs */
+enum {
+ /* BUCKs */
+ PV88080_ID_BUCK1,
+ PV88080_ID_BUCK2,
+ PV88080_ID_BUCK3,
+ PV88080_ID_HVBUCK,
+};
+
+enum pv88080_types {
+ TYPE_PV88080_AA,
+ TYPE_PV88080_BA,
+};
+
+struct pv88080_regulator {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned int n_current_limits;
+ const int *current_limits;
+ unsigned int limit_mask;
+ unsigned int mode_reg;
+ unsigned int limit_reg;
+ unsigned int conf2;
+ unsigned int conf5;
+};
+
+struct pv88080 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[PV88080_MAX_REGULATORS];
+ unsigned long type;
+ const struct pv88080_compatible_regmap *regmap_config;
+};
+
+struct pv88080_buck_voltage {
+ int min_uV;
+ int max_uV;
+ int uV_step;
+};
+
+struct pv88080_buck_regmap {
+ /* REGS */
+ int buck_enable_reg;
+ int buck_vsel_reg;
+ int buck_mode_reg;
+ int buck_limit_reg;
+ int buck_vdac_range_reg;
+ int buck_vrange_gain_reg;
+ /* MASKS */
+ int buck_enable_mask;
+ int buck_vsel_mask;
+ int buck_limit_mask;
+};
+
+struct pv88080_compatible_regmap {
+ /* BUCK1, 2, 3 */
+ struct pv88080_buck_regmap buck_regmap[PV88080_MAX_REGULATORS-1];
+ /* HVBUCK */
+ int hvbuck_enable_reg;
+ int hvbuck_vsel_reg;
+ int hvbuck_enable_mask;
+ int hvbuck_vsel_mask;
+};
+
+static const struct regmap_config pv88080_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1, BUCK2, BUCK3.
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88080_buck1_limits[] = {
+ 3230000, 5130000, 6960000, 8790000
+};
+
+static const int pv88080_buck23_limits[] = {
+ 1496000, 2393000, 3291000, 4189000
+};
+
+static const struct pv88080_buck_voltage pv88080_buck_vol[2] = {
+ {
+ .min_uV = 600000,
+ .max_uV = 1393750,
+ .uV_step = 6250,
+ },
+ {
+ .min_uV = 1400000,
+ .max_uV = 2193750,
+ .uV_step = 6250,
+ },
+};
+
+static const struct pv88080_compatible_regmap pv88080_aa_regs = {
+ /* BUCK1 */
+ .buck_regmap[0] = {
+ .buck_enable_reg = PV88080AA_REG_BUCK1_CONF0,
+ .buck_vsel_reg = PV88080AA_REG_BUCK1_CONF0,
+ .buck_mode_reg = PV88080AA_REG_BUCK1_CONF1,
+ .buck_limit_reg = PV88080AA_REG_BUCK1_CONF1,
+ .buck_vdac_range_reg = PV88080AA_REG_BUCK1_CONF2,
+ .buck_vrange_gain_reg = PV88080AA_REG_BUCK1_CONF5,
+ .buck_enable_mask = PV88080_BUCK1_EN,
+ .buck_vsel_mask = PV88080_VBUCK1_MASK,
+ .buck_limit_mask = PV88080_BUCK1_ILIM_MASK,
+ },
+ /* BUCK2 */
+ .buck_regmap[1] = {
+ .buck_enable_reg = PV88080AA_REG_BUCK2_CONF0,
+ .buck_vsel_reg = PV88080AA_REG_BUCK2_CONF0,
+ .buck_mode_reg = PV88080AA_REG_BUCK2_CONF1,
+ .buck_limit_reg = PV88080AA_REG_BUCK2_CONF1,
+ .buck_vdac_range_reg = PV88080AA_REG_BUCK2_CONF2,
+ .buck_vrange_gain_reg = PV88080AA_REG_BUCK2_CONF5,
+ .buck_enable_mask = PV88080_BUCK2_EN,
+ .buck_vsel_mask = PV88080_VBUCK2_MASK,
+ .buck_limit_mask = PV88080_BUCK2_ILIM_MASK,
+ },
+ /* BUCK3 */
+ .buck_regmap[2] = {
+ .buck_enable_reg = PV88080AA_REG_BUCK3_CONF0,
+ .buck_vsel_reg = PV88080AA_REG_BUCK3_CONF0,
+ .buck_mode_reg = PV88080AA_REG_BUCK3_CONF1,
+ .buck_limit_reg = PV88080AA_REG_BUCK3_CONF1,
+ .buck_vdac_range_reg = PV88080AA_REG_BUCK3_CONF2,
+ .buck_vrange_gain_reg = PV88080AA_REG_BUCK3_CONF5,
+ .buck_enable_mask = PV88080_BUCK3_EN,
+ .buck_vsel_mask = PV88080_VBUCK3_MASK,
+ .buck_limit_mask = PV88080_BUCK3_ILIM_MASK,
+ },
+ /* HVBUCK */
+ .hvbuck_enable_reg = PV88080AA_REG_HVBUCK_CONF2,
+ .hvbuck_vsel_reg = PV88080AA_REG_HVBUCK_CONF1,
+ .hvbuck_enable_mask = PV88080_HVBUCK_EN,
+ .hvbuck_vsel_mask = PV88080_VHVBUCK_MASK,
+};
+
+static const struct pv88080_compatible_regmap pv88080_ba_regs = {
+ /* BUCK1 */
+ .buck_regmap[0] = {
+ .buck_enable_reg = PV88080BA_REG_BUCK1_CONF0,
+ .buck_vsel_reg = PV88080BA_REG_BUCK1_CONF0,
+ .buck_mode_reg = PV88080BA_REG_BUCK1_CONF1,
+ .buck_limit_reg = PV88080BA_REG_BUCK1_CONF1,
+ .buck_vdac_range_reg = PV88080BA_REG_BUCK1_CONF2,
+ .buck_vrange_gain_reg = PV88080BA_REG_BUCK1_CONF5,
+ .buck_enable_mask = PV88080_BUCK1_EN,
+ .buck_vsel_mask = PV88080_VBUCK1_MASK,
+ .buck_limit_mask = PV88080_BUCK1_ILIM_MASK,
+ },
+ /* BUCK2 */
+ .buck_regmap[1] = {
+ .buck_enable_reg = PV88080BA_REG_BUCK2_CONF0,
+ .buck_vsel_reg = PV88080BA_REG_BUCK2_CONF0,
+ .buck_mode_reg = PV88080BA_REG_BUCK2_CONF1,
+ .buck_limit_reg = PV88080BA_REG_BUCK2_CONF1,
+ .buck_vdac_range_reg = PV88080BA_REG_BUCK2_CONF2,
+ .buck_vrange_gain_reg = PV88080BA_REG_BUCK2_CONF5,
+ .buck_enable_mask = PV88080_BUCK2_EN,
+ .buck_vsel_mask = PV88080_VBUCK2_MASK,
+ .buck_limit_mask = PV88080_BUCK2_ILIM_MASK,
+ },
+ /* BUCK3 */
+ .buck_regmap[2] = {
+ .buck_enable_reg = PV88080BA_REG_BUCK3_CONF0,
+ .buck_vsel_reg = PV88080BA_REG_BUCK3_CONF0,
+ .buck_mode_reg = PV88080BA_REG_BUCK3_CONF1,
+ .buck_limit_reg = PV88080BA_REG_BUCK3_CONF1,
+ .buck_vdac_range_reg = PV88080BA_REG_BUCK3_CONF2,
+ .buck_vrange_gain_reg = PV88080BA_REG_BUCK3_CONF5,
+ .buck_enable_mask = PV88080_BUCK3_EN,
+ .buck_vsel_mask = PV88080_VBUCK3_MASK,
+ .buck_limit_mask = PV88080_BUCK3_ILIM_MASK,
+ },
+ /* HVBUCK */
+ .hvbuck_enable_reg = PV88080BA_REG_HVBUCK_CONF2,
+ .hvbuck_vsel_reg = PV88080BA_REG_HVBUCK_CONF1,
+ .hvbuck_enable_mask = PV88080_HVBUCK_EN,
+ .hvbuck_vsel_mask = PV88080_VHVBUCK_MASK,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88080_dt_ids[] = {
+ { .compatible = "pvs,pv88080", .data = (void *)TYPE_PV88080_AA },
+ { .compatible = "pvs,pv88080-aa", .data = (void *)TYPE_PV88080_AA },
+ { .compatible = "pvs,pv88080-ba", .data = (void *)TYPE_PV88080_BA },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pv88080_dt_ids);
+#endif
+
+static unsigned int pv88080_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct pv88080_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret, mode = 0;
+
+ ret = regmap_read(rdev->regmap, info->mode_reg, &data);
+ if (ret < 0)
+ return ret;
+
+ switch (data & PV88080_BUCK1_MODE_MASK) {
+ case PV88080_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case PV88080_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case PV88080_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mode;
+}
+
+static int pv88080_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct pv88080_regulator *info = rdev_get_drvdata(rdev);
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = PV88080_BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = PV88080_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = PV88080_BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, info->mode_reg,
+ PV88080_BUCK1_MODE_MASK, val);
+}
+
+static int pv88080_set_current_limit(struct regulator_dev *rdev, int min,
+ int max)
+{
+ struct pv88080_regulator *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* search for closest to maximum */
+ for (i = info->n_current_limits - 1; i >= 0; i--) {
+ if (min <= info->current_limits[i]
+ && max >= info->current_limits[i]) {
+ return regmap_update_bits(rdev->regmap,
+ info->limit_reg,
+ info->limit_mask,
+ i << PV88080_BUCK1_ILIM_SHIFT);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int pv88080_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pv88080_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, info->limit_reg, &data);
+ if (ret < 0)
+ return ret;
+
+ data = (data & info->limit_mask) >> PV88080_BUCK1_ILIM_SHIFT;
+ return info->current_limits[data];
+}
+
+static const struct regulator_ops pv88080_buck_ops = {
+ .get_mode = pv88080_buck_get_mode,
+ .set_mode = pv88080_buck_set_mode,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = pv88080_set_current_limit,
+ .get_current_limit = pv88080_get_current_limit,
+};
+
+static const struct regulator_ops pv88080_hvbuck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88080_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88080_buck_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ },\
+ .current_limits = limits_array, \
+ .n_current_limits = ARRAY_SIZE(limits_array), \
+}
+
+#define PV88080_HVBUCK(chip, regl_name, min, step, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88080_hvbuck_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ },\
+}
+
+static struct pv88080_regulator pv88080_regulator_info[] = {
+ PV88080_BUCK(PV88080, BUCK1, 600000, 6250, 1393750,
+ pv88080_buck1_limits),
+ PV88080_BUCK(PV88080, BUCK2, 600000, 6250, 1393750,
+ pv88080_buck23_limits),
+ PV88080_BUCK(PV88080, BUCK3, 600000, 6250, 1393750,
+ pv88080_buck23_limits),
+ PV88080_HVBUCK(PV88080, HVBUCK, 0, 5000, 1275000),
+};
+
+static irqreturn_t pv88080_irq_handler(int irq, void *data)
+{
+ struct pv88080 *chip = data;
+ int i, reg_val, err, ret = IRQ_NONE;
+
+ err = regmap_read(chip->regmap, PV88080_REG_EVENT_A, &reg_val);
+ if (err < 0)
+ goto error_i2c;
+
+ if (reg_val & PV88080_E_VDD_FLT) {
+ for (i = 0; i < PV88080_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ }
+ }
+
+ err = regmap_write(chip->regmap, PV88080_REG_EVENT_A,
+ PV88080_E_VDD_FLT);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (reg_val & PV88080_E_OVER_TEMP) {
+ for (i = 0; i < PV88080_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_OVER_TEMP,
+ NULL);
+ }
+ }
+
+ err = regmap_write(chip->regmap, PV88080_REG_EVENT_A,
+ PV88080_E_OVER_TEMP);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(chip->dev, "I2C error : %d\n", err);
+ return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88080_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+ struct pv88080 *chip;
+ const struct pv88080_compatible_regmap *regmap_config;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+ int i, error, ret;
+ unsigned int conf2, conf5;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88080), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &i2c->dev;
+ chip->regmap = devm_regmap_init_i2c(i2c, &pv88080_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ if (i2c->dev.of_node) {
+ match = of_match_node(pv88080_dt_ids, i2c->dev.of_node);
+ if (!match) {
+ dev_err(chip->dev, "Failed to get of_match_node\n");
+ return -EINVAL;
+ }
+ chip->type = (unsigned long)match->data;
+ } else {
+ chip->type = id->driver_data;
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ if (i2c->irq != 0) {
+ ret = regmap_write(chip->regmap, PV88080_REG_MASK_A, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask A reg: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(chip->regmap, PV88080_REG_MASK_B, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask B reg: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(chip->regmap, PV88080_REG_MASK_C, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask C reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ pv88080_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "pv88080", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to request IRQ: %d\n",
+ i2c->irq);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap, PV88080_REG_MASK_A,
+ PV88080_M_VDD_FLT | PV88080_M_OVER_TEMP, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to update mask reg: %d\n", ret);
+ return ret;
+ }
+ } else {
+ dev_warn(chip->dev, "No IRQ configured\n");
+ }
+
+ switch (chip->type) {
+ case TYPE_PV88080_AA:
+ chip->regmap_config = &pv88080_aa_regs;
+ break;
+ case TYPE_PV88080_BA:
+ chip->regmap_config = &pv88080_ba_regs;
+ break;
+ }
+
+ regmap_config = chip->regmap_config;
+ config.dev = chip->dev;
+ config.regmap = chip->regmap;
+
+ /* Registeration for BUCK1, 2, 3 */
+ for (i = 0; i < PV88080_MAX_REGULATORS-1; i++) {
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ pv88080_regulator_info[i].limit_reg
+ = regmap_config->buck_regmap[i].buck_limit_reg;
+ pv88080_regulator_info[i].limit_mask
+ = regmap_config->buck_regmap[i].buck_limit_mask;
+ pv88080_regulator_info[i].mode_reg
+ = regmap_config->buck_regmap[i].buck_mode_reg;
+ pv88080_regulator_info[i].conf2
+ = regmap_config->buck_regmap[i].buck_vdac_range_reg;
+ pv88080_regulator_info[i].conf5
+ = regmap_config->buck_regmap[i].buck_vrange_gain_reg;
+ pv88080_regulator_info[i].desc.enable_reg
+ = regmap_config->buck_regmap[i].buck_enable_reg;
+ pv88080_regulator_info[i].desc.enable_mask
+ = regmap_config->buck_regmap[i].buck_enable_mask;
+ pv88080_regulator_info[i].desc.vsel_reg
+ = regmap_config->buck_regmap[i].buck_vsel_reg;
+ pv88080_regulator_info[i].desc.vsel_mask
+ = regmap_config->buck_regmap[i].buck_vsel_mask;
+
+ ret = regmap_read(chip->regmap,
+ pv88080_regulator_info[i].conf2, &conf2);
+ if (ret < 0)
+ return ret;
+ conf2 = ((conf2 >> PV88080_BUCK_VDAC_RANGE_SHIFT) &
+ PV88080_BUCK_VDAC_RANGE_MASK);
+
+ ret = regmap_read(chip->regmap,
+ pv88080_regulator_info[i].conf5, &conf5);
+ if (ret < 0)
+ return ret;
+ conf5 = ((conf5 >> PV88080_BUCK_VRANGE_GAIN_SHIFT) &
+ PV88080_BUCK_VRANGE_GAIN_MASK);
+
+ pv88080_regulator_info[i].desc.min_uV =
+ pv88080_buck_vol[conf2].min_uV * (conf5+1);
+ pv88080_regulator_info[i].desc.uV_step =
+ pv88080_buck_vol[conf2].uV_step * (conf5+1);
+ pv88080_regulator_info[i].desc.n_voltages =
+ ((pv88080_buck_vol[conf2].max_uV * (conf5+1))
+ - (pv88080_regulator_info[i].desc.min_uV))
+ /(pv88080_regulator_info[i].desc.uV_step) + 1;
+
+ config.driver_data = (void *)&pv88080_regulator_info[i];
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ &pv88080_regulator_info[i].desc, &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev,
+ "Failed to register PV88080 regulator\n");
+ return PTR_ERR(chip->rdev[i]);
+ }
+ }
+
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.enable_reg
+ = regmap_config->hvbuck_enable_reg;
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.enable_mask
+ = regmap_config->hvbuck_enable_mask;
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.vsel_reg
+ = regmap_config->hvbuck_vsel_reg;
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.vsel_mask
+ = regmap_config->hvbuck_vsel_mask;
+
+ /* Registeration for HVBUCK */
+ if (init_data)
+ config.init_data = &init_data[PV88080_ID_HVBUCK];
+
+ config.driver_data = (void *)&pv88080_regulator_info[PV88080_ID_HVBUCK];
+ chip->rdev[PV88080_ID_HVBUCK] = devm_regulator_register(chip->dev,
+ &pv88080_regulator_info[PV88080_ID_HVBUCK].desc, &config);
+ if (IS_ERR(chip->rdev[PV88080_ID_HVBUCK])) {
+ dev_err(chip->dev, "Failed to register PV88080 regulator\n");
+ return PTR_ERR(chip->rdev[PV88080_ID_HVBUCK]);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id pv88080_i2c_id[] = {
+ { "pv88080", TYPE_PV88080_AA },
+ { "pv88080-aa", TYPE_PV88080_AA },
+ { "pv88080-ba", TYPE_PV88080_BA },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pv88080_i2c_id);
+
+static struct i2c_driver pv88080_regulator_driver = {
+ .driver = {
+ .name = "pv88080",
+ .of_match_table = of_match_ptr(pv88080_dt_ids),
+ },
+ .probe = pv88080_i2c_probe,
+ .id_table = pv88080_i2c_id,
+};
+
+module_i2c_driver(pv88080_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88080");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88080-regulator.h b/drivers/regulator/pv88080-regulator.h
new file mode 100644
index 000000000..ae25ff360
--- /dev/null
+++ b/drivers/regulator/pv88080-regulator.h
@@ -0,0 +1,118 @@
+/*
+ * pv88080-regulator.h - Regulator definitions for PV88080
+ * Copyright (C) 2016 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __PV88080_REGISTERS_H__
+#define __PV88080_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define PV88080_REG_EVENT_A 0x04
+#define PV88080_REG_MASK_A 0x09
+#define PV88080_REG_MASK_B 0x0A
+#define PV88080_REG_MASK_C 0x0B
+
+/* Regulator Registers - rev. AA */
+#define PV88080AA_REG_HVBUCK_CONF1 0x2D
+#define PV88080AA_REG_HVBUCK_CONF2 0x2E
+#define PV88080AA_REG_BUCK1_CONF0 0x27
+#define PV88080AA_REG_BUCK1_CONF1 0x28
+#define PV88080AA_REG_BUCK1_CONF2 0x59
+#define PV88080AA_REG_BUCK1_CONF5 0x5C
+#define PV88080AA_REG_BUCK2_CONF0 0x29
+#define PV88080AA_REG_BUCK2_CONF1 0x2A
+#define PV88080AA_REG_BUCK2_CONF2 0x61
+#define PV88080AA_REG_BUCK2_CONF5 0x64
+#define PV88080AA_REG_BUCK3_CONF0 0x2B
+#define PV88080AA_REG_BUCK3_CONF1 0x2C
+#define PV88080AA_REG_BUCK3_CONF2 0x69
+#define PV88080AA_REG_BUCK3_CONF5 0x6C
+
+/* Regulator Registers - rev. BA */
+#define PV88080BA_REG_HVBUCK_CONF1 0x33
+#define PV88080BA_REG_HVBUCK_CONF2 0x34
+#define PV88080BA_REG_BUCK1_CONF0 0x2A
+#define PV88080BA_REG_BUCK1_CONF1 0x2C
+#define PV88080BA_REG_BUCK1_CONF2 0x5A
+#define PV88080BA_REG_BUCK1_CONF5 0x5D
+#define PV88080BA_REG_BUCK2_CONF0 0x2D
+#define PV88080BA_REG_BUCK2_CONF1 0x2F
+#define PV88080BA_REG_BUCK2_CONF2 0x63
+#define PV88080BA_REG_BUCK2_CONF5 0x66
+#define PV88080BA_REG_BUCK3_CONF0 0x30
+#define PV88080BA_REG_BUCK3_CONF1 0x32
+#define PV88080BA_REG_BUCK3_CONF2 0x6C
+#define PV88080BA_REG_BUCK3_CONF5 0x6F
+
+/* PV88080_REG_EVENT_A (addr=0x04) */
+#define PV88080_E_VDD_FLT 0x01
+#define PV88080_E_OVER_TEMP 0x02
+
+/* PV88080_REG_MASK_A (addr=0x09) */
+#define PV88080_M_VDD_FLT 0x01
+#define PV88080_M_OVER_TEMP 0x02
+
+/* PV88080_REG_BUCK1_CONF0 (addr=0x27|0x2A) */
+#define PV88080_BUCK1_EN 0x80
+#define PV88080_VBUCK1_MASK 0x7F
+
+/* PV88080_REG_BUCK2_CONF0 (addr=0x29|0x2D) */
+#define PV88080_BUCK2_EN 0x80
+#define PV88080_VBUCK2_MASK 0x7F
+
+/* PV88080_REG_BUCK3_CONF0 (addr=0x2B|0x30) */
+#define PV88080_BUCK3_EN 0x80
+#define PV88080_VBUCK3_MASK 0x7F
+
+/* PV88080_REG_BUCK1_CONF1 (addr=0x28|0x2C) */
+#define PV88080_BUCK1_ILIM_SHIFT 2
+#define PV88080_BUCK1_ILIM_MASK 0x0C
+#define PV88080_BUCK1_MODE_MASK 0x03
+
+/* PV88080_REG_BUCK2_CONF1 (addr=0x2A|0x2F) */
+#define PV88080_BUCK2_ILIM_SHIFT 2
+#define PV88080_BUCK2_ILIM_MASK 0x0C
+#define PV88080_BUCK2_MODE_MASK 0x03
+
+/* PV88080_REG_BUCK3_CONF1 (addr=0x2C|0x32) */
+#define PV88080_BUCK3_ILIM_SHIFT 2
+#define PV88080_BUCK3_ILIM_MASK 0x0C
+#define PV88080_BUCK3_MODE_MASK 0x03
+
+#define PV88080_BUCK_MODE_SLEEP 0x00
+#define PV88080_BUCK_MODE_AUTO 0x01
+#define PV88080_BUCK_MODE_SYNC 0x02
+
+/* PV88080_REG_HVBUCK_CONF1 (addr=0x2D|0x33) */
+#define PV88080_VHVBUCK_MASK 0xFF
+
+/* PV88080_REG_HVBUCK_CONF1 (addr=0x2E|0x34) */
+#define PV88080_HVBUCK_EN 0x01
+
+/* PV88080_REG_BUCK2_CONF2 (addr=0x61|0x63) */
+/* PV88080_REG_BUCK3_CONF2 (addr=0x69|0x6C) */
+#define PV88080_BUCK_VDAC_RANGE_SHIFT 7
+#define PV88080_BUCK_VDAC_RANGE_MASK 0x01
+
+#define PV88080_BUCK_VDAC_RANGE_1 0x00
+#define PV88080_BUCK_VDAC_RANGE_2 0x01
+
+/* PV88080_REG_BUCK2_CONF5 (addr=0x64|0x66) */
+/* PV88080_REG_BUCK3_CONF5 (addr=0x6C|0x6F) */
+#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0
+#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
+
+#define PV88080_BUCK_VRANGE_GAIN_1 0x00
+#define PV88080_BUCK_VRANGE_GAIN_2 0x01
+
+#endif /* __PV88080_REGISTERS_H__ */
diff --git a/drivers/regulator/pv88090-regulator.c b/drivers/regulator/pv88090-regulator.c
new file mode 100644
index 000000000..2302b0df7
--- /dev/null
+++ b/drivers/regulator/pv88090-regulator.c
@@ -0,0 +1,460 @@
+/*
+ * pv88090-regulator.c - Regulator device driver for PV88090
+ * Copyright (C) 2015 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include "pv88090-regulator.h"
+
+#define PV88090_MAX_REGULATORS 5
+
+/* PV88090 REGULATOR IDs */
+enum {
+ /* BUCKs */
+ PV88090_ID_BUCK1,
+ PV88090_ID_BUCK2,
+ PV88090_ID_BUCK3,
+
+ /* LDOs */
+ PV88090_ID_LDO1,
+ PV88090_ID_LDO2,
+};
+
+struct pv88090_regulator {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned int n_current_limits;
+ const int *current_limits;
+ unsigned int limit_mask;
+ unsigned int conf;
+ unsigned int conf2;
+};
+
+struct pv88090 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[PV88090_MAX_REGULATORS];
+};
+
+struct pv88090_buck_voltage {
+ int min_uV;
+ int max_uV;
+ int uV_step;
+};
+
+static const struct regmap_config pv88090_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1, BUCK2, BUCK3.
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88090_buck1_limits[] = {
+ 220000, 440000, 660000, 880000, 1100000, 1320000, 1540000, 1760000,
+ 1980000, 2200000, 2420000, 2640000, 2860000, 3080000, 3300000, 3520000,
+ 3740000, 3960000, 4180000, 4400000, 4620000, 4840000, 5060000, 5280000,
+ 5500000, 5720000, 5940000, 6160000, 6380000, 6600000, 6820000, 7040000
+};
+
+static const int pv88090_buck23_limits[] = {
+ 1496000, 2393000, 3291000, 4189000
+};
+
+static const struct pv88090_buck_voltage pv88090_buck_vol[3] = {
+ {
+ .min_uV = 600000,
+ .max_uV = 1393750,
+ .uV_step = 6250,
+ },
+
+ {
+ .min_uV = 1400000,
+ .max_uV = 2193750,
+ .uV_step = 6250,
+ },
+ {
+ .min_uV = 1250000,
+ .max_uV = 2837500,
+ .uV_step = 12500,
+ },
+};
+
+static unsigned int pv88090_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret, mode = 0;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ switch (data & PV88090_BUCK1_MODE_MASK) {
+ case PV88090_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case PV88090_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case PV88090_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ }
+
+ return mode;
+}
+
+static int pv88090_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = PV88090_BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = PV88090_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = PV88090_BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, info->conf,
+ PV88090_BUCK1_MODE_MASK, val);
+}
+
+static int pv88090_set_current_limit(struct regulator_dev *rdev, int min,
+ int max)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* search for closest to maximum */
+ for (i = info->n_current_limits - 1; i >= 0; i--) {
+ if (min <= info->current_limits[i]
+ && max >= info->current_limits[i]) {
+ return regmap_update_bits(rdev->regmap,
+ info->conf,
+ info->limit_mask,
+ i << PV88090_BUCK1_ILIM_SHIFT);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int pv88090_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ data = (data & info->limit_mask) >> PV88090_BUCK1_ILIM_SHIFT;
+ return info->current_limits[data];
+}
+
+static const struct regulator_ops pv88090_buck_ops = {
+ .get_mode = pv88090_buck_get_mode,
+ .set_mode = pv88090_buck_set_mode,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = pv88090_set_current_limit,
+ .get_current_limit = pv88090_get_current_limit,
+};
+
+static const struct regulator_ops pv88090_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88090_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88090_buck_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ .enable_reg = PV88090_REG_##regl_name##_CONF0, \
+ .enable_mask = PV88090_##regl_name##_EN, \
+ .vsel_reg = PV88090_REG_##regl_name##_CONF0, \
+ .vsel_mask = PV88090_V##regl_name##_MASK, \
+ },\
+ .current_limits = limits_array, \
+ .n_current_limits = ARRAY_SIZE(limits_array), \
+ .limit_mask = PV88090_##regl_name##_ILIM_MASK, \
+ .conf = PV88090_REG_##regl_name##_CONF1, \
+ .conf2 = PV88090_REG_##regl_name##_CONF2, \
+}
+
+#define PV88090_LDO(chip, regl_name, min, step, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88090_ldo_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ .enable_reg = PV88090_REG_##regl_name##_CONT, \
+ .enable_mask = PV88090_##regl_name##_EN, \
+ .vsel_reg = PV88090_REG_##regl_name##_CONT, \
+ .vsel_mask = PV88090_V##regl_name##_MASK, \
+ },\
+}
+
+static struct pv88090_regulator pv88090_regulator_info[] = {
+ PV88090_BUCK(PV88090, BUCK1, 600000, 6250, 1393750,
+ pv88090_buck1_limits),
+ PV88090_BUCK(PV88090, BUCK2, 600000, 6250, 1393750,
+ pv88090_buck23_limits),
+ PV88090_BUCK(PV88090, BUCK3, 600000, 6250, 1393750,
+ pv88090_buck23_limits),
+ PV88090_LDO(PV88090, LDO1, 1200000, 50000, 4350000),
+ PV88090_LDO(PV88090, LDO2, 650000, 25000, 2225000),
+};
+
+static irqreturn_t pv88090_irq_handler(int irq, void *data)
+{
+ struct pv88090 *chip = data;
+ int i, reg_val, err, ret = IRQ_NONE;
+
+ err = regmap_read(chip->regmap, PV88090_REG_EVENT_A, &reg_val);
+ if (err < 0)
+ goto error_i2c;
+
+ if (reg_val & PV88090_E_VDD_FLT) {
+ for (i = 0; i < PV88090_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ }
+ }
+
+ err = regmap_write(chip->regmap, PV88090_REG_EVENT_A,
+ PV88090_E_VDD_FLT);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (reg_val & PV88090_E_OVER_TEMP) {
+ for (i = 0; i < PV88090_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_OVER_TEMP,
+ NULL);
+ }
+ }
+
+ err = regmap_write(chip->regmap, PV88090_REG_EVENT_A,
+ PV88090_E_OVER_TEMP);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(chip->dev, "I2C error : %d\n", err);
+ return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88090_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+ struct pv88090 *chip;
+ struct regulator_config config = { };
+ int error, i, ret = 0;
+ unsigned int conf2, range, index;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88090), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &i2c->dev;
+ chip->regmap = devm_regmap_init_i2c(i2c, &pv88090_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ if (i2c->irq != 0) {
+ ret = regmap_write(chip->regmap, PV88090_REG_MASK_A, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask A reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, PV88090_REG_MASK_B, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask B reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ pv88090_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "pv88090", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to request IRQ: %d\n",
+ i2c->irq);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap, PV88090_REG_MASK_A,
+ PV88090_M_VDD_FLT | PV88090_M_OVER_TEMP, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to update mask reg: %d\n", ret);
+ return ret;
+ }
+
+ } else {
+ dev_warn(chip->dev, "No IRQ configured\n");
+ }
+
+ config.dev = chip->dev;
+ config.regmap = chip->regmap;
+
+ for (i = 0; i < PV88090_MAX_REGULATORS; i++) {
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ if (i == PV88090_ID_BUCK2 || i == PV88090_ID_BUCK3) {
+ ret = regmap_read(chip->regmap,
+ pv88090_regulator_info[i].conf2, &conf2);
+ if (ret < 0)
+ return ret;
+
+ conf2 = (conf2 >> PV88090_BUCK_VDAC_RANGE_SHIFT) &
+ PV88090_BUCK_VDAC_RANGE_MASK;
+
+ ret = regmap_read(chip->regmap,
+ PV88090_REG_BUCK_FOLD_RANGE, &range);
+ if (ret < 0)
+ return ret;
+
+ range = (range >>
+ (PV88090_BUCK_VRANGE_GAIN_SHIFT + i - 1)) &
+ PV88090_BUCK_VRANGE_GAIN_MASK;
+ index = ((range << 1) | conf2);
+ if (index > PV88090_ID_BUCK3) {
+ dev_err(chip->dev,
+ "Invalid index(%d)\n", index);
+ return -EINVAL;
+ }
+
+ pv88090_regulator_info[i].desc.min_uV
+ = pv88090_buck_vol[index].min_uV;
+ pv88090_regulator_info[i].desc.uV_step
+ = pv88090_buck_vol[index].uV_step;
+ pv88090_regulator_info[i].desc.n_voltages
+ = ((pv88090_buck_vol[index].max_uV)
+ - (pv88090_buck_vol[index].min_uV))
+ /(pv88090_buck_vol[index].uV_step) + 1;
+ }
+
+ config.driver_data = (void *)&pv88090_regulator_info[i];
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ &pv88090_regulator_info[i].desc, &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev,
+ "Failed to register PV88090 regulator\n");
+ return PTR_ERR(chip->rdev[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id pv88090_i2c_id[] = {
+ {"pv88090", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pv88090_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88090_dt_ids[] = {
+ { .compatible = "pvs,pv88090", .data = &pv88090_i2c_id[0] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pv88090_dt_ids);
+#endif
+
+static struct i2c_driver pv88090_regulator_driver = {
+ .driver = {
+ .name = "pv88090",
+ .of_match_table = of_match_ptr(pv88090_dt_ids),
+ },
+ .probe = pv88090_i2c_probe,
+ .id_table = pv88090_i2c_id,
+};
+
+module_i2c_driver(pv88090_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88090");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88090-regulator.h b/drivers/regulator/pv88090-regulator.h
new file mode 100644
index 000000000..62d902927
--- /dev/null
+++ b/drivers/regulator/pv88090-regulator.h
@@ -0,0 +1,98 @@
+/*
+ * pv88090-regulator.h - Regulator definitions for PV88090
+ * Copyright (C) 2015 Powerventure Semiconductor Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __PV88090_REGISTERS_H__
+#define __PV88090_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define PV88090_REG_EVENT_A 0x03
+#define PV88090_REG_MASK_A 0x06
+#define PV88090_REG_MASK_B 0x07
+
+/* Regulator Registers */
+#define PV88090_REG_BUCK1_CONF0 0x18
+#define PV88090_REG_BUCK1_CONF1 0x19
+#define PV88090_REG_BUCK1_CONF2 0x1a
+#define PV88090_REG_BUCK2_CONF0 0x1b
+#define PV88090_REG_BUCK2_CONF1 0x1c
+#define PV88090_REG_BUCK2_CONF2 0x58
+#define PV88090_REG_BUCK3_CONF0 0x1d
+#define PV88090_REG_BUCK3_CONF1 0x1e
+#define PV88090_REG_BUCK3_CONF2 0x5c
+
+#define PV88090_REG_LDO1_CONT 0x1f
+#define PV88090_REG_LDO2_CONT 0x20
+#define PV88090_REG_LDO3_CONT 0x21
+#define PV88090_REG_BUCK_FOLD_RANGE 0x61
+
+/* PV88090_REG_EVENT_A (addr=0x03) */
+#define PV88090_E_VDD_FLT 0x01
+#define PV88090_E_OVER_TEMP 0x02
+
+/* PV88090_REG_MASK_A (addr=0x06) */
+#define PV88090_M_VDD_FLT 0x01
+#define PV88090_M_OVER_TEMP 0x02
+
+/* PV88090_REG_BUCK1_CONF0 (addr=0x18) */
+#define PV88090_BUCK1_EN 0x80
+#define PV88090_VBUCK1_MASK 0x7F
+/* PV88090_REG_BUCK2_CONF0 (addr=0x1b) */
+#define PV88090_BUCK2_EN 0x80
+#define PV88090_VBUCK2_MASK 0x7F
+/* PV88090_REG_BUCK3_CONF0 (addr=0x1d) */
+#define PV88090_BUCK3_EN 0x80
+#define PV88090_VBUCK3_MASK 0x7F
+/* PV88090_REG_LDO1_CONT (addr=0x1f) */
+#define PV88090_LDO1_EN 0x40
+#define PV88090_VLDO1_MASK 0x3F
+/* PV88090_REG_LDO2_CONT (addr=0x20) */
+#define PV88090_LDO2_EN 0x40
+#define PV88090_VLDO2_MASK 0x3F
+
+/* PV88090_REG_BUCK1_CONF1 (addr=0x19) */
+#define PV88090_BUCK1_ILIM_SHIFT 2
+#define PV88090_BUCK1_ILIM_MASK 0x7C
+#define PV88090_BUCK1_MODE_MASK 0x03
+
+/* PV88090_REG_BUCK2_CONF1 (addr=0x1c) */
+#define PV88090_BUCK2_ILIM_SHIFT 2
+#define PV88090_BUCK2_ILIM_MASK 0x0C
+#define PV88090_BUCK2_MODE_MASK 0x03
+
+/* PV88090_REG_BUCK3_CONF1 (addr=0x1e) */
+#define PV88090_BUCK3_ILIM_SHIFT 2
+#define PV88090_BUCK3_ILIM_MASK 0x0C
+#define PV88090_BUCK3_MODE_MASK 0x03
+
+#define PV88090_BUCK_MODE_SLEEP 0x00
+#define PV88090_BUCK_MODE_AUTO 0x01
+#define PV88090_BUCK_MODE_SYNC 0x02
+
+/* PV88090_REG_BUCK2_CONF2 (addr=0x58) */
+/* PV88090_REG_BUCK3_CONF2 (addr=0x5c) */
+#define PV88090_BUCK_VDAC_RANGE_SHIFT 7
+#define PV88090_BUCK_VDAC_RANGE_MASK 0x01
+
+#define PV88090_BUCK_VDAC_RANGE_1 0x00
+#define PV88090_BUCK_VDAC_RANGE_2 0x01
+
+/* PV88090_REG_BUCK_FOLD_RANGE (addr=0x61) */
+#define PV88090_BUCK_VRANGE_GAIN_SHIFT 3
+#define PV88090_BUCK_VRANGE_GAIN_MASK 0x01
+
+#define PV88090_BUCK_VRANGE_GAIN_1 0x00
+#define PV88090_BUCK_VRANGE_GAIN_2 0x01
+
+#endif /* __PV88090_REGISTERS_H__ */
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
new file mode 100644
index 000000000..34f3b9778
--- /dev/null
+++ b/drivers/regulator/pwm-regulator.c
@@ -0,0 +1,418 @@
+/*
+ * Regulator driver for PWM Regulators
+ *
+ * Copyright (C) 2014 - STMicroelectronics Inc.
+ *
+ * Author: Lee Jones <lee.jones@linaro.org>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pwm.h>
+#include <linux/gpio/consumer.h>
+
+struct pwm_continuous_reg_data {
+ unsigned int min_uV_dutycycle;
+ unsigned int max_uV_dutycycle;
+ unsigned int dutycycle_unit;
+};
+
+struct pwm_regulator_data {
+ /* Shared */
+ struct pwm_device *pwm;
+
+ /* Voltage table */
+ struct pwm_voltages *duty_cycle_table;
+
+ /* Continuous mode info */
+ struct pwm_continuous_reg_data continuous;
+
+ /* regulator descriptor */
+ struct regulator_desc desc;
+
+ /* Regulator ops */
+ struct regulator_ops ops;
+
+ int state;
+
+ /* Enable GPIO */
+ struct gpio_desc *enb_gpio;
+};
+
+struct pwm_voltages {
+ unsigned int uV;
+ unsigned int dutycycle;
+};
+
+/**
+ * Voltage table call-backs
+ */
+static void pwm_regulator_init_state(struct regulator_dev *rdev)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+ struct pwm_state pwm_state;
+ unsigned int dutycycle;
+ int i;
+
+ pwm_get_state(drvdata->pwm, &pwm_state);
+ dutycycle = pwm_get_relative_duty_cycle(&pwm_state, 100);
+
+ for (i = 0; i < rdev->desc->n_voltages; i++) {
+ if (dutycycle == drvdata->duty_cycle_table[i].dutycycle) {
+ drvdata->state = i;
+ return;
+ }
+ }
+}
+
+static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+
+ if (drvdata->state < 0)
+ pwm_regulator_init_state(rdev);
+
+ return drvdata->state;
+}
+
+static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+ struct pwm_state pstate;
+ int ret;
+
+ pwm_init_state(drvdata->pwm, &pstate);
+ pwm_set_relative_duty_cycle(&pstate,
+ drvdata->duty_cycle_table[selector].dutycycle, 100);
+
+ ret = pwm_apply_state(drvdata->pwm, &pstate);
+ if (ret) {
+ dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
+ return ret;
+ }
+
+ drvdata->state = selector;
+
+ return 0;
+}
+
+static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ return drvdata->duty_cycle_table[selector].uV;
+}
+
+static int pwm_regulator_enable(struct regulator_dev *dev)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+ gpiod_set_value_cansleep(drvdata->enb_gpio, 1);
+
+ return pwm_enable(drvdata->pwm);
+}
+
+static int pwm_regulator_disable(struct regulator_dev *dev)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+ pwm_disable(drvdata->pwm);
+
+ gpiod_set_value_cansleep(drvdata->enb_gpio, 0);
+
+ return 0;
+}
+
+static int pwm_regulator_is_enabled(struct regulator_dev *dev)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+ if (drvdata->enb_gpio && !gpiod_get_value_cansleep(drvdata->enb_gpio))
+ return false;
+
+ return pwm_is_enabled(drvdata->pwm);
+}
+
+static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+ unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
+ unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
+ unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
+ int min_uV = rdev->constraints->min_uV;
+ int max_uV = rdev->constraints->max_uV;
+ int diff_uV = max_uV - min_uV;
+ struct pwm_state pstate;
+ unsigned int diff_duty;
+ unsigned int voltage;
+
+ pwm_get_state(drvdata->pwm, &pstate);
+
+ voltage = pwm_get_relative_duty_cycle(&pstate, duty_unit);
+
+ /*
+ * The dutycycle for min_uV might be greater than the one for max_uV.
+ * This is happening when the user needs an inversed polarity, but the
+ * PWM device does not support inversing it in hardware.
+ */
+ if (max_uV_duty < min_uV_duty) {
+ voltage = min_uV_duty - voltage;
+ diff_duty = min_uV_duty - max_uV_duty;
+ } else {
+ voltage = voltage - min_uV_duty;
+ diff_duty = max_uV_duty - min_uV_duty;
+ }
+
+ voltage = DIV_ROUND_CLOSEST_ULL((u64)voltage * diff_uV, diff_duty);
+
+ return voltage + min_uV;
+}
+
+static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
+ int req_min_uV, int req_max_uV,
+ unsigned int *selector)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+ unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
+ unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
+ unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
+ int min_uV = rdev->constraints->min_uV;
+ int max_uV = rdev->constraints->max_uV;
+ int diff_uV = max_uV - min_uV;
+ struct pwm_state pstate;
+ unsigned int diff_duty;
+ unsigned int dutycycle;
+ int ret;
+
+ pwm_init_state(drvdata->pwm, &pstate);
+
+ /*
+ * The dutycycle for min_uV might be greater than the one for max_uV.
+ * This is happening when the user needs an inversed polarity, but the
+ * PWM device does not support inversing it in hardware.
+ */
+ if (max_uV_duty < min_uV_duty)
+ diff_duty = min_uV_duty - max_uV_duty;
+ else
+ diff_duty = max_uV_duty - min_uV_duty;
+
+ dutycycle = DIV_ROUND_CLOSEST_ULL((u64)(req_min_uV - min_uV) *
+ diff_duty,
+ diff_uV);
+
+ if (max_uV_duty < min_uV_duty)
+ dutycycle = min_uV_duty - dutycycle;
+ else
+ dutycycle = min_uV_duty + dutycycle;
+
+ pwm_set_relative_duty_cycle(&pstate, dutycycle, duty_unit);
+
+ ret = pwm_apply_state(drvdata->pwm, &pstate);
+ if (ret) {
+ dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct regulator_ops pwm_regulator_voltage_table_ops = {
+ .set_voltage_sel = pwm_regulator_set_voltage_sel,
+ .get_voltage_sel = pwm_regulator_get_voltage_sel,
+ .list_voltage = pwm_regulator_list_voltage,
+ .map_voltage = regulator_map_voltage_iterate,
+ .enable = pwm_regulator_enable,
+ .disable = pwm_regulator_disable,
+ .is_enabled = pwm_regulator_is_enabled,
+};
+
+static struct regulator_ops pwm_regulator_voltage_continuous_ops = {
+ .get_voltage = pwm_regulator_get_voltage,
+ .set_voltage = pwm_regulator_set_voltage,
+ .enable = pwm_regulator_enable,
+ .disable = pwm_regulator_disable,
+ .is_enabled = pwm_regulator_is_enabled,
+};
+
+static struct regulator_desc pwm_regulator_desc = {
+ .name = "pwm-regulator",
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .supply_name = "pwm",
+};
+
+static int pwm_regulator_init_table(struct platform_device *pdev,
+ struct pwm_regulator_data *drvdata)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pwm_voltages *duty_cycle_table;
+ unsigned int length = 0;
+ int ret;
+
+ of_find_property(np, "voltage-table", &length);
+
+ if ((length < sizeof(*duty_cycle_table)) ||
+ (length % sizeof(*duty_cycle_table))) {
+ dev_err(&pdev->dev, "voltage-table length(%d) is invalid\n",
+ length);
+ return -EINVAL;
+ }
+
+ duty_cycle_table = devm_kzalloc(&pdev->dev, length, GFP_KERNEL);
+ if (!duty_cycle_table)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "voltage-table",
+ (u32 *)duty_cycle_table,
+ length / sizeof(u32));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read voltage-table: %d\n", ret);
+ return ret;
+ }
+
+ drvdata->state = -ENOTRECOVERABLE;
+ drvdata->duty_cycle_table = duty_cycle_table;
+ memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops,
+ sizeof(drvdata->ops));
+ drvdata->desc.ops = &drvdata->ops;
+ drvdata->desc.n_voltages = length / sizeof(*duty_cycle_table);
+
+ return 0;
+}
+
+static int pwm_regulator_init_continuous(struct platform_device *pdev,
+ struct pwm_regulator_data *drvdata)
+{
+ u32 dutycycle_range[2] = { 0, 100 };
+ u32 dutycycle_unit = 100;
+
+ memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
+ sizeof(drvdata->ops));
+ drvdata->desc.ops = &drvdata->ops;
+ drvdata->desc.continuous_voltage_range = true;
+
+ of_property_read_u32_array(pdev->dev.of_node,
+ "pwm-dutycycle-range",
+ dutycycle_range, 2);
+ of_property_read_u32(pdev->dev.of_node, "pwm-dutycycle-unit",
+ &dutycycle_unit);
+
+ if (dutycycle_range[0] > dutycycle_unit ||
+ dutycycle_range[1] > dutycycle_unit)
+ return -EINVAL;
+
+ drvdata->continuous.dutycycle_unit = dutycycle_unit;
+ drvdata->continuous.min_uV_dutycycle = dutycycle_range[0];
+ drvdata->continuous.max_uV_dutycycle = dutycycle_range[1];
+
+ return 0;
+}
+
+static int pwm_regulator_probe(struct platform_device *pdev)
+{
+ const struct regulator_init_data *init_data;
+ struct pwm_regulator_data *drvdata;
+ struct regulator_dev *regulator;
+ struct regulator_config config = { };
+ struct device_node *np = pdev->dev.of_node;
+ enum gpiod_flags gpio_flags;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Device Tree node missing\n");
+ return -EINVAL;
+ }
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(drvdata->desc));
+
+ if (of_find_property(np, "voltage-table", NULL))
+ ret = pwm_regulator_init_table(pdev, drvdata);
+ else
+ ret = pwm_regulator_init_continuous(pdev, drvdata);
+ if (ret)
+ return ret;
+
+ init_data = of_get_regulator_init_data(&pdev->dev, np,
+ &drvdata->desc);
+ if (!init_data)
+ return -ENOMEM;
+
+ config.of_node = np;
+ config.dev = &pdev->dev;
+ config.driver_data = drvdata;
+ config.init_data = init_data;
+
+ drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
+ if (IS_ERR(drvdata->pwm)) {
+ ret = PTR_ERR(drvdata->pwm);
+ dev_err(&pdev->dev, "Failed to get PWM: %d\n", ret);
+ return ret;
+ }
+
+ if (init_data->constraints.boot_on || init_data->constraints.always_on)
+ gpio_flags = GPIOD_OUT_HIGH;
+ else
+ gpio_flags = GPIOD_OUT_LOW;
+ drvdata->enb_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
+ gpio_flags);
+ if (IS_ERR(drvdata->enb_gpio)) {
+ ret = PTR_ERR(drvdata->enb_gpio);
+ dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n", ret);
+ return ret;
+ }
+
+ ret = pwm_adjust_config(drvdata->pwm);
+ if (ret)
+ return ret;
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &drvdata->desc, &config);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+ dev_err(&pdev->dev, "Failed to register regulator %s: %d\n",
+ drvdata->desc.name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id pwm_of_match[] = {
+ { .compatible = "pwm-regulator" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pwm_of_match);
+
+static struct platform_driver pwm_regulator_driver = {
+ .driver = {
+ .name = "pwm-regulator",
+ .of_match_table = of_match_ptr(pwm_of_match),
+ },
+ .probe = pwm_regulator_probe,
+};
+
+module_platform_driver(pwm_regulator_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
+MODULE_DESCRIPTION("PWM Regulator Driver");
+MODULE_ALIAS("platform:pwm-regulator");
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
new file mode 100644
index 000000000..9f27daebd
--- /dev/null
+++ b/drivers/regulator/qcom-rpmh-regulator.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/**
+ * enum rpmh_regulator_type - supported RPMh accelerator types
+ * %VRM: RPMh VRM accelerator which supports voting on enable, voltage,
+ * and mode of LDO, SMPS, and BOB type PMIC regulators.
+ * %XOB: RPMh XOB accelerator which supports voting on the enable state
+ * of PMIC regulators.
+ */
+enum rpmh_regulator_type {
+ VRM,
+ XOB,
+};
+
+#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0
+#define RPMH_REGULATOR_REG_ENABLE 0x4
+#define RPMH_REGULATOR_REG_VRM_MODE 0x8
+
+#define PMIC4_LDO_MODE_RETENTION 4
+#define PMIC4_LDO_MODE_LPM 5
+#define PMIC4_LDO_MODE_HPM 7
+
+#define PMIC4_SMPS_MODE_RETENTION 4
+#define PMIC4_SMPS_MODE_PFM 5
+#define PMIC4_SMPS_MODE_AUTO 6
+#define PMIC4_SMPS_MODE_PWM 7
+
+#define PMIC4_BOB_MODE_PASS 0
+#define PMIC4_BOB_MODE_PFM 1
+#define PMIC4_BOB_MODE_AUTO 2
+#define PMIC4_BOB_MODE_PWM 3
+
+/**
+ * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
+ * @regulator_type: RPMh accelerator type used to manage this
+ * regulator
+ * @ops: Pointer to regulator ops callback structure
+ * @voltage_range: The single range of voltages supported by this
+ * PMIC regulator type
+ * @n_voltages: The number of unique voltage set points defined
+ * by voltage_range
+ * @hpm_min_load_uA: Minimum load current in microamps that requires
+ * high power mode (HPM) operation. This is used
+ * for LDO hardware type regulators only.
+ * @pmic_mode_map: Array indexed by regulator framework mode
+ * containing PMIC hardware modes. Must be large
+ * enough to index all framework modes supported
+ * by this regulator hardware type.
+ * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined
+ * in device tree to a regulator framework mode
+ */
+struct rpmh_vreg_hw_data {
+ enum rpmh_regulator_type regulator_type;
+ const struct regulator_ops *ops;
+ const struct regulator_linear_range voltage_range;
+ int n_voltages;
+ int hpm_min_load_uA;
+ const int *pmic_mode_map;
+ unsigned int (*of_map_mode)(unsigned int mode);
+};
+
+/**
+ * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a
+ * single regulator device
+ * @dev: Device pointer for the top-level PMIC RPMh
+ * regulator parent device. This is used as a
+ * handle in RPMh write requests.
+ * @addr: Base address of the regulator resource within
+ * an RPMh accelerator
+ * @rdesc: Regulator descriptor
+ * @hw_data: PMIC regulator configuration data for this RPMh
+ * regulator
+ * @always_wait_for_ack: Boolean flag indicating if a request must always
+ * wait for an ACK from RPMh before continuing even
+ * if it corresponds to a strictly lower power
+ * state (e.g. enabled --> disabled).
+ * @enabled: Flag indicating if the regulator is enabled or
+ * not
+ * @bypassed: Boolean indicating if the regulator is in
+ * bypass (pass-through) mode or not. This is
+ * only used by BOB rpmh-regulator resources.
+ * @voltage_selector: Selector used for get_voltage_sel() and
+ * set_voltage_sel() callbacks
+ * @mode: RPMh VRM regulator current framework mode
+ */
+struct rpmh_vreg {
+ struct device *dev;
+ u32 addr;
+ struct regulator_desc rdesc;
+ const struct rpmh_vreg_hw_data *hw_data;
+ bool always_wait_for_ack;
+
+ int enabled;
+ bool bypassed;
+ int voltage_selector;
+ unsigned int mode;
+};
+
+/**
+ * struct rpmh_vreg_init_data - initialization data for an RPMh regulator
+ * @name: Name for the regulator which also corresponds
+ * to the device tree subnode name of the regulator
+ * @resource_name: RPMh regulator resource name format string.
+ * This must include exactly one field: '%s' which
+ * is filled at run-time with the PMIC ID provided
+ * by device tree property qcom,pmic-id. Example:
+ * "ldo%s1" for RPMh resource "ldoa1".
+ * @supply_name: Parent supply regulator name
+ * @hw_data: Configuration data for this PMIC regulator type
+ */
+struct rpmh_vreg_init_data {
+ const char *name;
+ const char *resource_name;
+ const char *supply_name;
+ const struct rpmh_vreg_hw_data *hw_data;
+};
+
+/**
+ * rpmh_regulator_send_request() - send the request to RPMh
+ * @vreg: Pointer to the RPMh regulator
+ * @cmd: Pointer to the RPMh command to send
+ * @wait_for_ack: Boolean indicating if execution must wait until the
+ * request has been acknowledged as complete
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_send_request(struct rpmh_vreg *vreg,
+ struct tcs_cmd *cmd, bool wait_for_ack)
+{
+ int ret;
+
+ if (wait_for_ack || vreg->always_wait_for_ack)
+ ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, 1);
+ else
+ ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd,
+ 1);
+
+ return ret;
+}
+
+static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector, bool wait_for_ack)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
+ };
+ int ret;
+
+ /* VRM voltage control register is set with voltage in millivolts. */
+ cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev,
+ selector), 1000);
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, wait_for_ack);
+ if (!ret)
+ vreg->voltage_selector = selector;
+
+ return ret;
+}
+
+static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ if (vreg->enabled == -EINVAL) {
+ /*
+ * Cache the voltage and send it later when the regulator is
+ * enabled or disabled.
+ */
+ vreg->voltage_selector = selector;
+ return 0;
+ }
+
+ return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector,
+ selector > vreg->voltage_selector);
+}
+
+static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->voltage_selector;
+}
+
+static int rpmh_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->enabled;
+}
+
+static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev,
+ bool enable)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+ .data = enable,
+ };
+ int ret;
+
+ if (vreg->enabled == -EINVAL &&
+ vreg->voltage_selector != -ENOTRECOVERABLE) {
+ ret = _rpmh_regulator_vrm_set_voltage_sel(rdev,
+ vreg->voltage_selector, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, enable);
+ if (!ret)
+ vreg->enabled = enable;
+
+ return ret;
+}
+
+static int rpmh_regulator_enable(struct regulator_dev *rdev)
+{
+ return rpmh_regulator_set_enable_state(rdev, true);
+}
+
+static int rpmh_regulator_disable(struct regulator_dev *rdev)
+{
+ return rpmh_regulator_set_enable_state(rdev, false);
+}
+
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+ unsigned int mode, bool bypassed)
+{
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+ };
+ int pmic_mode;
+
+ if (mode > REGULATOR_MODE_STANDBY)
+ return -EINVAL;
+
+ pmic_mode = vreg->hw_data->pmic_mode_map[mode];
+ if (pmic_mode < 0)
+ return pmic_mode;
+
+ if (bypassed)
+ cmd.data = PMIC4_BOB_MODE_PASS;
+ else
+ cmd.data = pmic_mode;
+
+ return rpmh_regulator_send_request(vreg, &cmd, true);
+}
+
+static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (mode == vreg->mode)
+ return 0;
+
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
+ if (!ret)
+ vreg->mode = mode;
+
+ return ret;
+}
+
+static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->mode;
+}
+
+/**
+ * rpmh_regulator_vrm_set_load() - set the regulator mode based upon the load
+ * current requested
+ * @rdev: Regulator device pointer for the rpmh-regulator
+ * @load_uA: Aggregated load current in microamps
+ *
+ * This function is used in the regulator_ops for VRM type RPMh regulator
+ * devices.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode;
+
+ if (load_uA >= vreg->hw_data->hpm_min_load_uA)
+ mode = REGULATOR_MODE_NORMAL;
+ else
+ mode = REGULATOR_MODE_IDLE;
+
+ return rpmh_regulator_vrm_set_mode(rdev, mode);
+}
+
+static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev,
+ bool enable)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (vreg->bypassed == enable)
+ return 0;
+
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable);
+ if (!ret)
+ vreg->bypassed = enable;
+
+ return ret;
+}
+
+static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev,
+ bool *enable)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ *enable = vreg->bypassed;
+
+ return 0;
+}
+
+static const struct regulator_ops rpmh_regulator_vrm_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+ .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel,
+ .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_mode = rpmh_regulator_vrm_set_mode,
+ .get_mode = rpmh_regulator_vrm_get_mode,
+};
+
+static const struct regulator_ops rpmh_regulator_vrm_drms_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+ .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel,
+ .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_mode = rpmh_regulator_vrm_set_mode,
+ .get_mode = rpmh_regulator_vrm_get_mode,
+ .set_load = rpmh_regulator_vrm_set_load,
+};
+
+static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+ .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel,
+ .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_mode = rpmh_regulator_vrm_set_mode,
+ .get_mode = rpmh_regulator_vrm_get_mode,
+ .set_bypass = rpmh_regulator_vrm_set_bypass,
+ .get_bypass = rpmh_regulator_vrm_get_bypass,
+};
+
+static const struct regulator_ops rpmh_regulator_xob_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+};
+
+/**
+ * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator
+ * vreg: Pointer to the individual rpmh-regulator resource
+ * dev: Pointer to the top level rpmh-regulator PMIC device
+ * node: Pointer to the individual rpmh-regulator resource
+ * device node
+ * pmic_id: String used to identify the top level rpmh-regulator
+ * PMIC device on the board
+ * pmic_rpmh_data: Pointer to a null-terminated array of rpmh-regulator
+ * resources defined for the top level PMIC device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
+ struct device_node *node, const char *pmic_id,
+ const struct rpmh_vreg_init_data *pmic_rpmh_data)
+{
+ struct regulator_config reg_config = {};
+ char rpmh_resource_name[20] = "";
+ const struct rpmh_vreg_init_data *rpmh_data;
+ struct regulator_init_data *init_data;
+ struct regulator_dev *rdev;
+ int ret;
+
+ vreg->dev = dev;
+
+ for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++)
+ if (!strcmp(rpmh_data->name, node->name))
+ break;
+
+ if (!rpmh_data->name) {
+ dev_err(dev, "Unknown regulator %s\n", node->name);
+ return -EINVAL;
+ }
+
+ scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name),
+ rpmh_data->resource_name, pmic_id);
+
+ vreg->addr = cmd_db_read_addr(rpmh_resource_name);
+ if (!vreg->addr) {
+ dev_err(dev, "%s: could not find RPMh address for resource %s\n",
+ node->name, rpmh_resource_name);
+ return -ENODEV;
+ }
+
+ vreg->rdesc.name = rpmh_data->name;
+ vreg->rdesc.supply_name = rpmh_data->supply_name;
+ vreg->hw_data = rpmh_data->hw_data;
+
+ vreg->enabled = -EINVAL;
+ vreg->voltage_selector = -ENOTRECOVERABLE;
+ vreg->mode = REGULATOR_MODE_INVALID;
+
+ if (rpmh_data->hw_data->n_voltages) {
+ vreg->rdesc.linear_ranges = &rpmh_data->hw_data->voltage_range;
+ vreg->rdesc.n_linear_ranges = 1;
+ vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages;
+ }
+
+ vreg->always_wait_for_ack = of_property_read_bool(node,
+ "qcom,always-wait-for-ack");
+
+ vreg->rdesc.owner = THIS_MODULE;
+ vreg->rdesc.type = REGULATOR_VOLTAGE;
+ vreg->rdesc.ops = vreg->hw_data->ops;
+ vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode;
+
+ init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc);
+ if (!init_data)
+ return -ENOMEM;
+
+ if (rpmh_data->hw_data->regulator_type == XOB &&
+ init_data->constraints.min_uV &&
+ init_data->constraints.min_uV == init_data->constraints.max_uV) {
+ vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
+ vreg->rdesc.n_voltages = 1;
+ }
+
+ reg_config.dev = dev;
+ reg_config.init_data = init_data;
+ reg_config.of_node = node;
+ reg_config.driver_data = vreg;
+
+ rdev = devm_regulator_register(dev, &vreg->rdesc, &reg_config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "%s: devm_regulator_register() failed, ret=%d\n",
+ node->name, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s regulator registered for RPMh resource %s @ 0x%05X\n",
+ node->name, rpmh_resource_name, vreg->addr);
+
+ return 0;
+}
+
+static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+ [REGULATOR_MODE_INVALID] = -EINVAL,
+ [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
+ [REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM,
+ [REGULATOR_MODE_NORMAL] = PMIC4_LDO_MODE_HPM,
+ [REGULATOR_MODE_FAST] = -EINVAL,
+};
+
+static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode)
+{
+ unsigned int mode;
+
+ switch (rpmh_mode) {
+ case RPMH_REGULATOR_MODE_HPM:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case RPMH_REGULATOR_MODE_LPM:
+ mode = REGULATOR_MODE_IDLE;
+ break;
+ case RPMH_REGULATOR_MODE_RET:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ default:
+ mode = REGULATOR_MODE_INVALID;
+ }
+
+ return mode;
+}
+
+static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
+ [REGULATOR_MODE_INVALID] = -EINVAL,
+ [REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION,
+ [REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM,
+ [REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO,
+ [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM,
+};
+
+static unsigned int
+rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode)
+{
+ unsigned int mode;
+
+ switch (rpmh_mode) {
+ case RPMH_REGULATOR_MODE_HPM:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case RPMH_REGULATOR_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case RPMH_REGULATOR_MODE_LPM:
+ mode = REGULATOR_MODE_IDLE;
+ break;
+ case RPMH_REGULATOR_MODE_RET:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ default:
+ mode = REGULATOR_MODE_INVALID;
+ }
+
+ return mode;
+}
+
+static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = {
+ [REGULATOR_MODE_INVALID] = -EINVAL,
+ [REGULATOR_MODE_STANDBY] = -EINVAL,
+ [REGULATOR_MODE_IDLE] = PMIC4_BOB_MODE_PFM,
+ [REGULATOR_MODE_NORMAL] = PMIC4_BOB_MODE_AUTO,
+ [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM,
+};
+
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode)
+{
+ unsigned int mode;
+
+ switch (rpmh_mode) {
+ case RPMH_REGULATOR_MODE_HPM:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case RPMH_REGULATOR_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case RPMH_REGULATOR_MODE_LPM:
+ mode = REGULATOR_MODE_IDLE;
+ break;
+ default:
+ mode = REGULATOR_MODE_INVALID;
+ }
+
+ return mode;
+}
+
+static const struct rpmh_vreg_hw_data pmic4_pldo = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_drms_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+ .n_voltages = 256,
+ .hpm_min_load_uA = 10000,
+ .pmic_mode_map = pmic_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_pldo_lv = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_drms_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+ .n_voltages = 128,
+ .hpm_min_load_uA = 10000,
+ .pmic_mode_map = pmic_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_nldo = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_drms_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+ .n_voltages = 128,
+ .hpm_min_load_uA = 30000,
+ .pmic_mode_map = pmic_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
+ .n_voltages = 216,
+ .pmic_mode_map = pmic_mode_map_pmic4_smps,
+ .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
+ .n_voltages = 259,
+ .pmic_mode_map = pmic_mode_map_pmic4_smps,
+ .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_bob = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_bypass_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+ .n_voltages = 84,
+ .pmic_mode_map = pmic_mode_map_pmic4_bob,
+ .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_lvs = {
+ .regulator_type = XOB,
+ .ops = &rpmh_regulator_xob_ops,
+ /* LVS hardware does not support voltage or mode configuration. */
+};
+
+#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \
+{ \
+ .name = _name, \
+ .resource_name = _resource_name, \
+ .hw_data = _hw_data, \
+ .supply_name = _supply_name, \
+}
+
+static const struct rpmh_vreg_init_data pm8998_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic4_hfsmps3, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"),
+ RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"),
+ RPMH_VREG("smps6", "smp%s6", &pmic4_ftsmps426, "vdd-s6"),
+ RPMH_VREG("smps7", "smp%s7", &pmic4_ftsmps426, "vdd-s7"),
+ RPMH_VREG("smps8", "smp%s8", &pmic4_ftsmps426, "vdd-s8"),
+ RPMH_VREG("smps9", "smp%s9", &pmic4_ftsmps426, "vdd-s9"),
+ RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd-s10"),
+ RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd-s11"),
+ RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd-s12"),
+ RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd-s13"),
+ RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l27"),
+ RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l8-l17"),
+ RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l3-l11"),
+ RPMH_VREG("ldo4", "ldo%s4", &pmic4_nldo, "vdd-l4-l5"),
+ RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l4-l5"),
+ RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l6"),
+ RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo8", "ldo%s8", &pmic4_nldo, "vdd-l2-l8-l17"),
+ RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo, "vdd-l9"),
+ RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo, "vdd-l10-l23-l25"),
+ RPMH_VREG("ldo11", "ldo%s11", &pmic4_nldo, "vdd-l3-l11"),
+ RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo, "vdd-l13-l19-l21"),
+ RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l16-l28"),
+ RPMH_VREG("ldo17", "ldo%s17", &pmic4_nldo, "vdd-l2-l8-l17"),
+ RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l18-l22"),
+ RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l13-l19-l21"),
+ RPMH_VREG("ldo20", "ldo%s20", &pmic4_pldo, "vdd-l20-l24"),
+ RPMH_VREG("ldo21", "ldo%s21", &pmic4_pldo, "vdd-l13-l19-l21"),
+ RPMH_VREG("ldo22", "ldo%s22", &pmic4_pldo, "vdd-l18-l22"),
+ RPMH_VREG("ldo23", "ldo%s23", &pmic4_pldo, "vdd-l10-l23-l25"),
+ RPMH_VREG("ldo24", "ldo%s24", &pmic4_pldo, "vdd-l20-l24"),
+ RPMH_VREG("ldo25", "ldo%s25", &pmic4_pldo, "vdd-l10-l23-l25"),
+ RPMH_VREG("ldo26", "ldo%s26", &pmic4_nldo, "vdd-l26"),
+ RPMH_VREG("ldo27", "ldo%s27", &pmic4_nldo, "vdd-l1-l27"),
+ RPMH_VREG("ldo28", "ldo%s28", &pmic4_pldo, "vdd-l16-l28"),
+ RPMH_VREG("lvs1", "vs%s1", &pmic4_lvs, "vin-lvs-1-2"),
+ RPMH_VREG("lvs2", "vs%s2", &pmic4_lvs, "vin-lvs-1-2"),
+ {},
+};
+
+static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = {
+ RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"),
+ {},
+};
+
+static const struct rpmh_vreg_init_data pm8005_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic4_ftsmps426, "vdd-s4"),
+ {},
+};
+
+static int rpmh_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct rpmh_vreg_init_data *vreg_data;
+ struct device_node *node;
+ struct rpmh_vreg *vreg;
+ const char *pmic_id;
+ int ret;
+
+ vreg_data = of_device_get_match_data(dev);
+ if (!vreg_data)
+ return -ENODEV;
+
+ ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id);
+ if (ret < 0) {
+ dev_err(dev, "qcom,pmic-id missing in DT node\n");
+ return ret;
+ }
+
+ for_each_available_child_of_node(dev->of_node, node) {
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ of_node_put(node);
+ return -ENOMEM;
+ }
+
+ ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id,
+ vreg_data);
+ if (ret < 0) {
+ of_node_put(node);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rpmh_regulator_match_table[] = {
+ {
+ .compatible = "qcom,pm8998-rpmh-regulators",
+ .data = pm8998_vreg_data,
+ },
+ {
+ .compatible = "qcom,pmi8998-rpmh-regulators",
+ .data = pmi8998_vreg_data,
+ },
+ {
+ .compatible = "qcom,pm8005-rpmh-regulators",
+ .data = pm8005_vreg_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
+
+static struct platform_driver rpmh_regulator_driver = {
+ .driver = {
+ .name = "qcom-rpmh-regulator",
+ .of_match_table = of_match_ptr(rpmh_regulator_match_table),
+ },
+ .probe = rpmh_regulator_probe,
+};
+module_platform_driver(rpmh_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm RPMh regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c
new file mode 100644
index 000000000..88dc0b0f0
--- /dev/null
+++ b/drivers/regulator/qcom_rpm-regulator.c
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, The Linux Foundation. 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 version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/qcom_rpm.h>
+
+#include <dt-bindings/mfd/qcom-rpm.h>
+
+#define MAX_REQUEST_LEN 2
+
+struct request_member {
+ int word;
+ unsigned int mask;
+ int shift;
+};
+
+struct rpm_reg_parts {
+ struct request_member mV; /* used if voltage is in mV */
+ struct request_member uV; /* used if voltage is in uV */
+ struct request_member ip; /* peak current in mA */
+ struct request_member pd; /* pull down enable */
+ struct request_member ia; /* average current in mA */
+ struct request_member fm; /* force mode */
+ struct request_member pm; /* power mode */
+ struct request_member pc; /* pin control */
+ struct request_member pf; /* pin function */
+ struct request_member enable_state; /* NCP and switch */
+ struct request_member comp_mode; /* NCP */
+ struct request_member freq; /* frequency: NCP and SMPS */
+ struct request_member freq_clk_src; /* clock source: SMPS */
+ struct request_member hpm; /* switch: control OCP and SS */
+ int request_len;
+};
+
+#define FORCE_MODE_IS_2_BITS(reg) \
+ (((reg)->parts->fm.mask >> (reg)->parts->fm.shift) == 3)
+
+struct qcom_rpm_reg {
+ struct qcom_rpm *rpm;
+
+ struct mutex lock;
+ struct device *dev;
+ struct regulator_desc desc;
+ const struct rpm_reg_parts *parts;
+
+ int resource;
+ u32 val[MAX_REQUEST_LEN];
+
+ int uV;
+ int is_enabled;
+
+ bool supports_force_mode_auto;
+ bool supports_force_mode_bypass;
+};
+
+static const struct rpm_reg_parts rpm8660_ldo_parts = {
+ .request_len = 2,
+ .mV = { 0, 0x00000FFF, 0 },
+ .ip = { 0, 0x00FFF000, 12 },
+ .fm = { 0, 0x03000000, 24 },
+ .pc = { 0, 0x3C000000, 26 },
+ .pf = { 0, 0xC0000000, 30 },
+ .pd = { 1, 0x00000001, 0 },
+ .ia = { 1, 0x00001FFE, 1 },
+};
+
+static const struct rpm_reg_parts rpm8660_smps_parts = {
+ .request_len = 2,
+ .mV = { 0, 0x00000FFF, 0 },
+ .ip = { 0, 0x00FFF000, 12 },
+ .fm = { 0, 0x03000000, 24 },
+ .pc = { 0, 0x3C000000, 26 },
+ .pf = { 0, 0xC0000000, 30 },
+ .pd = { 1, 0x00000001, 0 },
+ .ia = { 1, 0x00001FFE, 1 },
+ .freq = { 1, 0x001FE000, 13 },
+ .freq_clk_src = { 1, 0x00600000, 21 },
+};
+
+static const struct rpm_reg_parts rpm8660_switch_parts = {
+ .request_len = 1,
+ .enable_state = { 0, 0x00000001, 0 },
+ .pd = { 0, 0x00000002, 1 },
+ .pc = { 0, 0x0000003C, 2 },
+ .pf = { 0, 0x000000C0, 6 },
+ .hpm = { 0, 0x00000300, 8 },
+};
+
+static const struct rpm_reg_parts rpm8660_ncp_parts = {
+ .request_len = 1,
+ .mV = { 0, 0x00000FFF, 0 },
+ .enable_state = { 0, 0x00001000, 12 },
+ .comp_mode = { 0, 0x00002000, 13 },
+ .freq = { 0, 0x003FC000, 14 },
+};
+
+static const struct rpm_reg_parts rpm8960_ldo_parts = {
+ .request_len = 2,
+ .uV = { 0, 0x007FFFFF, 0 },
+ .pd = { 0, 0x00800000, 23 },
+ .pc = { 0, 0x0F000000, 24 },
+ .pf = { 0, 0xF0000000, 28 },
+ .ip = { 1, 0x000003FF, 0 },
+ .ia = { 1, 0x000FFC00, 10 },
+ .fm = { 1, 0x00700000, 20 },
+};
+
+static const struct rpm_reg_parts rpm8960_smps_parts = {
+ .request_len = 2,
+ .uV = { 0, 0x007FFFFF, 0 },
+ .pd = { 0, 0x00800000, 23 },
+ .pc = { 0, 0x0F000000, 24 },
+ .pf = { 0, 0xF0000000, 28 },
+ .ip = { 1, 0x000003FF, 0 },
+ .ia = { 1, 0x000FFC00, 10 },
+ .fm = { 1, 0x00700000, 20 },
+ .pm = { 1, 0x00800000, 23 },
+ .freq = { 1, 0x1F000000, 24 },
+ .freq_clk_src = { 1, 0x60000000, 29 },
+};
+
+static const struct rpm_reg_parts rpm8960_switch_parts = {
+ .request_len = 1,
+ .enable_state = { 0, 0x00000001, 0 },
+ .pd = { 0, 0x00000002, 1 },
+ .pc = { 0, 0x0000003C, 2 },
+ .pf = { 0, 0x000003C0, 6 },
+ .hpm = { 0, 0x00000C00, 10 },
+};
+
+static const struct rpm_reg_parts rpm8960_ncp_parts = {
+ .request_len = 1,
+ .uV = { 0, 0x007FFFFF, 0 },
+ .enable_state = { 0, 0x00800000, 23 },
+ .comp_mode = { 0, 0x01000000, 24 },
+ .freq = { 0, 0x3E000000, 25 },
+};
+
+/*
+ * Physically available PMIC regulator voltage ranges
+ */
+static const struct regulator_linear_range pldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 59, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 60, 123, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 124, 160, 50000),
+};
+
+static const struct regulator_linear_range nldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+};
+
+static const struct regulator_linear_range nldo1200_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 59, 6250),
+ REGULATOR_LINEAR_RANGE( 750000, 60, 123, 12500),
+};
+
+static const struct regulator_linear_range smps_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
+ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
+};
+
+static const struct regulator_linear_range ftsmps_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 350000, 0, 6, 50000),
+ REGULATOR_LINEAR_RANGE( 700000, 7, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000),
+};
+
+static const struct regulator_linear_range smb208_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
+ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000),
+};
+
+static const struct regulator_linear_range ncp_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000),
+};
+
+static int rpm_reg_write(struct qcom_rpm_reg *vreg,
+ const struct request_member *req,
+ const int value)
+{
+ if (WARN_ON((value << req->shift) & ~req->mask))
+ return -EINVAL;
+
+ vreg->val[req->word] &= ~req->mask;
+ vreg->val[req->word] |= value << req->shift;
+
+ return qcom_rpm_write(vreg->rpm,
+ QCOM_RPM_ACTIVE_STATE,
+ vreg->resource,
+ vreg->val,
+ vreg->parts->request_len);
+}
+
+static int rpm_reg_set_mV_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->mV;
+ int ret = 0;
+ int uV;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ uV = regulator_list_voltage_linear_range(rdev, selector);
+ if (uV < 0)
+ return uV;
+
+ mutex_lock(&vreg->lock);
+ if (vreg->is_enabled)
+ ret = rpm_reg_write(vreg, req, uV / 1000);
+
+ if (!ret)
+ vreg->uV = uV;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_set_uV_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->uV;
+ int ret = 0;
+ int uV;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ uV = regulator_list_voltage_linear_range(rdev, selector);
+ if (uV < 0)
+ return uV;
+
+ mutex_lock(&vreg->lock);
+ if (vreg->is_enabled)
+ ret = rpm_reg_write(vreg, req, uV);
+
+ if (!ret)
+ vreg->uV = uV;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_get_voltage(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->uV;
+}
+
+static int rpm_reg_mV_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->mV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
+ if (!ret)
+ vreg->is_enabled = 1;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_uV_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->uV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, vreg->uV);
+ if (!ret)
+ vreg->is_enabled = 1;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_switch_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->enable_state;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 1);
+ if (!ret)
+ vreg->is_enabled = 1;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_mV_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->mV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 0);
+ if (!ret)
+ vreg->is_enabled = 0;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_uV_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->uV;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 0);
+ if (!ret)
+ vreg->is_enabled = 0;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_switch_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->enable_state;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, 0);
+ if (!ret)
+ vreg->is_enabled = 0;
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static int rpm_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->is_enabled;
+}
+
+static int rpm_reg_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ const struct rpm_reg_parts *parts = vreg->parts;
+ const struct request_member *req = &parts->ia;
+ int load_mA = load_uA / 1000;
+ int max_mA = req->mask >> req->shift;
+ int ret;
+
+ if (req->mask == 0)
+ return -EINVAL;
+
+ if (load_mA > max_mA)
+ load_mA = max_mA;
+
+ mutex_lock(&vreg->lock);
+ ret = rpm_reg_write(vreg, req, load_mA);
+ mutex_unlock(&vreg->lock);
+
+ return ret;
+}
+
+static struct regulator_ops uV_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+
+ .set_voltage_sel = rpm_reg_set_uV_sel,
+ .get_voltage = rpm_reg_get_voltage,
+
+ .enable = rpm_reg_uV_enable,
+ .disable = rpm_reg_uV_disable,
+ .is_enabled = rpm_reg_is_enabled,
+
+ .set_load = rpm_reg_set_load,
+};
+
+static struct regulator_ops mV_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+
+ .set_voltage_sel = rpm_reg_set_mV_sel,
+ .get_voltage = rpm_reg_get_voltage,
+
+ .enable = rpm_reg_mV_enable,
+ .disable = rpm_reg_mV_disable,
+ .is_enabled = rpm_reg_is_enabled,
+
+ .set_load = rpm_reg_set_load,
+};
+
+static struct regulator_ops switch_ops = {
+ .enable = rpm_reg_switch_enable,
+ .disable = rpm_reg_switch_disable,
+ .is_enabled = rpm_reg_is_enabled,
+};
+
+/*
+ * PM8018 regulators
+ */
+static const struct qcom_rpm_reg pm8018_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8018_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8018_smps = {
+ .desc.linear_ranges = smps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+ .desc.n_voltages = 154,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8018_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8960_switch_parts,
+};
+
+/*
+ * PM8058 regulators
+ */
+static const struct qcom_rpm_reg pm8058_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8058_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8058_smps = {
+ .desc.linear_ranges = smps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+ .desc.n_voltages = 154,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_smps_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8058_ncp = {
+ .desc.linear_ranges = ncp_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
+ .desc.n_voltages = 32,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ncp_parts,
+};
+
+static const struct qcom_rpm_reg pm8058_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8660_switch_parts,
+};
+
+/*
+ * PM8901 regulators
+ */
+static const struct qcom_rpm_reg pm8901_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8901_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8901_ftsmps = {
+ .desc.linear_ranges = ftsmps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
+ .desc.n_voltages = 101,
+ .desc.ops = &mV_ops,
+ .parts = &rpm8660_smps_parts,
+ .supports_force_mode_auto = true,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8901_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8660_switch_parts,
+};
+
+/*
+ * PM8921 regulators
+ */
+static const struct qcom_rpm_reg pm8921_pldo = {
+ .desc.linear_ranges = pldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+ .desc.n_voltages = 161,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8921_nldo = {
+ .desc.linear_ranges = nldo_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+ .desc.n_voltages = 64,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8921_nldo1200 = {
+ .desc.linear_ranges = nldo1200_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(nldo1200_ranges),
+ .desc.n_voltages = 124,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ldo_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8921_smps = {
+ .desc.linear_ranges = smps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+ .desc.n_voltages = 154,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+ .supports_force_mode_auto = true,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8921_ftsmps = {
+ .desc.linear_ranges = ftsmps_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
+ .desc.n_voltages = 101,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+ .supports_force_mode_auto = true,
+ .supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8921_ncp = {
+ .desc.linear_ranges = ncp_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
+ .desc.n_voltages = 32,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_ncp_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_switch = {
+ .desc.ops = &switch_ops,
+ .parts = &rpm8960_switch_parts,
+};
+
+static const struct qcom_rpm_reg smb208_smps = {
+ .desc.linear_ranges = smb208_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smb208_ranges),
+ .desc.n_voltages = 235,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
+static int rpm_reg_set(struct qcom_rpm_reg *vreg,
+ const struct request_member *req,
+ const int value)
+{
+ if (req->mask == 0 || (value << req->shift) & ~req->mask)
+ return -EINVAL;
+
+ vreg->val[req->word] &= ~req->mask;
+ vreg->val[req->word] |= value << req->shift;
+
+ return 0;
+}
+
+static int rpm_reg_of_parse_freq(struct device *dev,
+ struct device_node *node,
+ struct qcom_rpm_reg *vreg)
+{
+ static const int freq_table[] = {
+ 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, 2740000,
+ 2400000, 2130000, 1920000, 1750000, 1600000, 1480000, 1370000,
+ 1280000, 1200000,
+
+ };
+ const char *key;
+ u32 freq;
+ int ret;
+ int i;
+
+ key = "qcom,switch-mode-frequency";
+ ret = of_property_read_u32(node, key, &freq);
+ if (ret) {
+ dev_err(dev, "regulator requires %s property\n", key);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ if (freq == freq_table[i]) {
+ rpm_reg_set(vreg, &vreg->parts->freq, i + 1);
+ return 0;
+ }
+ }
+
+ dev_err(dev, "invalid frequency %d\n", freq);
+ return -EINVAL;
+}
+
+static int rpm_reg_of_parse(struct device_node *node,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct qcom_rpm_reg *vreg = config->driver_data;
+ struct device *dev = config->dev;
+ const char *key;
+ u32 force_mode;
+ bool pwm;
+ u32 val;
+ int ret;
+
+ key = "bias-pull-down";
+ if (of_property_read_bool(node, key)) {
+ ret = rpm_reg_set(vreg, &vreg->parts->pd, 1);
+ if (ret) {
+ dev_err(dev, "%s is invalid", key);
+ return ret;
+ }
+ }
+
+ if (vreg->parts->freq.mask) {
+ ret = rpm_reg_of_parse_freq(dev, node, vreg);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (vreg->parts->pm.mask) {
+ key = "qcom,power-mode-hysteretic";
+ pwm = !of_property_read_bool(node, key);
+
+ ret = rpm_reg_set(vreg, &vreg->parts->pm, pwm);
+ if (ret) {
+ dev_err(dev, "failed to set power mode\n");
+ return ret;
+ }
+ }
+
+ if (vreg->parts->fm.mask) {
+ force_mode = -1;
+
+ key = "qcom,force-mode";
+ ret = of_property_read_u32(node, key, &val);
+ if (ret == -EINVAL) {
+ val = QCOM_RPM_FORCE_MODE_NONE;
+ } else if (ret < 0) {
+ dev_err(dev, "failed to read %s\n", key);
+ return ret;
+ }
+
+ /*
+ * If force-mode is encoded as 2 bits then the
+ * possible register values are:
+ * NONE, LPM, HPM
+ * otherwise:
+ * NONE, LPM, AUTO, HPM, BYPASS
+ */
+ switch (val) {
+ case QCOM_RPM_FORCE_MODE_NONE:
+ force_mode = 0;
+ break;
+ case QCOM_RPM_FORCE_MODE_LPM:
+ force_mode = 1;
+ break;
+ case QCOM_RPM_FORCE_MODE_HPM:
+ if (FORCE_MODE_IS_2_BITS(vreg))
+ force_mode = 2;
+ else
+ force_mode = 3;
+ break;
+ case QCOM_RPM_FORCE_MODE_AUTO:
+ if (vreg->supports_force_mode_auto)
+ force_mode = 2;
+ break;
+ case QCOM_RPM_FORCE_MODE_BYPASS:
+ if (vreg->supports_force_mode_bypass)
+ force_mode = 4;
+ break;
+ }
+
+ if (force_mode == -1) {
+ dev_err(dev, "invalid force mode\n");
+ return -EINVAL;
+ }
+
+ ret = rpm_reg_set(vreg, &vreg->parts->fm, force_mode);
+ if (ret) {
+ dev_err(dev, "failed to set force mode\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+struct rpm_regulator_data {
+ const char *name;
+ int resource;
+ const struct qcom_rpm_reg *template;
+ const char *supply;
+};
+
+static const struct rpm_regulator_data rpm_pm8018_regulators[] = {
+ { "s1", QCOM_RPM_PM8018_SMPS1, &pm8018_smps, "vdd_s1" },
+ { "s2", QCOM_RPM_PM8018_SMPS2, &pm8018_smps, "vdd_s2" },
+ { "s3", QCOM_RPM_PM8018_SMPS3, &pm8018_smps, "vdd_s3" },
+ { "s4", QCOM_RPM_PM8018_SMPS4, &pm8018_smps, "vdd_s4" },
+ { "s5", QCOM_RPM_PM8018_SMPS5, &pm8018_smps, "vdd_s5" },
+
+ { "l2", QCOM_RPM_PM8018_LDO2, &pm8018_pldo, "vdd_l2" },
+ { "l3", QCOM_RPM_PM8018_LDO3, &pm8018_pldo, "vdd_l3" },
+ { "l4", QCOM_RPM_PM8018_LDO4, &pm8018_pldo, "vdd_l4" },
+ { "l5", QCOM_RPM_PM8018_LDO5, &pm8018_pldo, "vdd_l5" },
+ { "l6", QCOM_RPM_PM8018_LDO6, &pm8018_pldo, "vdd_l7" },
+ { "l7", QCOM_RPM_PM8018_LDO7, &pm8018_pldo, "vdd_l7" },
+ { "l8", QCOM_RPM_PM8018_LDO8, &pm8018_nldo, "vdd_l8" },
+ { "l9", QCOM_RPM_PM8018_LDO9, &pm8921_nldo1200,
+ "vdd_l9_l10_l11_l12" },
+ { "l10", QCOM_RPM_PM8018_LDO10, &pm8018_nldo, "vdd_l9_l10_l11_l12" },
+ { "l11", QCOM_RPM_PM8018_LDO11, &pm8018_nldo, "vdd_l9_l10_l11_l12" },
+ { "l12", QCOM_RPM_PM8018_LDO12, &pm8018_nldo, "vdd_l9_l10_l11_l12" },
+ { "l14", QCOM_RPM_PM8018_LDO14, &pm8018_pldo, "vdd_l14" },
+
+ { "lvs1", QCOM_RPM_PM8018_LVS1, &pm8018_switch, "lvs1_in" },
+
+ { }
+};
+
+static const struct rpm_regulator_data rpm_pm8058_regulators[] = {
+ { "l0", QCOM_RPM_PM8058_LDO0, &pm8058_nldo, "vdd_l0_l1_lvs" },
+ { "l1", QCOM_RPM_PM8058_LDO1, &pm8058_nldo, "vdd_l0_l1_lvs" },
+ { "l2", QCOM_RPM_PM8058_LDO2, &pm8058_pldo, "vdd_l2_l11_l12" },
+ { "l3", QCOM_RPM_PM8058_LDO3, &pm8058_pldo, "vdd_l3_l4_l5" },
+ { "l4", QCOM_RPM_PM8058_LDO4, &pm8058_pldo, "vdd_l3_l4_l5" },
+ { "l5", QCOM_RPM_PM8058_LDO5, &pm8058_pldo, "vdd_l3_l4_l5" },
+ { "l6", QCOM_RPM_PM8058_LDO6, &pm8058_pldo, "vdd_l6_l7" },
+ { "l7", QCOM_RPM_PM8058_LDO7, &pm8058_pldo, "vdd_l6_l7" },
+ { "l8", QCOM_RPM_PM8058_LDO8, &pm8058_pldo, "vdd_l8" },
+ { "l9", QCOM_RPM_PM8058_LDO9, &pm8058_pldo, "vdd_l9" },
+ { "l10", QCOM_RPM_PM8058_LDO10, &pm8058_pldo, "vdd_l10" },
+ { "l11", QCOM_RPM_PM8058_LDO11, &pm8058_pldo, "vdd_l2_l11_l12" },
+ { "l12", QCOM_RPM_PM8058_LDO12, &pm8058_pldo, "vdd_l2_l11_l12" },
+ { "l13", QCOM_RPM_PM8058_LDO13, &pm8058_pldo, "vdd_l13_l16" },
+ { "l14", QCOM_RPM_PM8058_LDO14, &pm8058_pldo, "vdd_l14_l15" },
+ { "l15", QCOM_RPM_PM8058_LDO15, &pm8058_pldo, "vdd_l14_l15" },
+ { "l16", QCOM_RPM_PM8058_LDO16, &pm8058_pldo, "vdd_l13_l16" },
+ { "l17", QCOM_RPM_PM8058_LDO17, &pm8058_pldo, "vdd_l17_l18" },
+ { "l18", QCOM_RPM_PM8058_LDO18, &pm8058_pldo, "vdd_l17_l18" },
+ { "l19", QCOM_RPM_PM8058_LDO19, &pm8058_pldo, "vdd_l19_l20" },
+ { "l20", QCOM_RPM_PM8058_LDO20, &pm8058_pldo, "vdd_l19_l20" },
+ { "l21", QCOM_RPM_PM8058_LDO21, &pm8058_nldo, "vdd_l21" },
+ { "l22", QCOM_RPM_PM8058_LDO22, &pm8058_nldo, "vdd_l22" },
+ { "l23", QCOM_RPM_PM8058_LDO23, &pm8058_nldo, "vdd_l23_l24_l25" },
+ { "l24", QCOM_RPM_PM8058_LDO24, &pm8058_nldo, "vdd_l23_l24_l25" },
+ { "l25", QCOM_RPM_PM8058_LDO25, &pm8058_nldo, "vdd_l23_l24_l25" },
+
+ { "s0", QCOM_RPM_PM8058_SMPS0, &pm8058_smps, "vdd_s0" },
+ { "s1", QCOM_RPM_PM8058_SMPS1, &pm8058_smps, "vdd_s1" },
+ { "s2", QCOM_RPM_PM8058_SMPS2, &pm8058_smps, "vdd_s2" },
+ { "s3", QCOM_RPM_PM8058_SMPS3, &pm8058_smps, "vdd_s3" },
+ { "s4", QCOM_RPM_PM8058_SMPS4, &pm8058_smps, "vdd_s4" },
+
+ { "lvs0", QCOM_RPM_PM8058_LVS0, &pm8058_switch, "vdd_l0_l1_lvs" },
+ { "lvs1", QCOM_RPM_PM8058_LVS1, &pm8058_switch, "vdd_l0_l1_lvs" },
+
+ { "ncp", QCOM_RPM_PM8058_NCP, &pm8058_ncp, "vdd_ncp" },
+ { }
+};
+
+static const struct rpm_regulator_data rpm_pm8901_regulators[] = {
+ { "l0", QCOM_RPM_PM8901_LDO0, &pm8901_nldo, "vdd_l0" },
+ { "l1", QCOM_RPM_PM8901_LDO1, &pm8901_pldo, "vdd_l1" },
+ { "l2", QCOM_RPM_PM8901_LDO2, &pm8901_pldo, "vdd_l2" },
+ { "l3", QCOM_RPM_PM8901_LDO3, &pm8901_pldo, "vdd_l3" },
+ { "l4", QCOM_RPM_PM8901_LDO4, &pm8901_pldo, "vdd_l4" },
+ { "l5", QCOM_RPM_PM8901_LDO5, &pm8901_pldo, "vdd_l5" },
+ { "l6", QCOM_RPM_PM8901_LDO6, &pm8901_pldo, "vdd_l6" },
+
+ { "s0", QCOM_RPM_PM8901_SMPS0, &pm8901_ftsmps, "vdd_s0" },
+ { "s1", QCOM_RPM_PM8901_SMPS1, &pm8901_ftsmps, "vdd_s1" },
+ { "s2", QCOM_RPM_PM8901_SMPS2, &pm8901_ftsmps, "vdd_s2" },
+ { "s3", QCOM_RPM_PM8901_SMPS3, &pm8901_ftsmps, "vdd_s3" },
+ { "s4", QCOM_RPM_PM8901_SMPS4, &pm8901_ftsmps, "vdd_s4" },
+
+ { "lvs0", QCOM_RPM_PM8901_LVS0, &pm8901_switch, "lvs0_in" },
+ { "lvs1", QCOM_RPM_PM8901_LVS1, &pm8901_switch, "lvs1_in" },
+ { "lvs2", QCOM_RPM_PM8901_LVS2, &pm8901_switch, "lvs2_in" },
+ { "lvs3", QCOM_RPM_PM8901_LVS3, &pm8901_switch, "lvs3_in" },
+
+ { "mvs", QCOM_RPM_PM8901_MVS, &pm8901_switch, "mvs_in" },
+ { }
+};
+
+static const struct rpm_regulator_data rpm_pm8921_regulators[] = {
+ { "s1", QCOM_RPM_PM8921_SMPS1, &pm8921_smps, "vdd_s1" },
+ { "s2", QCOM_RPM_PM8921_SMPS2, &pm8921_smps, "vdd_s2" },
+ { "s3", QCOM_RPM_PM8921_SMPS3, &pm8921_smps },
+ { "s4", QCOM_RPM_PM8921_SMPS4, &pm8921_smps, "vdd_s4" },
+ { "s7", QCOM_RPM_PM8921_SMPS7, &pm8921_smps, "vdd_s7" },
+ { "s8", QCOM_RPM_PM8921_SMPS8, &pm8921_smps, "vdd_s8" },
+
+ { "l1", QCOM_RPM_PM8921_LDO1, &pm8921_nldo, "vdd_l1_l2_l12_l18" },
+ { "l2", QCOM_RPM_PM8921_LDO2, &pm8921_nldo, "vdd_l1_l2_l12_l18" },
+ { "l3", QCOM_RPM_PM8921_LDO3, &pm8921_pldo, "vdd_l3_l15_l17" },
+ { "l4", QCOM_RPM_PM8921_LDO4, &pm8921_pldo, "vdd_l4_l14" },
+ { "l5", QCOM_RPM_PM8921_LDO5, &pm8921_pldo, "vdd_l5_l8_l16" },
+ { "l6", QCOM_RPM_PM8921_LDO6, &pm8921_pldo, "vdd_l6_l7" },
+ { "l7", QCOM_RPM_PM8921_LDO7, &pm8921_pldo, "vdd_l6_l7" },
+ { "l8", QCOM_RPM_PM8921_LDO8, &pm8921_pldo, "vdd_l5_l8_l16" },
+ { "l9", QCOM_RPM_PM8921_LDO9, &pm8921_pldo, "vdd_l9_l11" },
+ { "l10", QCOM_RPM_PM8921_LDO10, &pm8921_pldo, "vdd_l10_l22" },
+ { "l11", QCOM_RPM_PM8921_LDO11, &pm8921_pldo, "vdd_l9_l11" },
+ { "l12", QCOM_RPM_PM8921_LDO12, &pm8921_nldo, "vdd_l1_l2_l12_l18" },
+ { "l14", QCOM_RPM_PM8921_LDO14, &pm8921_pldo, "vdd_l4_l14" },
+ { "l15", QCOM_RPM_PM8921_LDO15, &pm8921_pldo, "vdd_l3_l15_l17" },
+ { "l16", QCOM_RPM_PM8921_LDO16, &pm8921_pldo, "vdd_l5_l8_l16" },
+ { "l17", QCOM_RPM_PM8921_LDO17, &pm8921_pldo, "vdd_l3_l15_l17" },
+ { "l18", QCOM_RPM_PM8921_LDO18, &pm8921_nldo, "vdd_l1_l2_l12_l18" },
+ { "l21", QCOM_RPM_PM8921_LDO21, &pm8921_pldo, "vdd_l21_l23_l29" },
+ { "l22", QCOM_RPM_PM8921_LDO22, &pm8921_pldo, "vdd_l10_l22" },
+ { "l23", QCOM_RPM_PM8921_LDO23, &pm8921_pldo, "vdd_l21_l23_l29" },
+ { "l24", QCOM_RPM_PM8921_LDO24, &pm8921_nldo1200, "vdd_l24" },
+ { "l25", QCOM_RPM_PM8921_LDO25, &pm8921_nldo1200, "vdd_l25" },
+ { "l26", QCOM_RPM_PM8921_LDO26, &pm8921_nldo1200, "vdd_l26" },
+ { "l27", QCOM_RPM_PM8921_LDO27, &pm8921_nldo1200, "vdd_l27" },
+ { "l28", QCOM_RPM_PM8921_LDO28, &pm8921_nldo1200, "vdd_l28" },
+ { "l29", QCOM_RPM_PM8921_LDO29, &pm8921_pldo, "vdd_l21_l23_l29" },
+
+ { "lvs1", QCOM_RPM_PM8921_LVS1, &pm8921_switch, "vin_lvs1_3_6" },
+ { "lvs2", QCOM_RPM_PM8921_LVS2, &pm8921_switch, "vin_lvs2" },
+ { "lvs3", QCOM_RPM_PM8921_LVS3, &pm8921_switch, "vin_lvs1_3_6" },
+ { "lvs4", QCOM_RPM_PM8921_LVS4, &pm8921_switch, "vin_lvs4_5_7" },
+ { "lvs5", QCOM_RPM_PM8921_LVS5, &pm8921_switch, "vin_lvs4_5_7" },
+ { "lvs6", QCOM_RPM_PM8921_LVS6, &pm8921_switch, "vin_lvs1_3_6" },
+ { "lvs7", QCOM_RPM_PM8921_LVS7, &pm8921_switch, "vin_lvs4_5_7" },
+
+ { "usb-switch", QCOM_RPM_USB_OTG_SWITCH, &pm8921_switch, "vin_5vs" },
+ { "hdmi-switch", QCOM_RPM_HDMI_SWITCH, &pm8921_switch, "vin_5vs" },
+ { "ncp", QCOM_RPM_PM8921_NCP, &pm8921_ncp, "vdd_ncp" },
+ { }
+};
+
+static const struct of_device_id rpm_of_match[] = {
+ { .compatible = "qcom,rpm-pm8018-regulators",
+ .data = &rpm_pm8018_regulators },
+ { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators },
+ { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators },
+ { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rpm_of_match);
+
+static int rpm_reg_probe(struct platform_device *pdev)
+{
+ const struct rpm_regulator_data *reg;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct qcom_rpm_reg *vreg;
+ struct qcom_rpm *rpm;
+
+ rpm = dev_get_drvdata(pdev->dev.parent);
+ if (!rpm) {
+ dev_err(&pdev->dev, "unable to retrieve handle to rpm\n");
+ return -ENODEV;
+ }
+
+ match = of_match_device(rpm_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ for (reg = match->data; reg->name; reg++) {
+ vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+
+ memcpy(vreg, reg->template, sizeof(*vreg));
+ mutex_init(&vreg->lock);
+
+ vreg->dev = &pdev->dev;
+ vreg->resource = reg->resource;
+ vreg->rpm = rpm;
+
+ vreg->desc.id = -1;
+ vreg->desc.owner = THIS_MODULE;
+ vreg->desc.type = REGULATOR_VOLTAGE;
+ vreg->desc.name = reg->name;
+ vreg->desc.supply_name = reg->supply;
+ vreg->desc.of_match = reg->name;
+ vreg->desc.of_parse_cb = rpm_reg_of_parse;
+
+ config.dev = &pdev->dev;
+ config.driver_data = vreg;
+ rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n", reg->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver rpm_reg_driver = {
+ .probe = rpm_reg_probe,
+ .driver = {
+ .name = "qcom_rpm_reg",
+ .of_match_table = of_match_ptr(rpm_of_match),
+ },
+};
+
+static int __init rpm_reg_init(void)
+{
+ return platform_driver_register(&rpm_reg_driver);
+}
+subsys_initcall(rpm_reg_init);
+
+static void __exit rpm_reg_exit(void)
+{
+ platform_driver_unregister(&rpm_reg_driver);
+}
+module_exit(rpm_reg_exit)
+
+MODULE_DESCRIPTION("Qualcomm RPM regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
new file mode 100644
index 000000000..3c6d6afd4
--- /dev/null
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -0,0 +1,791 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, The Linux Foundation. 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 version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/soc/qcom/smd-rpm.h>
+
+struct qcom_rpm_reg {
+ struct device *dev;
+
+ struct qcom_smd_rpm *rpm;
+
+ u32 type;
+ u32 id;
+
+ struct regulator_desc desc;
+
+ int is_enabled;
+ int uV;
+};
+
+struct rpm_regulator_req {
+ __le32 key;
+ __le32 nbytes;
+ __le32 value;
+};
+
+#define RPM_KEY_SWEN 0x6e657773 /* "swen" */
+#define RPM_KEY_UV 0x00007675 /* "uv" */
+#define RPM_KEY_MA 0x0000616d /* "ma" */
+
+static int rpm_reg_write_active(struct qcom_rpm_reg *vreg,
+ struct rpm_regulator_req *req,
+ size_t size)
+{
+ return qcom_rpm_smd_write(vreg->rpm,
+ QCOM_SMD_RPM_ACTIVE_STATE,
+ vreg->type,
+ vreg->id,
+ req, size);
+}
+
+static int rpm_reg_enable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ struct rpm_regulator_req req;
+ int ret;
+
+ req.key = cpu_to_le32(RPM_KEY_SWEN);
+ req.nbytes = cpu_to_le32(sizeof(u32));
+ req.value = cpu_to_le32(1);
+
+ ret = rpm_reg_write_active(vreg, &req, sizeof(req));
+ if (!ret)
+ vreg->is_enabled = 1;
+
+ return ret;
+}
+
+static int rpm_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->is_enabled;
+}
+
+static int rpm_reg_disable(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ struct rpm_regulator_req req;
+ int ret;
+
+ req.key = cpu_to_le32(RPM_KEY_SWEN);
+ req.nbytes = cpu_to_le32(sizeof(u32));
+ req.value = 0;
+
+ ret = rpm_reg_write_active(vreg, &req, sizeof(req));
+ if (!ret)
+ vreg->is_enabled = 0;
+
+ return ret;
+}
+
+static int rpm_reg_get_voltage(struct regulator_dev *rdev)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->uV;
+}
+
+static int rpm_reg_set_voltage(struct regulator_dev *rdev,
+ int min_uV,
+ int max_uV,
+ unsigned *selector)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ struct rpm_regulator_req req;
+ int ret = 0;
+
+ req.key = cpu_to_le32(RPM_KEY_UV);
+ req.nbytes = cpu_to_le32(sizeof(u32));
+ req.value = cpu_to_le32(min_uV);
+
+ ret = rpm_reg_write_active(vreg, &req, sizeof(req));
+ if (!ret)
+ vreg->uV = min_uV;
+
+ return ret;
+}
+
+static int rpm_reg_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+ struct rpm_regulator_req req;
+
+ req.key = cpu_to_le32(RPM_KEY_MA);
+ req.nbytes = cpu_to_le32(sizeof(u32));
+ req.value = cpu_to_le32(load_uA / 1000);
+
+ return rpm_reg_write_active(vreg, &req, sizeof(req));
+}
+
+static const struct regulator_ops rpm_smps_ldo_ops = {
+ .enable = rpm_reg_enable,
+ .disable = rpm_reg_disable,
+ .is_enabled = rpm_reg_is_enabled,
+ .list_voltage = regulator_list_voltage_linear_range,
+
+ .get_voltage = rpm_reg_get_voltage,
+ .set_voltage = rpm_reg_set_voltage,
+
+ .set_load = rpm_reg_set_load,
+};
+
+static const struct regulator_ops rpm_smps_ldo_ops_fixed = {
+ .enable = rpm_reg_enable,
+ .disable = rpm_reg_disable,
+ .is_enabled = rpm_reg_is_enabled,
+
+ .get_voltage = rpm_reg_get_voltage,
+ .set_voltage = rpm_reg_set_voltage,
+
+ .set_load = rpm_reg_set_load,
+};
+
+static const struct regulator_ops rpm_switch_ops = {
+ .enable = rpm_reg_enable,
+ .disable = rpm_reg_disable,
+ .is_enabled = rpm_reg_is_enabled,
+};
+
+static const struct regulator_ops rpm_bob_ops = {
+ .enable = rpm_reg_enable,
+ .disable = rpm_reg_disable,
+ .is_enabled = rpm_reg_is_enabled,
+
+ .get_voltage = rpm_reg_get_voltage,
+ .set_voltage = rpm_reg_set_voltage,
+};
+
+static const struct regulator_desc pma8084_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 159,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000),
+ REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 262,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
+ },
+ .n_linear_ranges = 3,
+ .n_voltages = 164,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 64,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_switch = {
+ .ops = &rpm_switch_ops,
+};
+
+static const struct regulator_desc pm8x41_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(1575000, 96, 158, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 159,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8841_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000),
+ REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 262,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8941_boost = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(4000000, 0, 30, 50000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 31,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8941_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
+ },
+ .n_linear_ranges = 3,
+ .n_voltages = 164,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8941_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 64,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8941_lnldo = {
+ .fixed_uV = 1740000,
+ .n_voltages = 1,
+ .ops = &rpm_smps_ldo_ops_fixed,
+};
+
+static const struct regulator_desc pm8941_switch = {
+ .ops = &rpm_switch_ops,
+};
+
+static const struct regulator_desc pm8916_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 208, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 209,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8916_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(375000, 0, 93, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 94,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8916_buck_lvo_smps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(750000, 96, 127, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 128,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8916_buck_hvo_smps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1550000, 0, 31, 25000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 32,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 159,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(350000, 0, 199, 5000),
+ REGULATOR_LINEAR_RANGE(700000, 200, 349, 10000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 350,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 64,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
+ },
+ .n_linear_ranges = 3,
+ .n_voltages = 164,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8994_switch = {
+ .ops = &rpm_switch_ops,
+};
+
+static const struct regulator_desc pm8994_lnldo = {
+ .fixed_uV = 1740000,
+ .n_voltages = 1,
+ .ops = &rpm_smps_ldo_ops_fixed,
+};
+
+static const struct regulator_desc pm8998_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 259,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 216,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 128,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 256,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_pldo_lv = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 128,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8998_switch = {
+ .ops = &rpm_switch_ops,
+};
+
+static const struct regulator_desc pmi8998_bob = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 84,
+ .ops = &rpm_bob_ops,
+};
+
+struct rpm_regulator_data {
+ const char *name;
+ u32 type;
+ u32 id;
+ const struct regulator_desc *desc;
+ const char *supply;
+};
+
+static const struct rpm_regulator_data rpm_pm8841_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPB, 1, &pm8x41_hfsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPB, 2, &pm8841_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPB, 3, &pm8x41_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPB, 4, &pm8841_ftsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPB, 5, &pm8841_ftsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPB, 6, &pm8841_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPB, 7, &pm8841_ftsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPB, 8, &pm8841_ftsmps, "vdd_s8" },
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pm8916_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8916_buck_lvo_smps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8916_buck_lvo_smps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8916_buck_lvo_smps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8916_buck_hvo_smps, "vdd_s4" },
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8916_nldo, "vdd_l1_l2_l3" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8916_nldo, "vdd_l1_l2_l3" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8916_nldo, "vdd_l1_l2_l3" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8916_pldo, "vdd_l4_l5_l6" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8916_pldo, "vdd_l4_l5_l6" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8916_pldo, "vdd_l4_l5_l6" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8916_pldo, "vdd_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pm8941_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8x41_hfsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8x41_hfsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8x41_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_BOOST, 1, &pm8941_boost },
+
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8941_nldo, "vdd_l1_l3" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8941_nldo, "vdd_l2_lvs1_2_3" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8941_nldo, "vdd_l1_l3" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8941_nldo, "vdd_l4_l11" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8941_lnldo, "vdd_l5_l7" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8941_pldo, "vdd_l6_l12_l14_l15" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8941_lnldo, "vdd_l5_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8941_pldo, "vdd_l8_l16_l18_l19" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8941_pldo, "vdd_l9_l10_l17_l22" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8941_pldo, "vdd_l9_l10_l17_l22" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8941_nldo, "vdd_l4_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8941_pldo, "vdd_l6_l12_l14_l15" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8941_pldo, "vdd_l13_l20_l23_l24" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8941_pldo, "vdd_l6_l12_l14_l15" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8941_pldo, "vdd_l6_l12_l14_l15" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8941_pldo, "vdd_l8_l16_l18_l19" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8941_pldo, "vdd_l9_l10_l17_l22" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8941_pldo, "vdd_l8_l16_l18_l19" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8941_pldo, "vdd_l8_l16_l18_l19" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8941_pldo, "vdd_l13_l20_l23_l24" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8941_pldo, "vdd_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8941_pldo, "vdd_l9_l10_l17_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8941_pldo, "vdd_l13_l20_l23_l24" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8941_pldo, "vdd_l13_l20_l23_l24" },
+
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8941_switch, "vdd_l2_lvs1_2_3" },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8941_switch, "vdd_l2_lvs1_2_3" },
+ { "lvs3", QCOM_SMD_RPM_VSA, 3, &pm8941_switch, "vdd_l2_lvs1_2_3" },
+
+ { "5vs1", QCOM_SMD_RPM_VSA, 4, &pm8941_switch, "vin_5vs" },
+ { "5vs2", QCOM_SMD_RPM_VSA, 5, &pm8941_switch, "vin_5vs" },
+
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pma8084_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pma8084_ftsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pma8084_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pma8084_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pma8084_hfsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPA, 5, &pma8084_hfsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPA, 6, &pma8084_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPA, 7, &pma8084_ftsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPA, 8, &pma8084_ftsmps, "vdd_s8" },
+ { "s9", QCOM_SMD_RPM_SMPA, 9, &pma8084_ftsmps, "vdd_s9" },
+ { "s10", QCOM_SMD_RPM_SMPA, 10, &pma8084_ftsmps, "vdd_s10" },
+ { "s11", QCOM_SMD_RPM_SMPA, 11, &pma8084_ftsmps, "vdd_s11" },
+ { "s12", QCOM_SMD_RPM_SMPA, 12, &pma8084_ftsmps, "vdd_s12" },
+
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pma8084_nldo, "vdd_l1_l11" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pma8084_pldo, "vdd_l5_l7" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pma8084_pldo, "vdd_l5_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pma8084_pldo, "vdd_l8" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pma8084_nldo, "vdd_l1_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pma8084_pldo, "vdd_l16_l25" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pma8084_pldo, "vdd_l17" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pma8084_pldo, "vdd_l18" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pma8084_pldo, "vdd_l19" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pma8084_pldo, "vdd_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pma8084_pldo, "vdd_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l25", QCOM_SMD_RPM_LDOA, 25, &pma8084_pldo, "vdd_l16_l25" },
+ { "l26", QCOM_SMD_RPM_LDOA, 26, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l27", QCOM_SMD_RPM_LDOA, 27, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pma8084_switch },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pma8084_switch },
+ { "lvs3", QCOM_SMD_RPM_VSA, 3, &pma8084_switch },
+ { "lvs4", QCOM_SMD_RPM_VSA, 4, &pma8084_switch },
+ { "5vs1", QCOM_SMD_RPM_VSA, 5, &pma8084_switch },
+
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pm8994_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8994_ftsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8994_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8994_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8994_hfsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8994_hfsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPA, 6, &pm8994_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPA, 7, &pm8994_hfsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPA, 8, &pm8994_ftsmps, "vdd_s8" },
+ { "s9", QCOM_SMD_RPM_SMPA, 9, &pm8994_ftsmps, "vdd_s9" },
+ { "s10", QCOM_SMD_RPM_SMPA, 10, &pm8994_ftsmps, "vdd_s10" },
+ { "s11", QCOM_SMD_RPM_SMPA, 11, &pm8994_ftsmps, "vdd_s11" },
+ { "s12", QCOM_SMD_RPM_SMPA, 12, &pm8994_ftsmps, "vdd_s12" },
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8994_nldo, "vdd_l1" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8994_nldo, "vdd_l2_l26_l28" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8994_nldo, "vdd_l3_l11" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8994_nldo, "vdd_l4_l27_l31" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8994_lnldo, "vdd_l5_l7" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8994_pldo, "vdd_l6_l12_l32" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8994_lnldo, "vdd_l5_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8994_pldo, "vdd_l8_l16_l30" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8994_nldo, "vdd_l3_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8994_pldo, "vdd_l6_l12_l32" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8994_pldo, "vdd_l14_l15" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8994_pldo, "vdd_l14_l15" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8994_pldo, "vdd_l8_l16_l30" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8994_pldo, "vdd_l17_l29" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8994_pldo, "vdd_l20_l21" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8994_pldo, "vdd_l20_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8994_pldo, "vdd_l9_l10_l18_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8994_pldo, "vdd_l13_l19_l23_l24" },
+ { "l25", QCOM_SMD_RPM_LDOA, 25, &pm8994_pldo, "vdd_l25" },
+ { "l26", QCOM_SMD_RPM_LDOA, 26, &pm8994_nldo, "vdd_l2_l26_l28" },
+ { "l27", QCOM_SMD_RPM_LDOA, 27, &pm8994_nldo, "vdd_l4_l27_l31" },
+ { "l28", QCOM_SMD_RPM_LDOA, 28, &pm8994_nldo, "vdd_l2_l26_l28" },
+ { "l29", QCOM_SMD_RPM_LDOA, 29, &pm8994_pldo, "vdd_l17_l29" },
+ { "l30", QCOM_SMD_RPM_LDOA, 30, &pm8994_pldo, "vdd_l8_l16_l30" },
+ { "l31", QCOM_SMD_RPM_LDOA, 31, &pm8994_nldo, "vdd_l4_l27_l31" },
+ { "l32", QCOM_SMD_RPM_LDOA, 32, &pm8994_pldo, "vdd_l6_l12_l32" },
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8994_switch, "vdd_lvs1_2" },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8994_switch, "vdd_lvs1_2" },
+
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pm8998_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8998_ftsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8998_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8998_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8998_hfsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPA, 5, &pm8998_hfsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPA, 6, &pm8998_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPA, 7, &pm8998_ftsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPA, 8, &pm8998_ftsmps, "vdd_s8" },
+ { "s9", QCOM_SMD_RPM_SMPA, 9, &pm8998_ftsmps, "vdd_s9" },
+ { "s10", QCOM_SMD_RPM_SMPA, 10, &pm8998_ftsmps, "vdd_s10" },
+ { "s11", QCOM_SMD_RPM_SMPA, 11, &pm8998_ftsmps, "vdd_s11" },
+ { "s12", QCOM_SMD_RPM_SMPA, 12, &pm8998_ftsmps, "vdd_s12" },
+ { "s13", QCOM_SMD_RPM_SMPA, 13, &pm8998_ftsmps, "vdd_s13" },
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8998_nldo, "vdd_l1_l27" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8998_nldo, "vdd_l2_l8_l17" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8998_nldo, "vdd_l3_l11" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8998_nldo, "vdd_l4_l5" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8998_nldo, "vdd_l4_l5" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8998_pldo, "vdd_l6" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8998_nldo, "vdd_l2_l8_l17" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8998_pldo, "vdd_l9" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8998_pldo, "vdd_l10_l23_l25" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8998_nldo, "vdd_l3_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8998_pldo, "vdd_l13_l19_l21" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8998_pldo_lv, "vdd_l7_l12_l14_l15" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8998_pldo, "vdd_l16_l28" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8998_nldo, "vdd_l2_l8_l17" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8998_pldo, "vdd_l18_l22" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8998_pldo, "vdd_l13_l19_l21" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8998_pldo, "vdd_l20_l24" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8998_pldo, "vdd_l13_l19_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8998_pldo, "vdd_l18_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8998_pldo, "vdd_l10_l23_l25" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8998_pldo, "vdd_l20_l24" },
+ { "l25", QCOM_SMD_RPM_LDOA, 25, &pm8998_pldo, "vdd_l10_l23_l25" },
+ { "l26", QCOM_SMD_RPM_LDOA, 26, &pm8998_nldo, "vdd_l26" },
+ { "l27", QCOM_SMD_RPM_LDOA, 27, &pm8998_nldo, "vdd_l1_l27" },
+ { "l28", QCOM_SMD_RPM_LDOA, 28, &pm8998_pldo, "vdd_l16_l28" },
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8998_switch, "vdd_lvs1_lvs2" },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8998_switch, "vdd_lvs1_lvs2" },
+ {}
+};
+
+static const struct rpm_regulator_data rpm_pmi8998_regulators[] = {
+ { "bob", QCOM_SMD_RPM_BOBB, 1, &pmi8998_bob, "vdd_bob" },
+ {}
+};
+
+static const struct of_device_id rpm_of_match[] = {
+ { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators },
+ { .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators },
+ { .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators },
+ { .compatible = "qcom,rpm-pm8994-regulators", .data = &rpm_pm8994_regulators },
+ { .compatible = "qcom,rpm-pm8998-regulators", .data = &rpm_pm8998_regulators },
+ { .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators },
+ { .compatible = "qcom,rpm-pmi8998-regulators", .data = &rpm_pmi8998_regulators },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rpm_of_match);
+
+/**
+ * rpm_regulator_init_vreg() - initialize all attributes of a qcom_smd-regulator
+ * @vreg: Pointer to the individual qcom_smd-regulator resource
+ * @dev: Pointer to the top level qcom_smd-regulator PMIC device
+ * @node: Pointer to the individual qcom_smd-regulator resource
+ * device node
+ * @rpm: Pointer to the rpm bus node
+ * @pmic_rpm_data: Pointer to a null-terminated array of qcom_smd-regulator
+ * resources defined for the top level PMIC device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpm_regulator_init_vreg(struct qcom_rpm_reg *vreg, struct device *dev,
+ struct device_node *node, struct qcom_smd_rpm *rpm,
+ const struct rpm_regulator_data *pmic_rpm_data)
+{
+ struct regulator_config config = {};
+ const struct rpm_regulator_data *rpm_data;
+ struct regulator_dev *rdev;
+ int ret;
+
+ for (rpm_data = pmic_rpm_data; rpm_data->name; rpm_data++)
+ if (of_node_name_eq(node, rpm_data->name))
+ break;
+
+ if (!rpm_data->name) {
+ dev_err(dev, "Unknown regulator %pOFn\n", node);
+ return -EINVAL;
+ }
+
+ vreg->dev = dev;
+ vreg->rpm = rpm;
+ vreg->type = rpm_data->type;
+ vreg->id = rpm_data->id;
+
+ memcpy(&vreg->desc, rpm_data->desc, sizeof(vreg->desc));
+ vreg->desc.name = rpm_data->name;
+ vreg->desc.supply_name = rpm_data->supply;
+ vreg->desc.owner = THIS_MODULE;
+ vreg->desc.type = REGULATOR_VOLTAGE;
+ vreg->desc.of_match = rpm_data->name;
+
+ config.dev = dev;
+ config.of_node = node;
+ config.driver_data = vreg;
+
+ rdev = devm_regulator_register(dev, &vreg->desc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "%pOFn: devm_regulator_register() failed, ret=%d\n", node, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rpm_reg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct rpm_regulator_data *vreg_data;
+ struct device_node *node;
+ struct qcom_rpm_reg *vreg;
+ struct qcom_smd_rpm *rpm;
+ int ret;
+
+ rpm = dev_get_drvdata(pdev->dev.parent);
+ if (!rpm) {
+ dev_err(&pdev->dev, "Unable to retrieve handle to rpm\n");
+ return -ENODEV;
+ }
+
+ vreg_data = of_device_get_match_data(dev);
+ if (!vreg_data)
+ return -ENODEV;
+
+ for_each_available_child_of_node(dev->of_node, node) {
+ vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ of_node_put(node);
+ return -ENOMEM;
+ }
+
+ ret = rpm_regulator_init_vreg(vreg, dev, node, rpm, vreg_data);
+
+ if (ret < 0) {
+ of_node_put(node);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver rpm_reg_driver = {
+ .probe = rpm_reg_probe,
+ .driver = {
+ .name = "qcom_rpm_smd_regulator",
+ .of_match_table = rpm_of_match,
+ },
+};
+
+static int __init rpm_reg_init(void)
+{
+ return platform_driver_register(&rpm_reg_driver);
+}
+subsys_initcall(rpm_reg_init);
+
+static void __exit rpm_reg_exit(void)
+{
+ platform_driver_unregister(&rpm_reg_driver);
+}
+module_exit(rpm_reg_exit)
+
+MODULE_DESCRIPTION("Qualcomm RPM regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
new file mode 100644
index 000000000..53a61fb65
--- /dev/null
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -0,0 +1,1883 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. 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 version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+
+/* Pin control enable input pins. */
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_NONE 0x00
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN0 0x01
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN1 0x02
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN2 0x04
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN3 0x08
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT 0x10
+
+/* Pin control high power mode input pins. */
+#define SPMI_REGULATOR_PIN_CTRL_HPM_NONE 0x00
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN0 0x01
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN1 0x02
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN2 0x04
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN3 0x08
+#define SPMI_REGULATOR_PIN_CTRL_HPM_SLEEP_B 0x10
+#define SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT 0x20
+
+/*
+ * Used with enable parameters to specify that hardware default register values
+ * should be left unaltered.
+ */
+#define SPMI_REGULATOR_USE_HW_DEFAULT 2
+
+/* Soft start strength of a voltage switch type regulator */
+enum spmi_vs_soft_start_str {
+ SPMI_VS_SOFT_START_STR_0P05_UA = 0,
+ SPMI_VS_SOFT_START_STR_0P25_UA,
+ SPMI_VS_SOFT_START_STR_0P55_UA,
+ SPMI_VS_SOFT_START_STR_0P75_UA,
+ SPMI_VS_SOFT_START_STR_HW_DEFAULT,
+};
+
+/**
+ * struct spmi_regulator_init_data - spmi-regulator initialization data
+ * @pin_ctrl_enable: Bit mask specifying which hardware pins should be
+ * used to enable the regulator, if any
+ * Value should be an ORing of
+ * SPMI_REGULATOR_PIN_CTRL_ENABLE_* constants. If
+ * the bit specified by
+ * SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT is
+ * set, then pin control enable hardware registers
+ * will not be modified.
+ * @pin_ctrl_hpm: Bit mask specifying which hardware pins should be
+ * used to force the regulator into high power
+ * mode, if any
+ * Value should be an ORing of
+ * SPMI_REGULATOR_PIN_CTRL_HPM_* constants. If
+ * the bit specified by
+ * SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT is
+ * set, then pin control mode hardware registers
+ * will not be modified.
+ * @vs_soft_start_strength: This parameter sets the soft start strength for
+ * voltage switch type regulators. Its value
+ * should be one of SPMI_VS_SOFT_START_STR_*. If
+ * its value is SPMI_VS_SOFT_START_STR_HW_DEFAULT,
+ * then the soft start strength will be left at its
+ * default hardware value.
+ */
+struct spmi_regulator_init_data {
+ unsigned pin_ctrl_enable;
+ unsigned pin_ctrl_hpm;
+ enum spmi_vs_soft_start_str vs_soft_start_strength;
+};
+
+/* These types correspond to unique register layouts. */
+enum spmi_regulator_logical_type {
+ SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_LDO,
+ SPMI_REGULATOR_LOGICAL_TYPE_VS,
+ SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
+ SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+ SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+};
+
+enum spmi_regulator_type {
+ SPMI_REGULATOR_TYPE_BUCK = 0x03,
+ SPMI_REGULATOR_TYPE_LDO = 0x04,
+ SPMI_REGULATOR_TYPE_VS = 0x05,
+ SPMI_REGULATOR_TYPE_BOOST = 0x1b,
+ SPMI_REGULATOR_TYPE_FTS = 0x1c,
+ SPMI_REGULATOR_TYPE_BOOST_BYP = 0x1f,
+ SPMI_REGULATOR_TYPE_ULT_LDO = 0x21,
+ SPMI_REGULATOR_TYPE_ULT_BUCK = 0x22,
+};
+
+enum spmi_regulator_subtype {
+ SPMI_REGULATOR_SUBTYPE_GP_CTL = 0x08,
+ SPMI_REGULATOR_SUBTYPE_RF_CTL = 0x09,
+ SPMI_REGULATOR_SUBTYPE_N50 = 0x01,
+ SPMI_REGULATOR_SUBTYPE_N150 = 0x02,
+ SPMI_REGULATOR_SUBTYPE_N300 = 0x03,
+ SPMI_REGULATOR_SUBTYPE_N600 = 0x04,
+ SPMI_REGULATOR_SUBTYPE_N1200 = 0x05,
+ SPMI_REGULATOR_SUBTYPE_N600_ST = 0x06,
+ SPMI_REGULATOR_SUBTYPE_N1200_ST = 0x07,
+ SPMI_REGULATOR_SUBTYPE_N900_ST = 0x14,
+ SPMI_REGULATOR_SUBTYPE_N300_ST = 0x15,
+ SPMI_REGULATOR_SUBTYPE_P50 = 0x08,
+ SPMI_REGULATOR_SUBTYPE_P150 = 0x09,
+ SPMI_REGULATOR_SUBTYPE_P300 = 0x0a,
+ SPMI_REGULATOR_SUBTYPE_P600 = 0x0b,
+ SPMI_REGULATOR_SUBTYPE_P1200 = 0x0c,
+ SPMI_REGULATOR_SUBTYPE_LN = 0x10,
+ SPMI_REGULATOR_SUBTYPE_LV_P50 = 0x28,
+ SPMI_REGULATOR_SUBTYPE_LV_P150 = 0x29,
+ SPMI_REGULATOR_SUBTYPE_LV_P300 = 0x2a,
+ SPMI_REGULATOR_SUBTYPE_LV_P600 = 0x2b,
+ SPMI_REGULATOR_SUBTYPE_LV_P1200 = 0x2c,
+ SPMI_REGULATOR_SUBTYPE_LV_P450 = 0x2d,
+ SPMI_REGULATOR_SUBTYPE_LV100 = 0x01,
+ SPMI_REGULATOR_SUBTYPE_LV300 = 0x02,
+ SPMI_REGULATOR_SUBTYPE_MV300 = 0x08,
+ SPMI_REGULATOR_SUBTYPE_MV500 = 0x09,
+ SPMI_REGULATOR_SUBTYPE_HDMI = 0x10,
+ SPMI_REGULATOR_SUBTYPE_OTG = 0x11,
+ SPMI_REGULATOR_SUBTYPE_5V_BOOST = 0x01,
+ SPMI_REGULATOR_SUBTYPE_FTS_CTL = 0x08,
+ SPMI_REGULATOR_SUBTYPE_FTS2p5_CTL = 0x09,
+ SPMI_REGULATOR_SUBTYPE_BB_2A = 0x01,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL1 = 0x0d,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL2 = 0x0e,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3 = 0x0f,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10,
+};
+
+enum spmi_common_regulator_registers {
+ SPMI_COMMON_REG_DIG_MAJOR_REV = 0x01,
+ SPMI_COMMON_REG_TYPE = 0x04,
+ SPMI_COMMON_REG_SUBTYPE = 0x05,
+ SPMI_COMMON_REG_VOLTAGE_RANGE = 0x40,
+ SPMI_COMMON_REG_VOLTAGE_SET = 0x41,
+ SPMI_COMMON_REG_MODE = 0x45,
+ SPMI_COMMON_REG_ENABLE = 0x46,
+ SPMI_COMMON_REG_PULL_DOWN = 0x48,
+ SPMI_COMMON_REG_SOFT_START = 0x4c,
+ SPMI_COMMON_REG_STEP_CTRL = 0x61,
+};
+
+enum spmi_vs_registers {
+ SPMI_VS_REG_OCP = 0x4a,
+ SPMI_VS_REG_SOFT_START = 0x4c,
+};
+
+enum spmi_boost_registers {
+ SPMI_BOOST_REG_CURRENT_LIMIT = 0x4a,
+};
+
+enum spmi_boost_byp_registers {
+ SPMI_BOOST_BYP_REG_CURRENT_LIMIT = 0x4b,
+};
+
+enum spmi_saw3_registers {
+ SAW3_SECURE = 0x00,
+ SAW3_ID = 0x04,
+ SAW3_SPM_STS = 0x0C,
+ SAW3_AVS_STS = 0x10,
+ SAW3_PMIC_STS = 0x14,
+ SAW3_RST = 0x18,
+ SAW3_VCTL = 0x1C,
+ SAW3_AVS_CTL = 0x20,
+ SAW3_AVS_LIMIT = 0x24,
+ SAW3_AVS_DLY = 0x28,
+ SAW3_AVS_HYSTERESIS = 0x2C,
+ SAW3_SPM_STS2 = 0x38,
+ SAW3_SPM_PMIC_DATA_3 = 0x4C,
+ SAW3_VERSION = 0xFD0,
+};
+
+/* Used for indexing into ctrl_reg. These are offets from 0x40 */
+enum spmi_common_control_register_index {
+ SPMI_COMMON_IDX_VOLTAGE_RANGE = 0,
+ SPMI_COMMON_IDX_VOLTAGE_SET = 1,
+ SPMI_COMMON_IDX_MODE = 5,
+ SPMI_COMMON_IDX_ENABLE = 6,
+};
+
+/* Common regulator control register layout */
+#define SPMI_COMMON_ENABLE_MASK 0x80
+#define SPMI_COMMON_ENABLE 0x80
+#define SPMI_COMMON_DISABLE 0x00
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN3_MASK 0x08
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN2_MASK 0x04
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN1_MASK 0x02
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN0_MASK 0x01
+#define SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK 0x0f
+
+/* Common regulator mode register layout */
+#define SPMI_COMMON_MODE_HPM_MASK 0x80
+#define SPMI_COMMON_MODE_AUTO_MASK 0x40
+#define SPMI_COMMON_MODE_BYPASS_MASK 0x20
+#define SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK 0x10
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN3_MASK 0x08
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN2_MASK 0x04
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN1_MASK 0x02
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN0_MASK 0x01
+#define SPMI_COMMON_MODE_FOLLOW_ALL_MASK 0x1f
+
+/* Common regulator pull down control register layout */
+#define SPMI_COMMON_PULL_DOWN_ENABLE_MASK 0x80
+
+/* LDO regulator current limit control register layout */
+#define SPMI_LDO_CURRENT_LIMIT_ENABLE_MASK 0x80
+
+/* LDO regulator soft start control register layout */
+#define SPMI_LDO_SOFT_START_ENABLE_MASK 0x80
+
+/* VS regulator over current protection control register layout */
+#define SPMI_VS_OCP_OVERRIDE 0x01
+#define SPMI_VS_OCP_NO_OVERRIDE 0x00
+
+/* VS regulator soft start control register layout */
+#define SPMI_VS_SOFT_START_ENABLE_MASK 0x80
+#define SPMI_VS_SOFT_START_SEL_MASK 0x03
+
+/* Boost regulator current limit control register layout */
+#define SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK 0x80
+#define SPMI_BOOST_CURRENT_LIMIT_MASK 0x07
+
+#define SPMI_VS_OCP_DEFAULT_MAX_RETRIES 10
+#define SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS 30
+#define SPMI_VS_OCP_FALL_DELAY_US 90
+#define SPMI_VS_OCP_FAULT_DELAY_US 20000
+
+#define SPMI_FTSMPS_STEP_CTRL_STEP_MASK 0x18
+#define SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT 3
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK 0x07
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT 0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_FTSMPS_CLOCK_RATE 19200
+
+/* Minimum voltage stepper delay for each step. */
+#define SPMI_FTSMPS_STEP_DELAY 8
+#define SPMI_DEFAULT_STEP_DELAY 20
+
+/*
+ * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define SPMI_FTSMPS_STEP_MARGIN_NUM 4
+#define SPMI_FTSMPS_STEP_MARGIN_DEN 5
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct spmi_voltage_range - regulator set point voltage mapping description
+ * @min_uV: Minimum programmable output voltage resulting from
+ * set point register value 0x00
+ * @max_uV: Maximum programmable output voltage
+ * @step_uV: Output voltage increase resulting from the set point
+ * register value increasing by 1
+ * @set_point_min_uV: Minimum allowed voltage
+ * @set_point_max_uV: Maximum allowed voltage. This may be tweaked in order
+ * to pick which range should be used in the case of
+ * overlapping set points.
+ * @n_voltages: Number of preferred voltage set points present in this
+ * range
+ * @range_sel: Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct spmi_voltage_range {
+ int min_uV;
+ int max_uV;
+ int step_uV;
+ int set_point_min_uV;
+ int set_point_max_uV;
+ unsigned n_voltages;
+ u8 range_sel;
+};
+
+/*
+ * The ranges specified in the spmi_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct spmi_voltage_set_points {
+ struct spmi_voltage_range *range;
+ int count;
+ unsigned n_voltages;
+};
+
+struct spmi_regulator {
+ struct regulator_desc desc;
+ struct device *dev;
+ struct delayed_work ocp_work;
+ struct regmap *regmap;
+ struct spmi_voltage_set_points *set_points;
+ enum spmi_regulator_logical_type logical_type;
+ int ocp_irq;
+ int ocp_count;
+ int ocp_max_retries;
+ int ocp_retry_delay_ms;
+ int hpm_min_load;
+ int slew_rate;
+ ktime_t vs_enable_time;
+ u16 base;
+ struct list_head node;
+};
+
+struct spmi_regulator_mapping {
+ enum spmi_regulator_type type;
+ enum spmi_regulator_subtype subtype;
+ enum spmi_regulator_logical_type logical_type;
+ u32 revision_min;
+ u32 revision_max;
+ struct regulator_ops *ops;
+ struct spmi_voltage_set_points *set_points;
+ int hpm_min_load;
+};
+
+struct spmi_regulator_data {
+ const char *name;
+ u16 base;
+ const char *supply;
+ const char *ocp;
+ u16 force_type;
+};
+
+#define SPMI_VREG(_type, _subtype, _dig_major_min, _dig_major_max, \
+ _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+ { \
+ .type = SPMI_REGULATOR_TYPE_##_type, \
+ .subtype = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+ .revision_min = _dig_major_min, \
+ .revision_max = _dig_major_max, \
+ .logical_type = SPMI_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+ .ops = &spmi_##_ops_val##_ops, \
+ .set_points = &_set_points_val##_set_points, \
+ .hpm_min_load = _hpm_min_load, \
+ }
+
+#define SPMI_VREG_VS(_subtype, _dig_major_min, _dig_major_max) \
+ { \
+ .type = SPMI_REGULATOR_TYPE_VS, \
+ .subtype = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+ .revision_min = _dig_major_min, \
+ .revision_max = _dig_major_max, \
+ .logical_type = SPMI_REGULATOR_LOGICAL_TYPE_VS, \
+ .ops = &spmi_vs_ops, \
+ }
+
+#define SPMI_VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+ _set_point_max_uV, _max_uV, _step_uV) \
+ { \
+ .min_uV = _min_uV, \
+ .max_uV = _max_uV, \
+ .set_point_min_uV = _set_point_min_uV, \
+ .set_point_max_uV = _set_point_max_uV, \
+ .step_uV = _step_uV, \
+ .range_sel = _range_sel, \
+ }
+
+#define DEFINE_SPMI_SET_POINTS(name) \
+struct spmi_voltage_set_points name##_set_points = { \
+ .range = name##_ranges, \
+ .count = ARRAY_SIZE(name##_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges. Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique. The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct spmi_voltage_range pldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(2, 750000, 750000, 1537500, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+ SPMI_VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct spmi_voltage_range nldo1_ranges[] = {
+ SPMI_VOLTAGE_RANGE(2, 750000, 750000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo2_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 0, 0, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 375000, 375000, 768750, 768750, 6250),
+ SPMI_VOLTAGE_RANGE(2, 750000, 775000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo3_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1537500, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 375000, 0, 0, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(2, 750000, 0, 0, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ln_ldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(1, 690000, 690000, 1110000, 1110000, 60000),
+ SPMI_VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct spmi_voltage_range smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1562500, 1562500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct spmi_voltage_range ftsmps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 0, 350000, 1275000, 1275000, 5000),
+ SPMI_VOLTAGE_RANGE(1, 0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct spmi_voltage_range ftsmps2p5_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 80000, 350000, 1355000, 1355000, 5000),
+ SPMI_VOLTAGE_RANGE(1, 160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct spmi_voltage_range boost_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct spmi_voltage_range boost_byp_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct spmi_voltage_range ult_lo_smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1562500, 1562500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 750000, 0, 0, 1525000, 25000),
+};
+
+static struct spmi_voltage_range ult_ho_smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct spmi_voltage_range ult_nldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ult_pldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static DEFINE_SPMI_SET_POINTS(pldo);
+static DEFINE_SPMI_SET_POINTS(nldo1);
+static DEFINE_SPMI_SET_POINTS(nldo2);
+static DEFINE_SPMI_SET_POINTS(nldo3);
+static DEFINE_SPMI_SET_POINTS(ln_ldo);
+static DEFINE_SPMI_SET_POINTS(smps);
+static DEFINE_SPMI_SET_POINTS(ftsmps);
+static DEFINE_SPMI_SET_POINTS(ftsmps2p5);
+static DEFINE_SPMI_SET_POINTS(boost);
+static DEFINE_SPMI_SET_POINTS(boost_byp);
+static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
+static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
+static DEFINE_SPMI_SET_POINTS(ult_nldo);
+static DEFINE_SPMI_SET_POINTS(ult_pldo);
+
+static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf,
+ int len)
+{
+ return regmap_bulk_read(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static inline int spmi_vreg_write(struct spmi_regulator *vreg, u16 addr,
+ u8 *buf, int len)
+{
+ return regmap_bulk_write(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
+ u8 mask)
+{
+ return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
+}
+
+static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ if (vreg->ocp_irq) {
+ vreg->ocp_count = 0;
+ vreg->vs_enable_time = ktime_get();
+ }
+
+ return regulator_enable_regmap(rdev);
+}
+
+static int spmi_regulator_vs_ocp(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 reg = SPMI_VS_OCP_OVERRIDE;
+
+ return spmi_vreg_write(vreg, SPMI_VS_REG_OCP, &reg, 1);
+}
+
+static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
+ int min_uV, int max_uV)
+{
+ const struct spmi_voltage_range *range;
+ int uV = min_uV;
+ int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+ int selector, voltage_sel;
+
+ /* Check if request voltage is outside of physically settable range. */
+ lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+ lim_max_uV =
+ vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+ if (uV < lim_min_uV && max_uV >= lim_min_uV)
+ uV = lim_min_uV;
+
+ if (uV < lim_min_uV || uV > lim_max_uV) {
+ dev_err(vreg->dev,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, lim_min_uV, lim_max_uV);
+ return -EINVAL;
+ }
+
+ /* Find the range which uV is inside of. */
+ for (i = vreg->set_points->count - 1; i > 0; i--) {
+ range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+ if (uV > range_max_uV && range_max_uV > 0)
+ break;
+ }
+
+ range_id = i;
+ range = &vreg->set_points->range[range_id];
+
+ /*
+ * Force uV to be an allowed set point by applying a ceiling function to
+ * the uV value.
+ */
+ voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+ uV = voltage_sel * range->step_uV + range->min_uV;
+
+ if (uV > max_uV) {
+ dev_err(vreg->dev,
+ "request v=[%d, %d] cannot be met by any set point; "
+ "next set point: %d\n",
+ min_uV, max_uV, uV);
+ return -EINVAL;
+ }
+
+ selector = 0;
+ for (i = 0; i < range_id; i++)
+ selector += vreg->set_points->range[i].n_voltages;
+ selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+ return selector;
+}
+
+static int spmi_sw_selector_to_hw(struct spmi_regulator *vreg,
+ unsigned selector, u8 *range_sel,
+ u8 *voltage_sel)
+{
+ const struct spmi_voltage_range *range, *end;
+ unsigned offset;
+
+ range = vreg->set_points->range;
+ end = range + vreg->set_points->count;
+
+ for (; range < end; range++) {
+ if (selector < range->n_voltages) {
+ /*
+ * hardware selectors between set point min and real
+ * min are invalid so we ignore them
+ */
+ offset = range->set_point_min_uV - range->min_uV;
+ offset /= range->step_uV;
+ *voltage_sel = selector + offset;
+ *range_sel = range->range_sel;
+ return 0;
+ }
+
+ selector -= range->n_voltages;
+ }
+
+ return -EINVAL;
+}
+
+static int spmi_hw_selector_to_sw(struct spmi_regulator *vreg, u8 hw_sel,
+ const struct spmi_voltage_range *range)
+{
+ unsigned sw_sel = 0;
+ unsigned offset, max_hw_sel;
+ const struct spmi_voltage_range *r = vreg->set_points->range;
+ const struct spmi_voltage_range *end = r + vreg->set_points->count;
+
+ for (; r < end; r++) {
+ if (r == range && range->n_voltages) {
+ /*
+ * hardware selectors between set point min and real
+ * min and between set point max and real max are
+ * invalid so we return an error if they're
+ * programmed into the hardware
+ */
+ offset = range->set_point_min_uV - range->min_uV;
+ offset /= range->step_uV;
+ if (hw_sel < offset)
+ return -EINVAL;
+
+ max_hw_sel = range->set_point_max_uV - range->min_uV;
+ max_hw_sel /= range->step_uV;
+ if (hw_sel > max_hw_sel)
+ return -EINVAL;
+
+ return sw_sel + hw_sel - offset;
+ }
+ sw_sel += r->n_voltages;
+ }
+
+ return -EINVAL;
+}
+
+static const struct spmi_voltage_range *
+spmi_regulator_find_range(struct spmi_regulator *vreg)
+{
+ u8 range_sel;
+ const struct spmi_voltage_range *range, *end;
+
+ range = vreg->set_points->range;
+ end = range + vreg->set_points->count;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
+
+ for (; range < end; range++)
+ if (range->range_sel == range_sel)
+ return range;
+
+ return NULL;
+}
+
+static int spmi_regulator_select_voltage_same_range(struct spmi_regulator *vreg,
+ int min_uV, int max_uV)
+{
+ const struct spmi_voltage_range *range;
+ int uV = min_uV;
+ int i, selector;
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ goto different_range;
+
+ if (uV < range->min_uV && max_uV >= range->min_uV)
+ uV = range->min_uV;
+
+ if (uV < range->min_uV || uV > range->max_uV) {
+ /* Current range doesn't support the requested voltage. */
+ goto different_range;
+ }
+
+ /*
+ * Force uV to be an allowed set point by applying a ceiling function to
+ * the uV value.
+ */
+ uV = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+ uV = uV * range->step_uV + range->min_uV;
+
+ if (uV > max_uV) {
+ /*
+ * No set point in the current voltage range is within the
+ * requested min_uV to max_uV range.
+ */
+ goto different_range;
+ }
+
+ selector = 0;
+ for (i = 0; i < vreg->set_points->count; i++) {
+ if (uV >= vreg->set_points->range[i].set_point_min_uV
+ && uV <= vreg->set_points->range[i].set_point_max_uV) {
+ selector +=
+ (uV - vreg->set_points->range[i].set_point_min_uV)
+ / vreg->set_points->range[i].step_uV;
+ break;
+ }
+
+ selector += vreg->set_points->range[i].n_voltages;
+ }
+
+ if (selector >= vreg->set_points->n_voltages)
+ goto different_range;
+
+ return selector;
+
+different_range:
+ return spmi_regulator_select_voltage(vreg, min_uV, max_uV);
+}
+
+static int spmi_regulator_common_map_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ /*
+ * Favor staying in the current voltage range if possible. This avoids
+ * voltage spikes that occur when changing the voltage range.
+ */
+ return spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV);
+}
+
+static int
+spmi_regulator_common_set_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 buf[2];
+ u8 range_sel, voltage_sel;
+
+ ret = spmi_sw_selector_to_hw(vreg, selector, &range_sel, &voltage_sel);
+ if (ret)
+ return ret;
+
+ buf[0] = range_sel;
+ buf[1] = voltage_sel;
+ return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, buf, 2);
+}
+
+static int spmi_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector, unsigned int new_selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ int diff_uV;
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ diff_uV = abs(new_selector - old_selector) * range->step_uV;
+
+ return DIV_ROUND_UP(diff_uV, vreg->slew_rate);
+}
+
+static int spmi_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ return spmi_hw_selector_to_sw(vreg, voltage_sel, range);
+}
+
+static int spmi_regulator_single_map_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ return spmi_regulator_select_voltage(vreg, min_uV, max_uV);
+}
+
+static int spmi_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 sel = selector;
+
+ /*
+ * Certain types of regulators do not have a range select register so
+ * only voltage set register needs to be written.
+ */
+ return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &sel, 1);
+}
+
+static int spmi_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 selector;
+ int ret;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &selector, 1);
+ if (ret)
+ return ret;
+
+ return selector;
+}
+
+static int spmi_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 range_sel, voltage_sel;
+
+ ret = spmi_sw_selector_to_hw(vreg, selector, &range_sel, &voltage_sel);
+ if (ret)
+ return ret;
+
+ /*
+ * Calculate VSET based on range
+ * In case of range 0: voltage_sel is a 7 bit value, can be written
+ * witout any modification.
+ * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+ * [011].
+ */
+ if (range_sel == 1)
+ voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_VOLTAGE_SET,
+ voltage_sel, 0xff);
+}
+
+static int spmi_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ if (range->range_sel == 1)
+ voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+ return spmi_hw_selector_to_sw(vreg, voltage_sel, range);
+}
+
+static int spmi_regulator_common_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int uV = 0;
+ int i;
+
+ if (selector >= vreg->set_points->n_voltages)
+ return 0;
+
+ for (i = 0; i < vreg->set_points->count; i++) {
+ if (selector < vreg->set_points->range[i].n_voltages) {
+ uV = selector * vreg->set_points->range[i].step_uV
+ + vreg->set_points->range[i].set_point_min_uV;
+ break;
+ }
+
+ selector -= vreg->set_points->range[i].n_voltages;
+ }
+
+ return uV;
+}
+
+static int
+spmi_regulator_common_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 mask = SPMI_COMMON_MODE_BYPASS_MASK;
+ u8 val = 0;
+
+ if (enable)
+ val = mask;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 val;
+ int ret;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &val, 1);
+ *enable = val & SPMI_COMMON_MODE_BYPASS_MASK;
+
+ return ret;
+}
+
+static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 reg;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &reg, 1);
+
+ if (reg & SPMI_COMMON_MODE_HPM_MASK)
+ return REGULATOR_MODE_NORMAL;
+
+ if (reg & SPMI_COMMON_MODE_AUTO_MASK)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static int
+spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 mask = SPMI_COMMON_MODE_HPM_MASK | SPMI_COMMON_MODE_AUTO_MASK;
+ u8 val = 0;
+
+ if (mode == REGULATOR_MODE_NORMAL)
+ val = SPMI_COMMON_MODE_HPM_MASK;
+ else if (mode == REGULATOR_MODE_FAST)
+ val = SPMI_COMMON_MODE_AUTO_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode;
+
+ if (load_uA >= vreg->hpm_min_load)
+ mode = REGULATOR_MODE_NORMAL;
+ else
+ mode = REGULATOR_MODE_IDLE;
+
+ return spmi_regulator_common_set_mode(rdev, mode);
+}
+
+static int spmi_regulator_common_set_pull_down(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mask = SPMI_COMMON_PULL_DOWN_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_PULL_DOWN,
+ mask, mask);
+}
+
+static int spmi_regulator_common_set_soft_start(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mask = SPMI_LDO_SOFT_START_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_SOFT_START,
+ mask, mask);
+}
+
+static int spmi_regulator_set_ilim(struct regulator_dev *rdev, int ilim_uA)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ enum spmi_regulator_logical_type type = vreg->logical_type;
+ unsigned int current_reg;
+ u8 reg;
+ u8 mask = SPMI_BOOST_CURRENT_LIMIT_MASK |
+ SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+ int max = (SPMI_BOOST_CURRENT_LIMIT_MASK + 1) * 500;
+
+ if (type == SPMI_REGULATOR_LOGICAL_TYPE_BOOST)
+ current_reg = SPMI_BOOST_REG_CURRENT_LIMIT;
+ else
+ current_reg = SPMI_BOOST_BYP_REG_CURRENT_LIMIT;
+
+ if (ilim_uA > max || ilim_uA <= 0)
+ return -EINVAL;
+
+ reg = (ilim_uA - 1) / 500;
+ reg |= SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, current_reg, reg, mask);
+}
+
+static int spmi_regulator_vs_clear_ocp(struct spmi_regulator *vreg)
+{
+ int ret;
+
+ ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+
+ vreg->vs_enable_time = ktime_get();
+
+ ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+
+ return ret;
+}
+
+static void spmi_regulator_vs_ocp_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct spmi_regulator *vreg
+ = container_of(dwork, struct spmi_regulator, ocp_work);
+
+ spmi_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
+{
+ struct spmi_regulator *vreg = data;
+ ktime_t ocp_irq_time;
+ s64 ocp_trigger_delay_us;
+
+ ocp_irq_time = ktime_get();
+ ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+ vreg->vs_enable_time);
+
+ /*
+ * Reset the OCP count if there is a large delay between switch enable
+ * and when OCP triggers. This is indicative of a hotplug event as
+ * opposed to a fault.
+ */
+ if (ocp_trigger_delay_us > SPMI_VS_OCP_FAULT_DELAY_US)
+ vreg->ocp_count = 0;
+
+ /* Wait for switch output to settle back to 0 V after OCP triggered. */
+ udelay(SPMI_VS_OCP_FALL_DELAY_US);
+
+ vreg->ocp_count++;
+
+ if (vreg->ocp_count == 1) {
+ /* Immediately clear the over current condition. */
+ spmi_regulator_vs_clear_ocp(vreg);
+ } else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+ /* Schedule the over current clear task to run later. */
+ schedule_delayed_work(&vreg->ocp_work,
+ msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+ } else {
+ dev_err(vreg->dev,
+ "OCP triggered %d times; no further retries\n",
+ vreg->ocp_count);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#define SAW3_VCTL_DATA_MASK 0xFF
+#define SAW3_VCTL_CLEAR_MASK 0x700FF
+#define SAW3_AVS_CTL_EN_MASK 0x1
+#define SAW3_AVS_CTL_TGGL_MASK 0x8000000
+#define SAW3_AVS_CTL_CLEAR_MASK 0x7efc00
+
+static struct regmap *saw_regmap;
+
+static void spmi_saw_set_vdd(void *data)
+{
+ u32 vctl, data3, avs_ctl, pmic_sts;
+ bool avs_enabled = false;
+ unsigned long timeout;
+ u8 voltage_sel = *(u8 *)data;
+
+ regmap_read(saw_regmap, SAW3_AVS_CTL, &avs_ctl);
+ regmap_read(saw_regmap, SAW3_VCTL, &vctl);
+ regmap_read(saw_regmap, SAW3_SPM_PMIC_DATA_3, &data3);
+
+ /* select the band */
+ vctl &= ~SAW3_VCTL_CLEAR_MASK;
+ vctl |= (u32)voltage_sel;
+
+ data3 &= ~SAW3_VCTL_CLEAR_MASK;
+ data3 |= (u32)voltage_sel;
+
+ /* If AVS is enabled, switch it off during the voltage change */
+ avs_enabled = SAW3_AVS_CTL_EN_MASK & avs_ctl;
+ if (avs_enabled) {
+ avs_ctl &= ~SAW3_AVS_CTL_TGGL_MASK;
+ regmap_write(saw_regmap, SAW3_AVS_CTL, avs_ctl);
+ }
+
+ regmap_write(saw_regmap, SAW3_RST, 1);
+ regmap_write(saw_regmap, SAW3_VCTL, vctl);
+ regmap_write(saw_regmap, SAW3_SPM_PMIC_DATA_3, data3);
+
+ timeout = jiffies + usecs_to_jiffies(100);
+ do {
+ regmap_read(saw_regmap, SAW3_PMIC_STS, &pmic_sts);
+ pmic_sts &= SAW3_VCTL_DATA_MASK;
+ if (pmic_sts == (u32)voltage_sel)
+ break;
+
+ cpu_relax();
+
+ } while (time_before(jiffies, timeout));
+
+ /* After successful voltage change, switch the AVS back on */
+ if (avs_enabled) {
+ pmic_sts &= 0x3f;
+ avs_ctl &= ~SAW3_AVS_CTL_CLEAR_MASK;
+ avs_ctl |= ((pmic_sts - 4) << 10);
+ avs_ctl |= (pmic_sts << 17);
+ avs_ctl |= SAW3_AVS_CTL_TGGL_MASK;
+ regmap_write(saw_regmap, SAW3_AVS_CTL, avs_ctl);
+ }
+}
+
+static int
+spmi_regulator_saw_set_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 range_sel, voltage_sel;
+
+ ret = spmi_sw_selector_to_hw(vreg, selector, &range_sel, &voltage_sel);
+ if (ret)
+ return ret;
+
+ if (0 != range_sel) {
+ dev_dbg(&rdev->dev, "range_sel = %02X voltage_sel = %02X", \
+ range_sel, voltage_sel);
+ return -EINVAL;
+ }
+
+ /* Always do the SAW register writes on the first CPU */
+ return smp_call_function_single(0, spmi_saw_set_vdd, \
+ &voltage_sel, true);
+}
+
+static struct regulator_ops spmi_saw_ops = {};
+
+static struct regulator_ops spmi_smps_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_common_set_voltage,
+ .set_voltage_time_sel = spmi_regulator_set_voltage_time_sel,
+ .get_voltage_sel = spmi_regulator_common_get_voltage,
+ .map_voltage = spmi_regulator_common_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_common_set_voltage,
+ .get_voltage_sel = spmi_regulator_common_get_voltage,
+ .map_voltage = spmi_regulator_common_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_ln_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_common_set_voltage,
+ .get_voltage_sel = spmi_regulator_common_get_voltage,
+ .map_voltage = spmi_regulator_common_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+};
+
+static struct regulator_ops spmi_vs_ops = {
+ .enable = spmi_regulator_vs_enable,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+ .set_over_current_protection = spmi_regulator_vs_ocp,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+};
+
+static struct regulator_ops spmi_boost_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_single_range_set_voltage,
+ .get_voltage_sel = spmi_regulator_single_range_get_voltage,
+ .map_voltage = spmi_regulator_single_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_input_current_limit = spmi_regulator_set_ilim,
+};
+
+static struct regulator_ops spmi_ftsmps_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_common_set_voltage,
+ .set_voltage_time_sel = spmi_regulator_set_voltage_time_sel,
+ .get_voltage_sel = spmi_regulator_common_get_voltage,
+ .map_voltage = spmi_regulator_common_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_lo_smps_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_ult_lo_smps_set_voltage,
+ .set_voltage_time_sel = spmi_regulator_set_voltage_time_sel,
+ .get_voltage_sel = spmi_regulator_ult_lo_smps_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ho_smps_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_single_range_set_voltage,
+ .set_voltage_time_sel = spmi_regulator_set_voltage_time_sel,
+ .get_voltage_sel = spmi_regulator_single_range_get_voltage,
+ .map_voltage = spmi_regulator_single_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = spmi_regulator_single_range_set_voltage,
+ .get_voltage_sel = spmi_regulator_single_range_get_voltage,
+ .map_voltage = spmi_regulator_single_map_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct spmi_regulator_mapping supported_regulators[] = {
+ /* type subtype dig_min dig_max ltype ops setpoints hpm_min */
+ SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000),
+ SPMI_VREG(LDO, N300, 0, INF, LDO, ldo, nldo1, 10000),
+ SPMI_VREG(LDO, N600, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N1200, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N600, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N1200, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N600_ST, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N1200_ST, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N600_ST, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N1200_ST, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, P50, 0, INF, LDO, ldo, pldo, 5000),
+ SPMI_VREG(LDO, P150, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P300, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P600, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P1200, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LN, 0, INF, LN_LDO, ln_ldo, ln_ldo, 0),
+ SPMI_VREG(LDO, LV_P50, 0, INF, LDO, ldo, pldo, 5000),
+ SPMI_VREG(LDO, LV_P150, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P300, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P600, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P1200, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG_VS(LV100, 0, INF),
+ SPMI_VREG_VS(LV300, 0, INF),
+ SPMI_VREG_VS(MV300, 0, INF),
+ SPMI_VREG_VS(MV500, 0, INF),
+ SPMI_VREG_VS(HDMI, 0, INF),
+ SPMI_VREG_VS(OTG, 0, INF),
+ SPMI_VREG(BOOST, 5V_BOOST, 0, INF, BOOST, boost, boost, 0),
+ SPMI_VREG(FTS, FTS_CTL, 0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+ SPMI_VREG(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5, 100000),
+ SPMI_VREG(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+ ult_ho_smps, 100000),
+ SPMI_VREG(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N900_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P150, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P300, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P450, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P600, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P150, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P50, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 5000),
+};
+
+static void spmi_calculate_num_voltages(struct spmi_voltage_set_points *points)
+{
+ unsigned int n;
+ struct spmi_voltage_range *range = points->range;
+
+ for (; range < points->range + points->count; range++) {
+ n = 0;
+ if (range->set_point_max_uV) {
+ n = range->set_point_max_uV - range->set_point_min_uV;
+ n = (n / range->step_uV) + 1;
+ }
+ range->n_voltages = n;
+ points->n_voltages += n;
+ }
+}
+
+static int spmi_regulator_match(struct spmi_regulator *vreg, u16 force_type)
+{
+ const struct spmi_regulator_mapping *mapping;
+ int ret, i;
+ u32 dig_major_rev;
+ u8 version[SPMI_COMMON_REG_SUBTYPE - SPMI_COMMON_REG_DIG_MAJOR_REV + 1];
+ u8 type, subtype;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_DIG_MAJOR_REV, version,
+ ARRAY_SIZE(version));
+ if (ret) {
+ dev_dbg(vreg->dev, "could not read version registers\n");
+ return ret;
+ }
+ dig_major_rev = version[SPMI_COMMON_REG_DIG_MAJOR_REV
+ - SPMI_COMMON_REG_DIG_MAJOR_REV];
+
+ if (!force_type) {
+ type = version[SPMI_COMMON_REG_TYPE -
+ SPMI_COMMON_REG_DIG_MAJOR_REV];
+ subtype = version[SPMI_COMMON_REG_SUBTYPE -
+ SPMI_COMMON_REG_DIG_MAJOR_REV];
+ } else {
+ type = force_type >> 8;
+ subtype = force_type;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+ mapping = &supported_regulators[i];
+ if (mapping->type == type && mapping->subtype == subtype
+ && mapping->revision_min <= dig_major_rev
+ && mapping->revision_max >= dig_major_rev)
+ goto found;
+ }
+
+ dev_err(vreg->dev,
+ "unsupported regulator: name=%s type=0x%02X, subtype=0x%02X, dig major rev=0x%02X\n",
+ vreg->desc.name, type, subtype, dig_major_rev);
+
+ return -ENODEV;
+
+found:
+ vreg->logical_type = mapping->logical_type;
+ vreg->set_points = mapping->set_points;
+ vreg->hpm_min_load = mapping->hpm_min_load;
+ vreg->desc.ops = mapping->ops;
+
+ if (mapping->set_points) {
+ if (!mapping->set_points->n_voltages)
+ spmi_calculate_num_voltages(mapping->set_points);
+ vreg->desc.n_voltages = mapping->set_points->n_voltages;
+ }
+
+ return 0;
+}
+
+static int spmi_regulator_init_slew_rate(struct spmi_regulator *vreg)
+{
+ int ret;
+ u8 reg = 0;
+ int step, delay, slew_rate, step_delay;
+ const struct spmi_voltage_range *range;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
+ if (ret) {
+ dev_err(vreg->dev, "spmi read failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ switch (vreg->logical_type) {
+ case SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS:
+ step_delay = SPMI_FTSMPS_STEP_DELAY;
+ break;
+ default:
+ step_delay = SPMI_DEFAULT_STEP_DELAY;
+ break;
+ }
+
+ step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
+ step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+ delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
+ delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+ /* slew_rate has units of uV/us */
+ slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+ slew_rate /= 1000 * (step_delay << delay);
+ slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
+ slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+
+ /* Ensure that the slew rate is greater than 0 */
+ vreg->slew_rate = max(slew_rate, 1);
+
+ return ret;
+}
+
+static int spmi_regulator_init_registers(struct spmi_regulator *vreg,
+ const struct spmi_regulator_init_data *data)
+{
+ int ret;
+ enum spmi_regulator_logical_type type;
+ u8 ctrl_reg[8], reg, mask;
+
+ type = vreg->logical_type;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8);
+ if (ret)
+ return ret;
+
+ /* Set up enable pin control. */
+ if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS
+ || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO
+ || type == SPMI_REGULATOR_LOGICAL_TYPE_VS)
+ && !(data->pin_ctrl_enable
+ & SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) {
+ ctrl_reg[SPMI_COMMON_IDX_ENABLE] &=
+ ~SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK;
+ ctrl_reg[SPMI_COMMON_IDX_ENABLE] |=
+ data->pin_ctrl_enable & SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK;
+ }
+
+ /* Set up mode pin control. */
+ if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS
+ || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO)
+ && !(data->pin_ctrl_hpm
+ & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+ ctrl_reg[SPMI_COMMON_IDX_MODE] &=
+ ~SPMI_COMMON_MODE_FOLLOW_ALL_MASK;
+ ctrl_reg[SPMI_COMMON_IDX_MODE] |=
+ data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_ALL_MASK;
+ }
+
+ if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS
+ && !(data->pin_ctrl_hpm & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+ ctrl_reg[SPMI_COMMON_IDX_MODE] &=
+ ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+ ctrl_reg[SPMI_COMMON_IDX_MODE] |=
+ data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+ }
+
+ if ((type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS
+ || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS
+ || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO)
+ && !(data->pin_ctrl_hpm
+ & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+ ctrl_reg[SPMI_COMMON_IDX_MODE] &=
+ ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+ ctrl_reg[SPMI_COMMON_IDX_MODE] |=
+ data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+ }
+
+ /* Write back any control register values that were modified. */
+ ret = spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8);
+ if (ret)
+ return ret;
+
+ /* Set soft start strength and over current protection for VS. */
+ if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS) {
+ if (data->vs_soft_start_strength
+ != SPMI_VS_SOFT_START_STR_HW_DEFAULT) {
+ reg = data->vs_soft_start_strength
+ & SPMI_VS_SOFT_START_SEL_MASK;
+ mask = SPMI_VS_SOFT_START_SEL_MASK;
+ return spmi_vreg_update_bits(vreg,
+ SPMI_VS_REG_SOFT_START,
+ reg, mask);
+ }
+ }
+
+ return 0;
+}
+
+static void spmi_regulator_get_dt_config(struct spmi_regulator *vreg,
+ struct device_node *node, struct spmi_regulator_init_data *data)
+{
+ /*
+ * Initialize configuration parameters to use hardware default in case
+ * no value is specified via device tree.
+ */
+ data->pin_ctrl_enable = SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT;
+ data->pin_ctrl_hpm = SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT;
+ data->vs_soft_start_strength = SPMI_VS_SOFT_START_STR_HW_DEFAULT;
+
+ /* These bindings are optional, so it is okay if they aren't found. */
+ of_property_read_u32(node, "qcom,ocp-max-retries",
+ &vreg->ocp_max_retries);
+ of_property_read_u32(node, "qcom,ocp-retry-delay",
+ &vreg->ocp_retry_delay_ms);
+ of_property_read_u32(node, "qcom,pin-ctrl-enable",
+ &data->pin_ctrl_enable);
+ of_property_read_u32(node, "qcom,pin-ctrl-hpm", &data->pin_ctrl_hpm);
+ of_property_read_u32(node, "qcom,vs-soft-start-strength",
+ &data->vs_soft_start_strength);
+}
+
+static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
+{
+ if (mode == 1)
+ return REGULATOR_MODE_NORMAL;
+ if (mode == 2)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_of_parse(struct device_node *node,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct spmi_regulator_init_data data = { };
+ struct spmi_regulator *vreg = config->driver_data;
+ struct device *dev = config->dev;
+ int ret;
+
+ spmi_regulator_get_dt_config(vreg, node, &data);
+
+ if (!vreg->ocp_max_retries)
+ vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+ if (!vreg->ocp_retry_delay_ms)
+ vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+ ret = spmi_regulator_init_registers(vreg, &data);
+ if (ret) {
+ dev_err(dev, "common initialization failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ switch (vreg->logical_type) {
+ case SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS:
+ case SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS:
+ case SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS:
+ case SPMI_REGULATOR_LOGICAL_TYPE_SMPS:
+ ret = spmi_regulator_init_slew_rate(vreg);
+ if (ret)
+ return ret;
+ default:
+ break;
+ }
+
+ if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_VS)
+ vreg->ocp_irq = 0;
+
+ if (vreg->ocp_irq) {
+ ret = devm_request_irq(dev, vreg->ocp_irq,
+ spmi_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+ vreg);
+ if (ret < 0) {
+ dev_err(dev, "failed to request irq %d, ret=%d\n",
+ vreg->ocp_irq, ret);
+ return ret;
+ }
+
+ INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+ }
+
+ return 0;
+}
+
+static const struct spmi_regulator_data pm8941_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0xa000, },
+ { "l1", 0x4000, "vdd_l1_l3", },
+ { "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
+ { "l3", 0x4200, "vdd_l1_l3", },
+ { "l4", 0x4300, "vdd_l4_l11", },
+ { "l5", 0x4400, "vdd_l5_l7", NULL, 0x0410 },
+ { "l6", 0x4500, "vdd_l6_l12_l14_l15", },
+ { "l7", 0x4600, "vdd_l5_l7", NULL, 0x0410 },
+ { "l8", 0x4700, "vdd_l8_l16_l18_19", },
+ { "l9", 0x4800, "vdd_l9_l10_l17_l22", },
+ { "l10", 0x4900, "vdd_l9_l10_l17_l22", },
+ { "l11", 0x4a00, "vdd_l4_l11", },
+ { "l12", 0x4b00, "vdd_l6_l12_l14_l15", },
+ { "l13", 0x4c00, "vdd_l13_l20_l23_l24", },
+ { "l14", 0x4d00, "vdd_l6_l12_l14_l15", },
+ { "l15", 0x4e00, "vdd_l6_l12_l14_l15", },
+ { "l16", 0x4f00, "vdd_l8_l16_l18_19", },
+ { "l17", 0x5000, "vdd_l9_l10_l17_l22", },
+ { "l18", 0x5100, "vdd_l8_l16_l18_19", },
+ { "l19", 0x5200, "vdd_l8_l16_l18_19", },
+ { "l20", 0x5300, "vdd_l13_l20_l23_l24", },
+ { "l21", 0x5400, "vdd_l21", },
+ { "l22", 0x5500, "vdd_l9_l10_l17_l22", },
+ { "l23", 0x5600, "vdd_l13_l20_l23_l24", },
+ { "l24", 0x5700, "vdd_l13_l20_l23_l24", },
+ { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
+ { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
+ { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
+ { "5vs1", 0x8300, "vin_5vs", "ocp-5vs1", },
+ { "5vs2", 0x8400, "vin_5vs", "ocp-5vs2", },
+ { }
+};
+
+static const struct spmi_regulator_data pm8841_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", NULL, 0x1c08 },
+ { "s5", 0x2000, "vdd_s5", NULL, 0x1c08 },
+ { "s6", 0x2300, "vdd_s6", NULL, 0x1c08 },
+ { "s7", 0x2600, "vdd_s7", NULL, 0x1c08 },
+ { "s8", 0x2900, "vdd_s8", NULL, 0x1c08 },
+ { }
+};
+
+static const struct spmi_regulator_data pm8916_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", },
+ { "l1", 0x4000, "vdd_l1_l3", },
+ { "l2", 0x4100, "vdd_l2", },
+ { "l3", 0x4200, "vdd_l1_l3", },
+ { "l4", 0x4300, "vdd_l4_l5_l6", },
+ { "l5", 0x4400, "vdd_l4_l5_l6", },
+ { "l6", 0x4500, "vdd_l4_l5_l6", },
+ { "l7", 0x4600, "vdd_l7", },
+ { "l8", 0x4700, "vdd_l8_l11_l14_l15_l16", },
+ { "l9", 0x4800, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l10", 0x4900, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l11", 0x4a00, "vdd_l8_l11_l14_l15_l16", },
+ { "l12", 0x4b00, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l13", 0x4c00, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l14", 0x4d00, "vdd_l8_l11_l14_l15_l16", },
+ { "l15", 0x4e00, "vdd_l8_l11_l14_l15_l16", },
+ { "l16", 0x4f00, "vdd_l8_l11_l14_l15_l16", },
+ { "l17", 0x5000, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l18", 0x5100, "vdd_l9_l10_l12_l13_l17_l18", },
+ { }
+};
+
+static const struct spmi_regulator_data pm8994_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", },
+ { "s5", 0x2000, "vdd_s5", },
+ { "s6", 0x2300, "vdd_s6", },
+ { "s7", 0x2600, "vdd_s7", },
+ { "s8", 0x2900, "vdd_s8", },
+ { "s9", 0x2c00, "vdd_s9", },
+ { "s10", 0x2f00, "vdd_s10", },
+ { "s11", 0x3200, "vdd_s11", },
+ { "s12", 0x3500, "vdd_s12", },
+ { "l1", 0x4000, "vdd_l1", },
+ { "l2", 0x4100, "vdd_l2_l26_l28", },
+ { "l3", 0x4200, "vdd_l3_l11", },
+ { "l4", 0x4300, "vdd_l4_l27_l31", },
+ { "l5", 0x4400, "vdd_l5_l7", },
+ { "l6", 0x4500, "vdd_l6_l12_l32", },
+ { "l7", 0x4600, "vdd_l5_l7", },
+ { "l8", 0x4700, "vdd_l8_l16_l30", },
+ { "l9", 0x4800, "vdd_l9_l10_l18_l22", },
+ { "l10", 0x4900, "vdd_l9_l10_l18_l22", },
+ { "l11", 0x4a00, "vdd_l3_l11", },
+ { "l12", 0x4b00, "vdd_l6_l12_l32", },
+ { "l13", 0x4c00, "vdd_l13_l19_l23_l24", },
+ { "l14", 0x4d00, "vdd_l14_l15", },
+ { "l15", 0x4e00, "vdd_l14_l15", },
+ { "l16", 0x4f00, "vdd_l8_l16_l30", },
+ { "l17", 0x5000, "vdd_l17_l29", },
+ { "l18", 0x5100, "vdd_l9_l10_l18_l22", },
+ { "l19", 0x5200, "vdd_l13_l19_l23_l24", },
+ { "l20", 0x5300, "vdd_l20_l21", },
+ { "l21", 0x5400, "vdd_l20_l21", },
+ { "l22", 0x5500, "vdd_l9_l10_l18_l22", },
+ { "l23", 0x5600, "vdd_l13_l19_l23_l24", },
+ { "l24", 0x5700, "vdd_l13_l19_l23_l24", },
+ { "l25", 0x5800, "vdd_l25", },
+ { "l26", 0x5900, "vdd_l2_l26_l28", },
+ { "l27", 0x5a00, "vdd_l4_l27_l31", },
+ { "l28", 0x5b00, "vdd_l2_l26_l28", },
+ { "l29", 0x5c00, "vdd_l17_l29", },
+ { "l30", 0x5d00, "vdd_l8_l16_l30", },
+ { "l31", 0x5e00, "vdd_l4_l27_l31", },
+ { "l32", 0x5f00, "vdd_l6_l12_l32", },
+ { "lvs1", 0x8000, "vdd_lvs_1_2", },
+ { "lvs2", 0x8100, "vdd_lvs_1_2", },
+ { }
+};
+
+static const struct spmi_regulator_data pmi8994_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "l1", 0x4000, "vdd_l1", },
+ { }
+};
+
+static const struct of_device_id qcom_spmi_regulator_match[] = {
+ { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
+ { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
+ { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
+ { .compatible = "qcom,pm8994-regulators", .data = &pm8994_regulators },
+ { .compatible = "qcom,pmi8994-regulators", .data = &pmi8994_regulators },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
+
+static int qcom_spmi_regulator_probe(struct platform_device *pdev)
+{
+ const struct spmi_regulator_data *reg;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct spmi_regulator *vreg;
+ struct regmap *regmap;
+ const char *name;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *syscon, *reg_node;
+ struct property *reg_prop;
+ int ret, lenp;
+ struct list_head *vreg_list;
+
+ vreg_list = devm_kzalloc(dev, sizeof(*vreg_list), GFP_KERNEL);
+ if (!vreg_list)
+ return -ENOMEM;
+ INIT_LIST_HEAD(vreg_list);
+ platform_set_drvdata(pdev, vreg_list);
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ match = of_match_device(qcom_spmi_regulator_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ if (of_find_property(node, "qcom,saw-reg", &lenp)) {
+ syscon = of_parse_phandle(node, "qcom,saw-reg", 0);
+ saw_regmap = syscon_node_to_regmap(syscon);
+ of_node_put(syscon);
+ if (IS_ERR(saw_regmap))
+ dev_err(dev, "ERROR reading SAW regmap\n");
+ }
+
+ for (reg = match->data; reg->name; reg++) {
+
+ if (saw_regmap) {
+ reg_node = of_get_child_by_name(node, reg->name);
+ reg_prop = of_find_property(reg_node, "qcom,saw-slave",
+ &lenp);
+ of_node_put(reg_node);
+ if (reg_prop)
+ continue;
+ }
+
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+
+ vreg->dev = dev;
+ vreg->base = reg->base;
+ vreg->regmap = regmap;
+ if (reg->ocp) {
+ vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
+ if (vreg->ocp_irq < 0) {
+ ret = vreg->ocp_irq;
+ goto err;
+ }
+ }
+ vreg->desc.id = -1;
+ vreg->desc.owner = THIS_MODULE;
+ vreg->desc.type = REGULATOR_VOLTAGE;
+ vreg->desc.enable_reg = reg->base + SPMI_COMMON_REG_ENABLE;
+ vreg->desc.enable_mask = SPMI_COMMON_ENABLE_MASK;
+ vreg->desc.enable_val = SPMI_COMMON_ENABLE;
+ vreg->desc.name = name = reg->name;
+ vreg->desc.supply_name = reg->supply;
+ vreg->desc.of_match = reg->name;
+ vreg->desc.of_parse_cb = spmi_regulator_of_parse;
+ vreg->desc.of_map_mode = spmi_regulator_of_map_mode;
+
+ ret = spmi_regulator_match(vreg, reg->force_type);
+ if (ret)
+ continue;
+
+ if (saw_regmap) {
+ reg_node = of_get_child_by_name(node, reg->name);
+ reg_prop = of_find_property(reg_node, "qcom,saw-leader",
+ &lenp);
+ of_node_put(reg_node);
+ if (reg_prop) {
+ spmi_saw_ops = *(vreg->desc.ops);
+ spmi_saw_ops.set_voltage_sel =
+ spmi_regulator_saw_set_voltage;
+ vreg->desc.ops = &spmi_saw_ops;
+ }
+ }
+
+ config.dev = dev;
+ config.driver_data = vreg;
+ config.regmap = regmap;
+ rdev = devm_regulator_register(dev, &vreg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n", name);
+ ret = PTR_ERR(rdev);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&vreg->node);
+ list_add(&vreg->node, vreg_list);
+ }
+
+ return 0;
+
+err:
+ list_for_each_entry(vreg, vreg_list, node)
+ if (vreg->ocp_irq)
+ cancel_delayed_work_sync(&vreg->ocp_work);
+ return ret;
+}
+
+static int qcom_spmi_regulator_remove(struct platform_device *pdev)
+{
+ struct spmi_regulator *vreg;
+ struct list_head *vreg_list = platform_get_drvdata(pdev);
+
+ list_for_each_entry(vreg, vreg_list, node)
+ if (vreg->ocp_irq)
+ cancel_delayed_work_sync(&vreg->ocp_work);
+
+ return 0;
+}
+
+static struct platform_driver qcom_spmi_regulator_driver = {
+ .driver = {
+ .name = "qcom-spmi-regulator",
+ .of_match_table = qcom_spmi_regulator_match,
+ },
+ .probe = qcom_spmi_regulator_probe,
+ .remove = qcom_spmi_regulator_remove,
+};
+module_platform_driver(qcom_spmi_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-spmi-regulator");
diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c
new file mode 100644
index 000000000..2ec51af43
--- /dev/null
+++ b/drivers/regulator/rc5t583-regulator.c
@@ -0,0 +1,203 @@
+/*
+ * Regulator driver for RICOH RC5T583 power management chip.
+ *
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+#include <linux/mfd/rc5t583.h>
+
+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 const 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_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_kcalloc(&pdev->dev,
+ RC5T583_REGULATOR_MAX,
+ sizeof(struct rc5t583_regulator),
+ GFP_KERNEL);
+ if (!regs)
+ return -ENOMEM;
+
+
+ for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) {
+ reg = &regs[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 = pdata->reg_init_data[id];
+ config.driver_data = reg;
+ config.regmap = rc5t583->regmap;
+
+ rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator %s\n",
+ ri->desc.name);
+ return PTR_ERR(rdev);
+ }
+ reg->rdev = rdev;
+ }
+ platform_set_drvdata(pdev, regs);
+ return 0;
+}
+
+static struct platform_driver rc5t583_regulator_driver = {
+ .driver = {
+ .name = "rc5t583-regulator",
+ },
+ .probe = rc5t583_regulator_probe,
+};
+
+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 <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("RC5T583 regulator driver");
+MODULE_ALIAS("platform:rc5t583-regulator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
new file mode 100644
index 000000000..92498ac50
--- /dev/null
+++ b/drivers/regulator/rk808-regulator.c
@@ -0,0 +1,804 @@
+/*
+ * Regulator driver for Rockchip RK808/RK818
+ *
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ * Author: Zhang Qing <zhangqing@rock-chips.com>
+ *
+ * Copyright (C) 2016 PHYTEC Messtechnik GmbH
+ *
+ * Author: Wadim Egorov <w.egorov@phytec.de>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/rk808.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/gpio/consumer.h>
+
+/* Field Definitions */
+#define RK808_BUCK_VSEL_MASK 0x3f
+#define RK808_BUCK4_VSEL_MASK 0xf
+#define RK808_LDO_VSEL_MASK 0x1f
+
+#define RK818_BUCK_VSEL_MASK 0x3f
+#define RK818_BUCK4_VSEL_MASK 0x1f
+#define RK818_LDO_VSEL_MASK 0x1f
+#define RK818_LDO3_ON_VSEL_MASK 0xf
+#define RK818_BOOST_ON_VSEL_MASK 0xe0
+
+/* Ramp rate definitions for buck1 / buck2 only */
+#define RK808_RAMP_RATE_OFFSET 3
+#define RK808_RAMP_RATE_MASK (3 << RK808_RAMP_RATE_OFFSET)
+#define RK808_RAMP_RATE_2MV_PER_US (0 << RK808_RAMP_RATE_OFFSET)
+#define RK808_RAMP_RATE_4MV_PER_US (1 << RK808_RAMP_RATE_OFFSET)
+#define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET)
+#define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET)
+
+#define RK808_DVS2_POL BIT(2)
+#define RK808_DVS1_POL BIT(1)
+
+/* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */
+#define RK808_SLP_REG_OFFSET 1
+
+/* Offset from XXX_ON_VSEL to XXX_DVS_VSEL */
+#define RK808_DVS_REG_OFFSET 2
+
+/* Offset from XXX_EN_REG to SLEEP_SET_OFF_XXX */
+#define RK808_SLP_SET_OFF_REG_OFFSET 2
+
+/* max steps for increase voltage of Buck1/2, equal 100mv*/
+#define MAX_STEPS_ONE_TIME 8
+
+#define RK805_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _etime) \
+ [_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = (_id), \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .enable_time = (_etime), \
+ .ops = &rk805_reg_ops, \
+ }
+
+#define RK8XX_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _etime) \
+ [_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = (_id), \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .enable_time = (_etime), \
+ .ops = &rk808_reg_ops, \
+ }
+
+#define RK8XX_DESC_SWITCH(_id, _match, _supply, _ereg, _emask) \
+ [_id] = { \
+ .name = (_match), \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = (_id), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .owner = THIS_MODULE, \
+ .ops = &rk808_switch_ops \
+ }
+
+
+struct rk808_regulator_data {
+ struct gpio_desc *dvs_gpio[2];
+};
+
+static const int rk808_buck_config_regs[] = {
+ RK808_BUCK1_CONFIG_REG,
+ RK808_BUCK2_CONFIG_REG,
+ RK808_BUCK3_CONFIG_REG,
+ RK808_BUCK4_CONFIG_REG,
+};
+
+static const struct regulator_linear_range rk808_ldo3_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0, 13, 100000),
+ REGULATOR_LINEAR_RANGE(2500000, 15, 15, 0),
+};
+
+static int rk808_buck1_2_get_voltage_sel_regmap(struct regulator_dev *rdev)
+{
+ struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct gpio_desc *gpio = pdata->dvs_gpio[id];
+ unsigned int val;
+ int ret;
+
+ if (!gpio || gpiod_get_value(gpio) == 0)
+ return regulator_get_voltage_sel_regmap(rdev);
+
+ ret = regmap_read(rdev->regmap,
+ rdev->desc->vsel_reg + RK808_DVS_REG_OFFSET,
+ &val);
+ if (ret != 0)
+ return ret;
+
+ val &= rdev->desc->vsel_mask;
+ val >>= ffs(rdev->desc->vsel_mask) - 1;
+
+ return val;
+}
+
+static int rk808_buck1_2_i2c_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned sel)
+{
+ int ret, delta_sel;
+ unsigned int old_sel, tmp, val, mask = rdev->desc->vsel_mask;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ tmp = val & ~mask;
+ old_sel = val & mask;
+ old_sel >>= ffs(mask) - 1;
+ delta_sel = sel - old_sel;
+
+ /*
+ * If directly modify the register to change the voltage, we will face
+ * the risk of overshoot. Put it into a multi-step, can effectively
+ * avoid this problem, a step is 100mv here.
+ */
+ while (delta_sel > MAX_STEPS_ONE_TIME) {
+ old_sel += MAX_STEPS_ONE_TIME;
+ val = old_sel << (ffs(mask) - 1);
+ val |= tmp;
+
+ /*
+ * i2c is 400kHz (2.5us per bit) and we must transmit _at least_
+ * 3 bytes (24 bits) plus start and stop so 26 bits. So we've
+ * got more than 65 us between each voltage change and thus
+ * won't ramp faster than ~1500 uV / us.
+ */
+ ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, val);
+ delta_sel = sel - old_sel;
+ }
+
+ sel <<= ffs(mask) - 1;
+ val = tmp | sel;
+ ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, val);
+
+ /*
+ * When we change the voltage register directly, the ramp rate is about
+ * 100000uv/us, wait 1us to make sure the target voltage to be stable,
+ * so we needn't wait extra time after that.
+ */
+ udelay(1);
+
+ return ret;
+}
+
+static int rk808_buck1_2_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned sel)
+{
+ struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct gpio_desc *gpio = pdata->dvs_gpio[id];
+ unsigned int reg = rdev->desc->vsel_reg;
+ unsigned old_sel;
+ int ret, gpio_level;
+
+ if (!gpio)
+ return rk808_buck1_2_i2c_set_voltage_sel(rdev, sel);
+
+ gpio_level = gpiod_get_value(gpio);
+ if (gpio_level == 0) {
+ reg += RK808_DVS_REG_OFFSET;
+ ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &old_sel);
+ } else {
+ ret = regmap_read(rdev->regmap,
+ reg + RK808_DVS_REG_OFFSET,
+ &old_sel);
+ }
+
+ if (ret != 0)
+ return ret;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+ sel |= old_sel & ~rdev->desc->vsel_mask;
+
+ ret = regmap_write(rdev->regmap, reg, sel);
+ if (ret)
+ return ret;
+
+ gpiod_set_value(gpio, !gpio_level);
+
+ return ret;
+}
+
+static int rk808_buck1_2_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector,
+ unsigned int new_selector)
+{
+ struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct gpio_desc *gpio = pdata->dvs_gpio[id];
+
+ /* if there is no dvs1/2 pin, we don't need wait extra time here. */
+ if (!gpio)
+ return 0;
+
+ return regulator_set_voltage_time_sel(rdev, old_selector, new_selector);
+}
+
+static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ unsigned int ramp_value = RK808_RAMP_RATE_10MV_PER_US;
+ unsigned int reg = rk808_buck_config_regs[rdev_get_id(rdev)];
+
+ switch (ramp_delay) {
+ case 1 ... 2000:
+ ramp_value = RK808_RAMP_RATE_2MV_PER_US;
+ break;
+ case 2001 ... 4000:
+ ramp_value = RK808_RAMP_RATE_4MV_PER_US;
+ break;
+ case 4001 ... 6000:
+ ramp_value = RK808_RAMP_RATE_6MV_PER_US;
+ break;
+ case 6001 ... 10000:
+ break;
+ default:
+ pr_warn("%s ramp_delay: %d not supported, setting 10000\n",
+ rdev->desc->name, ramp_delay);
+ }
+
+ return regmap_update_bits(rdev->regmap, reg,
+ RK808_RAMP_RATE_MASK, ramp_value);
+}
+
+static int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ unsigned int reg;
+ int sel = regulator_map_voltage_linear(rdev, uv, uv);
+
+ if (sel < 0)
+ return -EINVAL;
+
+ reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->vsel_mask,
+ sel);
+}
+
+static int rk808_set_suspend_voltage_range(struct regulator_dev *rdev, int uv)
+{
+ unsigned int reg;
+ int sel = regulator_map_voltage_linear_range(rdev, uv, uv);
+
+ if (sel < 0)
+ return -EINVAL;
+
+ reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->vsel_mask,
+ sel);
+}
+
+static int rk805_set_suspend_enable(struct regulator_dev *rdev)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->enable_mask,
+ rdev->desc->enable_mask);
+}
+
+static int rk805_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->enable_mask,
+ 0);
+}
+
+static int rk808_set_suspend_enable(struct regulator_dev *rdev)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->enable_mask,
+ 0);
+}
+
+static int rk808_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->enable_mask,
+ rdev->desc->enable_mask);
+}
+
+static struct regulator_ops rk805_reg_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,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_voltage = rk808_set_suspend_voltage,
+ .set_suspend_enable = rk805_set_suspend_enable,
+ .set_suspend_disable = rk805_set_suspend_disable,
+};
+
+static struct regulator_ops rk805_switch_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_enable = rk805_set_suspend_enable,
+ .set_suspend_disable = rk805_set_suspend_disable,
+};
+
+static struct regulator_ops rk808_buck1_2_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = rk808_buck1_2_get_voltage_sel_regmap,
+ .set_voltage_sel = rk808_buck1_2_set_voltage_sel,
+ .set_voltage_time_sel = rk808_buck1_2_set_voltage_time_sel,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_ramp_delay = rk808_set_ramp_delay,
+ .set_suspend_voltage = rk808_set_suspend_voltage,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
+};
+
+static struct regulator_ops rk808_reg_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,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_voltage = rk808_set_suspend_voltage,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
+};
+
+static struct regulator_ops rk808_reg_ops_ranges = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_voltage = rk808_set_suspend_voltage_range,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
+};
+
+static struct regulator_ops rk808_switch_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
+};
+
+static const struct regulator_desc rk805_reg[] = {
+ {
+ .name = "DCDC_REG1",
+ .supply_name = "vcc1",
+ .of_match = of_match_ptr("DCDC_REG1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK805_ID_DCDC1,
+ .ops = &rk805_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK805_BUCK1_ON_VSEL_REG,
+ .vsel_mask = RK818_BUCK_VSEL_MASK,
+ .enable_reg = RK805_DCDC_EN_REG,
+ .enable_mask = BIT(0),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG2",
+ .supply_name = "vcc2",
+ .of_match = of_match_ptr("DCDC_REG2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK805_ID_DCDC2,
+ .ops = &rk805_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK805_BUCK2_ON_VSEL_REG,
+ .vsel_mask = RK818_BUCK_VSEL_MASK,
+ .enable_reg = RK805_DCDC_EN_REG,
+ .enable_mask = BIT(1),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG3",
+ .supply_name = "vcc3",
+ .of_match = of_match_ptr("DCDC_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK805_ID_DCDC3,
+ .ops = &rk805_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 1,
+ .enable_reg = RK805_DCDC_EN_REG,
+ .enable_mask = BIT(2),
+ .owner = THIS_MODULE,
+ },
+
+ RK805_DESC(RK805_ID_DCDC4, "DCDC_REG4", "vcc4", 800, 3400, 100,
+ RK805_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK,
+ RK805_DCDC_EN_REG, BIT(3), 0),
+
+ RK805_DESC(RK805_ID_LDO1, "LDO_REG1", "vcc5", 800, 3400, 100,
+ RK805_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK805_LDO_EN_REG,
+ BIT(0), 400),
+ RK805_DESC(RK805_ID_LDO2, "LDO_REG2", "vcc5", 800, 3400, 100,
+ RK805_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK805_LDO_EN_REG,
+ BIT(1), 400),
+ RK805_DESC(RK805_ID_LDO3, "LDO_REG3", "vcc6", 800, 3400, 100,
+ RK805_LDO3_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK805_LDO_EN_REG,
+ BIT(2), 400),
+};
+
+static const struct regulator_desc rk808_reg[] = {
+ {
+ .name = "DCDC_REG1",
+ .supply_name = "vcc1",
+ .of_match = of_match_ptr("DCDC_REG1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK808_ID_DCDC1,
+ .ops = &rk808_buck1_2_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK808_BUCK1_ON_VSEL_REG,
+ .vsel_mask = RK808_BUCK_VSEL_MASK,
+ .enable_reg = RK808_DCDC_EN_REG,
+ .enable_mask = BIT(0),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG2",
+ .supply_name = "vcc2",
+ .of_match = of_match_ptr("DCDC_REG2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK808_ID_DCDC2,
+ .ops = &rk808_buck1_2_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK808_BUCK2_ON_VSEL_REG,
+ .vsel_mask = RK808_BUCK_VSEL_MASK,
+ .enable_reg = RK808_DCDC_EN_REG,
+ .enable_mask = BIT(1),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG3",
+ .supply_name = "vcc3",
+ .of_match = of_match_ptr("DCDC_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK808_ID_DCDC3,
+ .ops = &rk808_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 1,
+ .enable_reg = RK808_DCDC_EN_REG,
+ .enable_mask = BIT(2),
+ .owner = THIS_MODULE,
+ },
+ RK8XX_DESC(RK808_ID_DCDC4, "DCDC_REG4", "vcc4", 1800, 3300, 100,
+ RK808_BUCK4_ON_VSEL_REG, RK808_BUCK4_VSEL_MASK,
+ RK808_DCDC_EN_REG, BIT(3), 0),
+ RK8XX_DESC(RK808_ID_LDO1, "LDO_REG1", "vcc6", 1800, 3400, 100,
+ RK808_LDO1_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(0), 400),
+ RK8XX_DESC(RK808_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
+ RK808_LDO2_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(1), 400),
+ {
+ .name = "LDO_REG3",
+ .supply_name = "vcc7",
+ .of_match = of_match_ptr("LDO_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK808_ID_LDO3,
+ .ops = &rk808_reg_ops_ranges,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 16,
+ .linear_ranges = rk808_ldo3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk808_ldo3_voltage_ranges),
+ .vsel_reg = RK808_LDO3_ON_VSEL_REG,
+ .vsel_mask = RK808_BUCK4_VSEL_MASK,
+ .enable_reg = RK808_LDO_EN_REG,
+ .enable_mask = BIT(2),
+ .enable_time = 400,
+ .owner = THIS_MODULE,
+ },
+ RK8XX_DESC(RK808_ID_LDO4, "LDO_REG4", "vcc9", 1800, 3400, 100,
+ RK808_LDO4_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(3), 400),
+ RK8XX_DESC(RK808_ID_LDO5, "LDO_REG5", "vcc9", 1800, 3400, 100,
+ RK808_LDO5_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(4), 400),
+ RK8XX_DESC(RK808_ID_LDO6, "LDO_REG6", "vcc10", 800, 2500, 100,
+ RK808_LDO6_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(5), 400),
+ RK8XX_DESC(RK808_ID_LDO7, "LDO_REG7", "vcc7", 800, 2500, 100,
+ RK808_LDO7_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(6), 400),
+ RK8XX_DESC(RK808_ID_LDO8, "LDO_REG8", "vcc11", 1800, 3400, 100,
+ RK808_LDO8_ON_VSEL_REG, RK808_LDO_VSEL_MASK, RK808_LDO_EN_REG,
+ BIT(7), 400),
+ RK8XX_DESC_SWITCH(RK808_ID_SWITCH1, "SWITCH_REG1", "vcc8",
+ RK808_DCDC_EN_REG, BIT(5)),
+ RK8XX_DESC_SWITCH(RK808_ID_SWITCH2, "SWITCH_REG2", "vcc12",
+ RK808_DCDC_EN_REG, BIT(6)),
+};
+
+static const struct regulator_desc rk818_reg[] = {
+ {
+ .name = "DCDC_REG1",
+ .supply_name = "vcc1",
+ .of_match = of_match_ptr("DCDC_REG1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_DCDC1,
+ .ops = &rk808_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK818_BUCK1_ON_VSEL_REG,
+ .vsel_mask = RK818_BUCK_VSEL_MASK,
+ .enable_reg = RK818_DCDC_EN_REG,
+ .enable_mask = BIT(0),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG2",
+ .supply_name = "vcc2",
+ .of_match = of_match_ptr("DCDC_REG2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_DCDC2,
+ .ops = &rk808_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 712500,
+ .uV_step = 12500,
+ .n_voltages = 64,
+ .vsel_reg = RK818_BUCK2_ON_VSEL_REG,
+ .vsel_mask = RK818_BUCK_VSEL_MASK,
+ .enable_reg = RK818_DCDC_EN_REG,
+ .enable_mask = BIT(1),
+ .owner = THIS_MODULE,
+ }, {
+ .name = "DCDC_REG3",
+ .supply_name = "vcc3",
+ .of_match = of_match_ptr("DCDC_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_DCDC3,
+ .ops = &rk808_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 1,
+ .enable_reg = RK818_DCDC_EN_REG,
+ .enable_mask = BIT(2),
+ .owner = THIS_MODULE,
+ },
+ RK8XX_DESC(RK818_ID_DCDC4, "DCDC_REG4", "vcc4", 1800, 3600, 100,
+ RK818_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK,
+ RK818_DCDC_EN_REG, BIT(3), 0),
+ RK8XX_DESC(RK818_ID_BOOST, "DCDC_BOOST", "boost", 4700, 5400, 100,
+ RK818_BOOST_LDO9_ON_VSEL_REG, RK818_BOOST_ON_VSEL_MASK,
+ RK818_DCDC_EN_REG, BIT(4), 0),
+ RK8XX_DESC(RK818_ID_LDO1, "LDO_REG1", "vcc6", 1800, 3400, 100,
+ RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(0), 400),
+ RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
+ RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(1), 400),
+ {
+ .name = "LDO_REG3",
+ .supply_name = "vcc7",
+ .of_match = of_match_ptr("LDO_REG3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RK818_ID_LDO3,
+ .ops = &rk808_reg_ops_ranges,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 16,
+ .linear_ranges = rk808_ldo3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(rk808_ldo3_voltage_ranges),
+ .vsel_reg = RK818_LDO3_ON_VSEL_REG,
+ .vsel_mask = RK818_LDO3_ON_VSEL_MASK,
+ .enable_reg = RK818_LDO_EN_REG,
+ .enable_mask = BIT(2),
+ .enable_time = 400,
+ .owner = THIS_MODULE,
+ },
+ RK8XX_DESC(RK818_ID_LDO4, "LDO_REG4", "vcc8", 1800, 3400, 100,
+ RK818_LDO4_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(3), 400),
+ RK8XX_DESC(RK818_ID_LDO5, "LDO_REG5", "vcc7", 1800, 3400, 100,
+ RK818_LDO5_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(4), 400),
+ RK8XX_DESC(RK818_ID_LDO6, "LDO_REG6", "vcc8", 800, 2500, 100,
+ RK818_LDO6_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(5), 400),
+ RK8XX_DESC(RK818_ID_LDO7, "LDO_REG7", "vcc7", 800, 2500, 100,
+ RK818_LDO7_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(6), 400),
+ RK8XX_DESC(RK818_ID_LDO8, "LDO_REG8", "vcc8", 1800, 3400, 100,
+ RK818_LDO8_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ BIT(7), 400),
+ RK8XX_DESC(RK818_ID_LDO9, "LDO_REG9", "vcc9", 1800, 3400, 100,
+ RK818_BOOST_LDO9_ON_VSEL_REG, RK818_LDO_VSEL_MASK,
+ RK818_DCDC_EN_REG, BIT(5), 400),
+ RK8XX_DESC_SWITCH(RK818_ID_SWITCH, "SWITCH_REG", "vcc9",
+ RK818_DCDC_EN_REG, BIT(6)),
+ RK8XX_DESC_SWITCH(RK818_ID_HDMI_SWITCH, "HDMI_SWITCH", "h_5v",
+ RK818_H5V_EN_REG, BIT(0)),
+ RK8XX_DESC_SWITCH(RK818_ID_OTG_SWITCH, "OTG_SWITCH", "usb",
+ RK818_DCDC_EN_REG, BIT(7)),
+};
+
+static int rk808_regulator_dt_parse_pdata(struct device *dev,
+ struct device *client_dev,
+ struct regmap *map,
+ struct rk808_regulator_data *pdata)
+{
+ struct device_node *np;
+ int tmp, ret = 0, i;
+
+ np = of_get_child_by_name(client_dev->of_node, "regulators");
+ if (!np)
+ return -ENXIO;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dvs_gpio); i++) {
+ pdata->dvs_gpio[i] =
+ devm_gpiod_get_index_optional(client_dev, "dvs", i,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(pdata->dvs_gpio[i])) {
+ ret = PTR_ERR(pdata->dvs_gpio[i]);
+ dev_err(dev, "failed to get dvs%d gpio (%d)\n", i, ret);
+ goto dt_parse_end;
+ }
+
+ if (!pdata->dvs_gpio[i]) {
+ dev_info(dev, "there is no dvs%d gpio\n", i);
+ continue;
+ }
+
+ tmp = i ? RK808_DVS2_POL : RK808_DVS1_POL;
+ ret = regmap_update_bits(map, RK808_IO_POL_REG, tmp,
+ gpiod_is_active_low(pdata->dvs_gpio[i]) ?
+ 0 : tmp);
+ }
+
+dt_parse_end:
+ of_node_put(np);
+ return ret;
+}
+
+static int rk808_regulator_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct i2c_client *client = rk808->i2c;
+ struct regulator_config config = {};
+ struct regulator_dev *rk808_rdev;
+ struct rk808_regulator_data *pdata;
+ const struct regulator_desc *regulators;
+ int ret, i, nregulators;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = rk808_regulator_dt_parse_pdata(&pdev->dev, &client->dev,
+ rk808->regmap, pdata);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, pdata);
+
+ switch (rk808->variant) {
+ case RK805_ID:
+ regulators = rk805_reg;
+ nregulators = RK805_NUM_REGULATORS;
+ break;
+ case RK808_ID:
+ regulators = rk808_reg;
+ nregulators = RK808_NUM_REGULATORS;
+ break;
+ case RK818_ID:
+ regulators = rk818_reg;
+ nregulators = RK818_NUM_REGULATORS;
+ break;
+ default:
+ dev_err(&client->dev, "unsupported RK8XX ID %lu\n",
+ rk808->variant);
+ return -EINVAL;
+ }
+
+ config.dev = &client->dev;
+ config.driver_data = pdata;
+ config.regmap = rk808->regmap;
+
+ /* Instantiate the regulators */
+ for (i = 0; i < nregulators; i++) {
+ rk808_rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(rk808_rdev)) {
+ dev_err(&client->dev,
+ "failed to register %d regulator\n", i);
+ return PTR_ERR(rk808_rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver rk808_regulator_driver = {
+ .probe = rk808_regulator_probe,
+ .driver = {
+ .name = "rk808-regulator"
+ },
+};
+
+module_platform_driver(rk808_regulator_driver);
+
+MODULE_DESCRIPTION("regulator driver for the RK808/RK818 series PMICs");
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
+MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rk808-regulator");
diff --git a/drivers/regulator/rn5t618-regulator.c b/drivers/regulator/rn5t618-regulator.c
new file mode 100644
index 000000000..40b74648b
--- /dev/null
+++ b/drivers/regulator/rn5t618-regulator.c
@@ -0,0 +1,160 @@
+/*
+ * Regulator driver for Ricoh RN5T618 PMIC
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/mfd/rn5t618.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+static const struct regulator_ops rn5t618_reg_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+#define REG(rid, ereg, emask, vreg, vmask, min, max, step) \
+ { \
+ .name = #rid, \
+ .of_match = of_match_ptr(#rid), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = RN5T618_##rid, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .ops = &rn5t618_reg_ops, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .enable_reg = RN5T618_##ereg, \
+ .enable_mask = (emask), \
+ .vsel_reg = RN5T618_##vreg, \
+ .vsel_mask = (vmask), \
+ }
+
+static struct regulator_desc rn5t567_regulators[] = {
+ /* DCDC */
+ REG(DCDC1, DC1CTL, BIT(0), DC1DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC2, DC2CTL, BIT(0), DC2DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC3, DC3CTL, BIT(0), DC3DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC4, DC4CTL, BIT(0), DC4DAC, 0xff, 600000, 3500000, 12500),
+ /* LDO */
+ REG(LDO1, LDOEN1, BIT(0), LDO1DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO2, LDOEN1, BIT(1), LDO2DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO3, LDOEN1, BIT(2), LDO3DAC, 0x7f, 600000, 3500000, 25000),
+ REG(LDO4, LDOEN1, BIT(3), LDO4DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO5, LDOEN1, BIT(4), LDO5DAC, 0x7f, 900000, 3500000, 25000),
+ /* LDO RTC */
+ REG(LDORTC1, LDOEN2, BIT(4), LDORTCDAC, 0x7f, 1200000, 3500000, 25000),
+ REG(LDORTC2, LDOEN2, BIT(5), LDORTC2DAC, 0x7f, 900000, 3500000, 25000),
+};
+
+static struct regulator_desc rn5t618_regulators[] = {
+ /* DCDC */
+ REG(DCDC1, DC1CTL, BIT(0), DC1DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC2, DC2CTL, BIT(0), DC2DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC3, DC3CTL, BIT(0), DC3DAC, 0xff, 600000, 3500000, 12500),
+ /* LDO */
+ REG(LDO1, LDOEN1, BIT(0), LDO1DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO2, LDOEN1, BIT(1), LDO2DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO3, LDOEN1, BIT(2), LDO3DAC, 0x7f, 600000, 3500000, 25000),
+ REG(LDO4, LDOEN1, BIT(3), LDO4DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO5, LDOEN1, BIT(4), LDO5DAC, 0x7f, 900000, 3500000, 25000),
+ /* LDO RTC */
+ REG(LDORTC1, LDOEN2, BIT(4), LDORTCDAC, 0x7f, 1700000, 3500000, 25000),
+ REG(LDORTC2, LDOEN2, BIT(5), LDORTC2DAC, 0x7f, 900000, 3500000, 25000),
+};
+
+static struct regulator_desc rc5t619_regulators[] = {
+ /* DCDC */
+ REG(DCDC1, DC1CTL, BIT(0), DC1DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC2, DC2CTL, BIT(0), DC2DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC3, DC3CTL, BIT(0), DC3DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC4, DC4CTL, BIT(0), DC4DAC, 0xff, 600000, 3500000, 12500),
+ REG(DCDC5, DC5CTL, BIT(0), DC5DAC, 0xff, 600000, 3500000, 12500),
+ /* LDO */
+ REG(LDO1, LDOEN1, BIT(0), LDO1DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO2, LDOEN1, BIT(1), LDO2DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO3, LDOEN1, BIT(2), LDO3DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO4, LDOEN1, BIT(3), LDO4DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO5, LDOEN1, BIT(4), LDO5DAC, 0x7f, 600000, 3500000, 25000),
+ REG(LDO6, LDOEN1, BIT(5), LDO6DAC, 0x7f, 600000, 3500000, 25000),
+ REG(LDO7, LDOEN1, BIT(6), LDO7DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO8, LDOEN1, BIT(7), LDO8DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO9, LDOEN2, BIT(0), LDO9DAC, 0x7f, 900000, 3500000, 25000),
+ REG(LDO10, LDOEN2, BIT(0), LDO10DAC, 0x7f, 900000, 3500000, 25000),
+ /* LDO RTC */
+ REG(LDORTC1, LDOEN2, BIT(4), LDORTCDAC, 0x7f, 1700000, 3500000, 25000),
+ REG(LDORTC2, LDOEN2, BIT(5), LDORTC2DAC, 0x7f, 900000, 3500000, 25000),
+};
+
+static int rn5t618_regulator_probe(struct platform_device *pdev)
+{
+ struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct regulator_desc *regulators;
+ int i;
+ int num_regulators = 0;
+
+ switch (rn5t618->variant) {
+ case RN5T567:
+ regulators = rn5t567_regulators;
+ num_regulators = ARRAY_SIZE(rn5t567_regulators);
+ break;
+ case RN5T618:
+ regulators = rn5t618_regulators;
+ num_regulators = ARRAY_SIZE(rn5t618_regulators);
+ break;
+ case RC5T619:
+ regulators = rc5t619_regulators;
+ num_regulators = ARRAY_SIZE(rc5t619_regulators);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ config.dev = pdev->dev.parent;
+ config.regmap = rn5t618->regmap;
+
+ for (i = 0; i < num_regulators; i++) {
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s regulator\n",
+ regulators[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver rn5t618_regulator_driver = {
+ .probe = rn5t618_regulator_probe,
+ .driver = {
+ .name = "rn5t618-regulator",
+ },
+};
+
+module_platform_driver(rn5t618_regulator_driver);
+
+MODULE_ALIAS("platform:rn5t618-regulator");
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
+MODULE_DESCRIPTION("RN5T618 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/rt5033-regulator.c b/drivers/regulator/rt5033-regulator.c
new file mode 100644
index 000000000..96d2c18e0
--- /dev/null
+++ b/drivers/regulator/rt5033-regulator.c
@@ -0,0 +1,129 @@
+/*
+ * Regulator driver for the Richtek RT5033
+ *
+ * Copyright (C) 2014 Samsung Electronics, Co., Ltd.
+ * Author: Beomho Seo <beomho.seo@samsung.com>
+ *
+ * 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 bythe Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/rt5033.h>
+#include <linux/mfd/rt5033-private.h>
+#include <linux/regulator/of_regulator.h>
+
+static struct regulator_ops rt5033_safe_ldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops rt5033_buck_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,
+};
+
+static const struct regulator_desc rt5033_supported_regulators[] = {
+ [RT5033_BUCK] = {
+ .name = "BUCK",
+ .of_match = of_match_ptr("BUCK"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RT5033_BUCK,
+ .ops = &rt5033_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM,
+ .min_uV = RT5033_REGULATOR_BUCK_VOLTAGE_MIN,
+ .uV_step = RT5033_REGULATOR_BUCK_VOLTAGE_STEP,
+ .enable_reg = RT5033_REG_CTRL,
+ .enable_mask = RT5033_CTRL_EN_BUCK_MASK,
+ .vsel_reg = RT5033_REG_BUCK_CTRL,
+ .vsel_mask = RT5033_BUCK_CTRL_MASK,
+ },
+ [RT5033_LDO] = {
+ .name = "LDO",
+ .of_match = of_match_ptr("LDO"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RT5033_LDO,
+ .ops = &rt5033_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM,
+ .min_uV = RT5033_REGULATOR_LDO_VOLTAGE_MIN,
+ .uV_step = RT5033_REGULATOR_LDO_VOLTAGE_STEP,
+ .enable_reg = RT5033_REG_CTRL,
+ .enable_mask = RT5033_CTRL_EN_LDO_MASK,
+ .vsel_reg = RT5033_REG_LDO_CTRL,
+ .vsel_mask = RT5033_LDO_CTRL_MASK,
+ },
+ [RT5033_SAFE_LDO] = {
+ .name = "SAFE_LDO",
+ .of_match = of_match_ptr("SAFE_LDO"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = RT5033_SAFE_LDO,
+ .ops = &rt5033_safe_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ .min_uV = RT5033_REGULATOR_SAFE_LDO_VOLTAGE,
+ .enable_reg = RT5033_REG_CTRL,
+ .enable_mask = RT5033_CTRL_EN_SAFE_LDO_MASK,
+ },
+};
+
+static int rt5033_regulator_probe(struct platform_device *pdev)
+{
+ struct rt5033_dev *rt5033 = dev_get_drvdata(pdev->dev.parent);
+ int ret, i;
+ struct regulator_config config = {};
+
+ config.dev = rt5033->dev;
+ config.driver_data = rt5033;
+
+ for (i = 0; i < ARRAY_SIZE(rt5033_supported_regulators); i++) {
+ struct regulator_dev *regulator;
+
+ config.regmap = rt5033->regmap;
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &rt5033_supported_regulators[i], &config);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+ dev_err(&pdev->dev,
+ "Regulator init failed %d: with error: %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id rt5033_regulator_id[] = {
+ { "rt5033-regulator", },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, rt5033_regulator_id);
+
+static struct platform_driver rt5033_regulator_driver = {
+ .driver = {
+ .name = "rt5033-regulator",
+ },
+ .probe = rt5033_regulator_probe,
+ .id_table = rt5033_regulator_id,
+};
+module_platform_driver(rt5033_regulator_driver);
+
+MODULE_DESCRIPTION("Richtek RT5033 Regulator driver");
+MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c
new file mode 100644
index 000000000..58a1fe583
--- /dev/null
+++ b/drivers/regulator/s2mpa01.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2013 Samsung Electronics Co., Ltd
+// http://www.samsung.com
+
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s2mpa01.h>
+
+#define S2MPA01_REGULATOR_CNT ARRAY_SIZE(regulators)
+
+struct s2mpa01_info {
+ struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX];
+ int ramp_delay24;
+ int ramp_delay3;
+ int ramp_delay5;
+ int ramp_delay16;
+ int ramp_delay7;
+ int ramp_delay8910;
+};
+
+static int get_ramp_delay(int ramp_delay)
+{
+ unsigned char cnt = 0;
+
+ ramp_delay /= 6250;
+
+ while (true) {
+ ramp_delay = ramp_delay >> 1;
+ if (ramp_delay == 0)
+ break;
+ cnt++;
+ }
+
+ if (cnt > 3)
+ cnt = 3;
+
+ return cnt;
+}
+
+static int s2mpa01_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector,
+ unsigned int new_selector)
+{
+ struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev);
+ unsigned int ramp_delay = 0;
+ int old_volt, new_volt;
+
+ switch (rdev_get_id(rdev)) {
+ case S2MPA01_BUCK2:
+ case S2MPA01_BUCK4:
+ ramp_delay = s2mpa01->ramp_delay24;
+ break;
+ case S2MPA01_BUCK3:
+ ramp_delay = s2mpa01->ramp_delay3;
+ break;
+ case S2MPA01_BUCK5:
+ ramp_delay = s2mpa01->ramp_delay5;
+ break;
+ case S2MPA01_BUCK1:
+ case S2MPA01_BUCK6:
+ ramp_delay = s2mpa01->ramp_delay16;
+ break;
+ case S2MPA01_BUCK7:
+ ramp_delay = s2mpa01->ramp_delay7;
+ break;
+ case S2MPA01_BUCK8:
+ case S2MPA01_BUCK9:
+ case S2MPA01_BUCK10:
+ ramp_delay = s2mpa01->ramp_delay8910;
+ break;
+ }
+
+ if (ramp_delay == 0)
+ ramp_delay = rdev->desc->ramp_delay;
+
+ old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector);
+ new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector);
+
+ return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay);
+}
+
+static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev);
+ unsigned int ramp_val, ramp_shift, ramp_reg = S2MPA01_REG_RAMP2;
+ unsigned int ramp_enable = 1, enable_shift = 0;
+ int ret;
+
+ switch (rdev_get_id(rdev)) {
+ case S2MPA01_BUCK1:
+ enable_shift = S2MPA01_BUCK1_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mpa01->ramp_delay16)
+ s2mpa01->ramp_delay16 = ramp_delay;
+ else
+ ramp_delay = s2mpa01->ramp_delay16;
+
+ ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT;
+ break;
+ case S2MPA01_BUCK2:
+ enable_shift = S2MPA01_BUCK2_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mpa01->ramp_delay24)
+ s2mpa01->ramp_delay24 = ramp_delay;
+ else
+ ramp_delay = s2mpa01->ramp_delay24;
+
+ ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT;
+ ramp_reg = S2MPA01_REG_RAMP1;
+ break;
+ case S2MPA01_BUCK3:
+ enable_shift = S2MPA01_BUCK3_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ s2mpa01->ramp_delay3 = ramp_delay;
+ ramp_shift = S2MPA01_BUCK3_RAMP_SHIFT;
+ ramp_reg = S2MPA01_REG_RAMP1;
+ break;
+ case S2MPA01_BUCK4:
+ enable_shift = S2MPA01_BUCK4_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mpa01->ramp_delay24)
+ s2mpa01->ramp_delay24 = ramp_delay;
+ else
+ ramp_delay = s2mpa01->ramp_delay24;
+
+ ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT;
+ ramp_reg = S2MPA01_REG_RAMP1;
+ break;
+ case S2MPA01_BUCK5:
+ s2mpa01->ramp_delay5 = ramp_delay;
+ ramp_shift = S2MPA01_BUCK5_RAMP_SHIFT;
+ break;
+ case S2MPA01_BUCK6:
+ if (ramp_delay > s2mpa01->ramp_delay16)
+ s2mpa01->ramp_delay16 = ramp_delay;
+ else
+ ramp_delay = s2mpa01->ramp_delay16;
+
+ ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT;
+ break;
+ case S2MPA01_BUCK7:
+ s2mpa01->ramp_delay7 = ramp_delay;
+ ramp_shift = S2MPA01_BUCK7_RAMP_SHIFT;
+ break;
+ case S2MPA01_BUCK8:
+ case S2MPA01_BUCK9:
+ case S2MPA01_BUCK10:
+ if (ramp_delay > s2mpa01->ramp_delay8910)
+ s2mpa01->ramp_delay8910 = ramp_delay;
+ else
+ ramp_delay = s2mpa01->ramp_delay8910;
+
+ ramp_shift = S2MPA01_BUCK8910_RAMP_SHIFT;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!ramp_enable)
+ goto ramp_disable;
+
+ /* Ramp delay can be enabled/disabled only for buck[1234] */
+ if (rdev_get_id(rdev) >= S2MPA01_BUCK1 &&
+ rdev_get_id(rdev) <= S2MPA01_BUCK4) {
+ ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1,
+ 1 << enable_shift, 1 << enable_shift);
+ if (ret) {
+ dev_err(&rdev->dev, "failed to enable ramp rate\n");
+ return ret;
+ }
+ }
+
+ ramp_val = get_ramp_delay(ramp_delay);
+
+ return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift,
+ ramp_val << ramp_shift);
+
+ramp_disable:
+ return regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1,
+ 1 << enable_shift, 0);
+}
+
+static const struct regulator_ops s2mpa01_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 const struct regulator_ops s2mpa01_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 = s2mpa01_regulator_set_voltage_time_sel,
+ .set_ramp_delay = s2mpa01_set_ramp_delay,
+};
+
+#define regulator_desc_ldo(num, step) { \
+ .name = "LDO"#num, \
+ .id = S2MPA01_LDO##num, \
+ .ops = &s2mpa01_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_800_MV, \
+ .uV_step = step, \
+ .n_voltages = S2MPA01_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPA01_REG_L1CTRL + num - 1, \
+ .vsel_mask = S2MPA01_LDO_VSEL_MASK, \
+ .enable_reg = S2MPA01_REG_L1CTRL + num - 1, \
+ .enable_mask = S2MPA01_ENABLE_MASK \
+}
+
+#define regulator_desc_buck1_4(num) { \
+ .name = "BUCK"#num, \
+ .id = S2MPA01_BUCK##num, \
+ .ops = &s2mpa01_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_600_MV, \
+ .uV_step = STEP_6_25_MV, \
+ .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPA01_RAMP_DELAY, \
+ .vsel_reg = S2MPA01_REG_B1CTRL2 + (num - 1) * 2, \
+ .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPA01_REG_B1CTRL1 + (num - 1) * 2, \
+ .enable_mask = S2MPA01_ENABLE_MASK \
+}
+
+#define regulator_desc_buck5 { \
+ .name = "BUCK5", \
+ .id = S2MPA01_BUCK5, \
+ .ops = &s2mpa01_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_800_MV, \
+ .uV_step = STEP_6_25_MV, \
+ .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPA01_RAMP_DELAY, \
+ .vsel_reg = S2MPA01_REG_B5CTRL2, \
+ .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPA01_REG_B5CTRL1, \
+ .enable_mask = S2MPA01_ENABLE_MASK \
+}
+
+#define regulator_desc_buck6_10(num, min, step) { \
+ .name = "BUCK"#num, \
+ .id = S2MPA01_BUCK##num, \
+ .ops = &s2mpa01_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPA01_RAMP_DELAY, \
+ .vsel_reg = S2MPA01_REG_B6CTRL2 + (num - 6) * 2, \
+ .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPA01_REG_B6CTRL1 + (num - 6) * 2, \
+ .enable_mask = S2MPA01_ENABLE_MASK \
+}
+
+static const struct regulator_desc regulators[] = {
+ regulator_desc_ldo(1, STEP_25_MV),
+ regulator_desc_ldo(2, STEP_50_MV),
+ regulator_desc_ldo(3, STEP_50_MV),
+ regulator_desc_ldo(4, STEP_50_MV),
+ regulator_desc_ldo(5, STEP_25_MV),
+ regulator_desc_ldo(6, STEP_25_MV),
+ regulator_desc_ldo(7, STEP_50_MV),
+ regulator_desc_ldo(8, STEP_50_MV),
+ regulator_desc_ldo(9, STEP_50_MV),
+ regulator_desc_ldo(10, STEP_50_MV),
+ regulator_desc_ldo(11, STEP_50_MV),
+ regulator_desc_ldo(12, STEP_50_MV),
+ regulator_desc_ldo(13, STEP_50_MV),
+ regulator_desc_ldo(14, STEP_50_MV),
+ regulator_desc_ldo(15, STEP_50_MV),
+ regulator_desc_ldo(16, STEP_50_MV),
+ regulator_desc_ldo(17, STEP_50_MV),
+ regulator_desc_ldo(18, STEP_50_MV),
+ regulator_desc_ldo(19, STEP_50_MV),
+ regulator_desc_ldo(20, STEP_50_MV),
+ regulator_desc_ldo(21, STEP_50_MV),
+ regulator_desc_ldo(22, STEP_50_MV),
+ regulator_desc_ldo(23, STEP_50_MV),
+ regulator_desc_ldo(24, STEP_50_MV),
+ regulator_desc_ldo(25, STEP_50_MV),
+ regulator_desc_ldo(26, STEP_25_MV),
+ 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_10(6, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_buck6_10(7, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_buck6_10(8, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_buck6_10(9, MIN_1500_MV, STEP_12_5_MV),
+ regulator_desc_buck6_10(10, MIN_1000_MV, STEP_12_5_MV),
+};
+
+static int s2mpa01_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 device_node *reg_np = NULL;
+ struct regulator_config config = { };
+ struct of_regulator_match *rdata;
+ struct s2mpa01_info *s2mpa01;
+ int i;
+
+ s2mpa01 = devm_kzalloc(&pdev->dev, sizeof(*s2mpa01), GFP_KERNEL);
+ if (!s2mpa01)
+ return -ENOMEM;
+
+ rdata = s2mpa01->rdata;
+ for (i = 0; i < S2MPA01_REGULATOR_CNT; i++)
+ rdata[i].name = regulators[i].name;
+
+ if (iodev->dev->of_node) {
+ reg_np = of_get_child_by_name(iodev->dev->of_node,
+ "regulators");
+ if (!reg_np) {
+ dev_err(&pdev->dev,
+ "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
+
+ of_regulator_match(&pdev->dev, reg_np, rdata,
+ S2MPA01_REGULATOR_MAX);
+ of_node_put(reg_np);
+ }
+
+ platform_set_drvdata(pdev, s2mpa01);
+
+ config.dev = &pdev->dev;
+ config.regmap = iodev->regmap_pmic;
+ config.driver_data = s2mpa01;
+
+ for (i = 0; i < S2MPA01_REGULATOR_MAX; i++) {
+ struct regulator_dev *rdev;
+ if (pdata)
+ config.init_data = pdata->regulators[i].initdata;
+ else
+ config.init_data = rdata[i].init_data;
+
+ if (reg_np)
+ config.of_node = rdata[i].of_node;
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "regulator init failed for %d\n",
+ i);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id s2mpa01_pmic_id[] = {
+ { "s2mpa01-pmic", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, s2mpa01_pmic_id);
+
+static struct platform_driver s2mpa01_pmic_driver = {
+ .driver = {
+ .name = "s2mpa01-pmic",
+ },
+ .probe = s2mpa01_pmic_probe,
+ .id_table = s2mpa01_pmic_id,
+};
+
+module_platform_driver(s2mpa01_pmic_driver);
+
+/* Module information */
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_AUTHOR("Sachin Kamat <sachin.kamat@samsung.com>");
+MODULE_DESCRIPTION("SAMSUNG S2MPA01 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
new file mode 100644
index 000000000..7c598c156
--- /dev/null
+++ b/drivers/regulator/s2mps11.c
@@ -0,0 +1,1233 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
+// http://www.samsung.com
+
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps13.h>
+#include <linux/mfd/samsung/s2mps14.h>
+#include <linux/mfd/samsung/s2mps15.h>
+#include <linux/mfd/samsung/s2mpu02.h>
+
+/* The highest number of possible regulators for supported devices. */
+#define S2MPS_REGULATOR_MAX S2MPS13_REGULATOR_MAX
+struct s2mps11_info {
+ int ramp_delay2;
+ int ramp_delay34;
+ int ramp_delay5;
+ int ramp_delay16;
+ int ramp_delay7810;
+ int ramp_delay9;
+
+ enum sec_device_type dev_type;
+
+ /*
+ * One bit for each S2MPS13/S2MPS14/S2MPU02 regulator whether
+ * the suspend mode was enabled.
+ */
+ DECLARE_BITMAP(suspend_state, S2MPS_REGULATOR_MAX);
+
+ /*
+ * Array (size: number of regulators) with GPIO-s for external
+ * sleep control.
+ */
+ int *ext_control_gpio;
+};
+
+static int get_ramp_delay(int ramp_delay)
+{
+ unsigned char cnt = 0;
+
+ ramp_delay /= 6250;
+
+ while (true) {
+ ramp_delay = ramp_delay >> 1;
+ if (ramp_delay == 0)
+ break;
+ cnt++;
+ }
+
+ if (cnt > 3)
+ cnt = 3;
+
+ return cnt;
+}
+
+static int s2mps11_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector,
+ unsigned int new_selector)
+{
+ struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
+ unsigned int ramp_delay = 0;
+ int old_volt, new_volt;
+
+ switch (rdev_get_id(rdev)) {
+ case S2MPS11_BUCK2:
+ ramp_delay = s2mps11->ramp_delay2;
+ break;
+ case S2MPS11_BUCK3:
+ case S2MPS11_BUCK4:
+ ramp_delay = s2mps11->ramp_delay34;
+ break;
+ case S2MPS11_BUCK5:
+ ramp_delay = s2mps11->ramp_delay5;
+ break;
+ case S2MPS11_BUCK6:
+ case S2MPS11_BUCK1:
+ ramp_delay = s2mps11->ramp_delay16;
+ break;
+ case S2MPS11_BUCK7:
+ case S2MPS11_BUCK8:
+ case S2MPS11_BUCK10:
+ ramp_delay = s2mps11->ramp_delay7810;
+ break;
+ case S2MPS11_BUCK9:
+ ramp_delay = s2mps11->ramp_delay9;
+ }
+
+ if (ramp_delay == 0)
+ ramp_delay = rdev->desc->ramp_delay;
+
+ old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector);
+ new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector);
+
+ return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay);
+}
+
+static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
+ unsigned int ramp_val, ramp_shift, ramp_reg = S2MPS11_REG_RAMP_BUCK;
+ unsigned int ramp_enable = 1, enable_shift = 0;
+ int ret;
+
+ switch (rdev_get_id(rdev)) {
+ case S2MPS11_BUCK1:
+ if (ramp_delay > s2mps11->ramp_delay16)
+ s2mps11->ramp_delay16 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay16;
+
+ ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK2:
+ enable_shift = S2MPS11_BUCK2_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ s2mps11->ramp_delay2 = ramp_delay;
+ ramp_shift = S2MPS11_BUCK2_RAMP_SHIFT;
+ ramp_reg = S2MPS11_REG_RAMP;
+ break;
+ case S2MPS11_BUCK3:
+ enable_shift = S2MPS11_BUCK3_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mps11->ramp_delay34)
+ s2mps11->ramp_delay34 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay34;
+
+ ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT;
+ ramp_reg = S2MPS11_REG_RAMP;
+ break;
+ case S2MPS11_BUCK4:
+ enable_shift = S2MPS11_BUCK4_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mps11->ramp_delay34)
+ s2mps11->ramp_delay34 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay34;
+
+ ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT;
+ ramp_reg = S2MPS11_REG_RAMP;
+ break;
+ case S2MPS11_BUCK5:
+ s2mps11->ramp_delay5 = ramp_delay;
+ ramp_shift = S2MPS11_BUCK5_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK6:
+ enable_shift = S2MPS11_BUCK6_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mps11->ramp_delay16)
+ s2mps11->ramp_delay16 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay16;
+
+ ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK7:
+ case S2MPS11_BUCK8:
+ case S2MPS11_BUCK10:
+ if (ramp_delay > s2mps11->ramp_delay7810)
+ s2mps11->ramp_delay7810 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay7810;
+
+ ramp_shift = S2MPS11_BUCK7810_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK9:
+ s2mps11->ramp_delay9 = ramp_delay;
+ ramp_shift = S2MPS11_BUCK9_RAMP_SHIFT;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!ramp_enable)
+ goto ramp_disable;
+
+ /* Ramp delay can be enabled/disabled only for buck[2346] */
+ if ((rdev_get_id(rdev) >= S2MPS11_BUCK2 &&
+ rdev_get_id(rdev) <= S2MPS11_BUCK4) ||
+ rdev_get_id(rdev) == S2MPS11_BUCK6) {
+ ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP,
+ 1 << enable_shift, 1 << enable_shift);
+ if (ret) {
+ dev_err(&rdev->dev, "failed to enable ramp rate\n");
+ return ret;
+ }
+ }
+
+ ramp_val = get_ramp_delay(ramp_delay);
+
+ return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift,
+ ramp_val << ramp_shift);
+
+ramp_disable:
+ return regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP,
+ 1 << enable_shift, 0);
+}
+
+static const 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 const 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 = s2mps11_regulator_set_voltage_time_sel,
+ .set_ramp_delay = s2mps11_set_ramp_delay,
+};
+
+#define regulator_desc_s2mps11_ldo(num, step) { \
+ .name = "LDO"#num, \
+ .id = S2MPS11_LDO##num, \
+ .ops = &s2mps11_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .ramp_delay = RAMP_DELAY_12_MVUS, \
+ .min_uV = MIN_800_MV, \
+ .uV_step = step, \
+ .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_s2mps11_buck1_4(num) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS11_BUCK##num, \
+ .ops = &s2mps11_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_600_MV, \
+ .uV_step = STEP_6_25_MV, \
+ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS11_RAMP_DELAY, \
+ .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_s2mps11_buck5 { \
+ .name = "BUCK5", \
+ .id = S2MPS11_BUCK5, \
+ .ops = &s2mps11_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_600_MV, \
+ .uV_step = STEP_6_25_MV, \
+ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS11_RAMP_DELAY, \
+ .vsel_reg = S2MPS11_REG_B5CTRL2, \
+ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS11_REG_B5CTRL1, \
+ .enable_mask = S2MPS11_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps11_buck67810(num, min, step) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS11_BUCK##num, \
+ .ops = &s2mps11_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS11_RAMP_DELAY, \
+ .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_s2mps11_buck9 { \
+ .name = "BUCK9", \
+ .id = S2MPS11_BUCK9, \
+ .ops = &s2mps11_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_3000_MV, \
+ .uV_step = STEP_25_MV, \
+ .n_voltages = S2MPS11_BUCK9_N_VOLTAGES, \
+ .ramp_delay = S2MPS11_RAMP_DELAY, \
+ .vsel_reg = S2MPS11_REG_B9CTRL2, \
+ .vsel_mask = S2MPS11_BUCK9_VSEL_MASK, \
+ .enable_reg = S2MPS11_REG_B9CTRL1, \
+ .enable_mask = S2MPS11_ENABLE_MASK \
+}
+
+static const struct regulator_desc s2mps11_regulators[] = {
+ regulator_desc_s2mps11_ldo(1, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(2, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(3, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(4, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(5, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(6, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(7, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(8, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(9, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(10, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(11, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(12, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(13, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(14, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(15, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(16, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(17, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(18, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(19, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(20, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(21, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(22, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(23, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(24, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(25, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(26, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(27, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(28, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(29, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(30, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(31, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(32, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(33, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(34, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(35, STEP_25_MV),
+ regulator_desc_s2mps11_ldo(36, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(37, STEP_50_MV),
+ regulator_desc_s2mps11_ldo(38, STEP_50_MV),
+ regulator_desc_s2mps11_buck1_4(1),
+ regulator_desc_s2mps11_buck1_4(2),
+ regulator_desc_s2mps11_buck1_4(3),
+ regulator_desc_s2mps11_buck1_4(4),
+ regulator_desc_s2mps11_buck5,
+ regulator_desc_s2mps11_buck67810(6, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck67810(7, MIN_750_MV, STEP_12_5_MV),
+ regulator_desc_s2mps11_buck67810(8, MIN_750_MV, STEP_12_5_MV),
+ regulator_desc_s2mps11_buck9,
+ regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV),
+};
+
+static const struct regulator_ops s2mps14_reg_ops;
+
+#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \
+ .name = "LDO"#num, \
+ .id = S2MPS13_LDO##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .linear_min_sel = min_sel, \
+ .n_voltages = S2MPS14_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPS13_REG_L1CTRL + num - 1, \
+ .vsel_mask = S2MPS14_LDO_VSEL_MASK, \
+ .enable_reg = S2MPS13_REG_L1CTRL + num - 1, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps13_buck(num, min, step, min_sel) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS13_BUCK##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .linear_min_sel = min_sel, \
+ .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPS13_REG_B1OUT + (num - 1) * 2, \
+ .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps13_buck7(num, min, step, min_sel) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS13_BUCK##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .linear_min_sel = min_sel, \
+ .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPS13_REG_B1OUT + (num) * 2 - 1, \
+ .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps13_buck8_10(num, min, step, min_sel) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS13_BUCK##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .linear_min_sel = min_sel, \
+ .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPS13_REG_B1OUT + (num) * 2 - 1, \
+ .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS13_REG_B1CTRL + (num) * 2 - 1, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+static const struct regulator_desc s2mps13_regulators[] = {
+ regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(3, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(4, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(5, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(6, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(7, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(8, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(9, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(10, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(11, MIN_800_MV, STEP_25_MV, 0x10),
+ regulator_desc_s2mps13_ldo(12, MIN_800_MV, STEP_25_MV, 0x10),
+ regulator_desc_s2mps13_ldo(13, MIN_800_MV, STEP_25_MV, 0x10),
+ regulator_desc_s2mps13_ldo(14, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(15, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(16, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(17, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(18, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(19, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(20, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(21, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(22, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(23, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(24, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(25, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(26, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(27, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(28, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(29, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(30, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(31, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(32, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(33, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(34, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(35, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(36, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(37, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(38, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(39, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(40, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_buck(1, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(2, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(3, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck7(7, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck8_10(8, MIN_1000_MV, STEP_12_5_MV, 0x20),
+ regulator_desc_s2mps13_buck8_10(9, MIN_1000_MV, STEP_12_5_MV, 0x20),
+ regulator_desc_s2mps13_buck8_10(10, MIN_500_MV, STEP_6_25_MV, 0x10),
+};
+
+static int s2mps14_regulator_enable(struct regulator_dev *rdev)
+{
+ struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
+ unsigned int val;
+
+ switch (s2mps11->dev_type) {
+ case S2MPS13X:
+ case S2MPS14X:
+ if (test_bit(rdev_get_id(rdev), s2mps11->suspend_state))
+ val = S2MPS14_ENABLE_SUSPEND;
+ else if (gpio_is_valid(s2mps11->ext_control_gpio[rdev_get_id(rdev)]))
+ val = S2MPS14_ENABLE_EXT_CONTROL;
+ else
+ val = rdev->desc->enable_mask;
+ break;
+ case S2MPU02:
+ if (test_bit(rdev_get_id(rdev), s2mps11->suspend_state))
+ val = S2MPU02_ENABLE_SUSPEND;
+ else
+ val = rdev->desc->enable_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val);
+}
+
+static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ unsigned int val, state;
+ struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
+ int rdev_id = rdev_get_id(rdev);
+
+ /* Below LDO should be always on or does not support suspend mode. */
+ switch (s2mps11->dev_type) {
+ case S2MPS13X:
+ case S2MPS14X:
+ switch (rdev_id) {
+ case S2MPS14_LDO3:
+ return 0;
+ default:
+ state = S2MPS14_ENABLE_SUSPEND;
+ break;
+ }
+ break;
+ case S2MPU02:
+ switch (rdev_id) {
+ case S2MPU02_LDO13:
+ case S2MPU02_LDO14:
+ case S2MPU02_LDO15:
+ case S2MPU02_LDO17:
+ case S2MPU02_BUCK7:
+ state = S2MPU02_DISABLE_SUSPEND;
+ break;
+ default:
+ state = S2MPU02_ENABLE_SUSPEND;
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
+ if (ret < 0)
+ return ret;
+
+ set_bit(rdev_get_id(rdev), s2mps11->suspend_state);
+ /*
+ * Don't enable suspend mode if regulator is already disabled because
+ * this would effectively for a short time turn on the regulator after
+ * resuming.
+ * However we still want to toggle the suspend_state bit for regulator
+ * in case if it got enabled before suspending the system.
+ */
+ if (!(val & rdev->desc->enable_mask))
+ return 0;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, state);
+}
+
+static const struct regulator_ops s2mps14_reg_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = s2mps14_regulator_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 = s2mps14_regulator_set_suspend_disable,
+};
+
+#define regulator_desc_s2mps14_ldo(num, min, step) { \
+ .name = "LDO"#num, \
+ .id = S2MPS14_LDO##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = S2MPS14_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPS14_REG_L1CTRL + num - 1, \
+ .vsel_mask = S2MPS14_LDO_VSEL_MASK, \
+ .enable_reg = S2MPS14_REG_L1CTRL + num - 1, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps14_buck(num, min, step, min_sel) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS14_BUCK##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \
+ .linear_min_sel = min_sel, \
+ .ramp_delay = S2MPS14_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPS14_REG_B1CTRL2 + (num - 1) * 2, \
+ .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS14_REG_B1CTRL1 + (num - 1) * 2, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+static const struct regulator_desc s2mps14_regulators[] = {
+ regulator_desc_s2mps14_ldo(1, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(2, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(3, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(4, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(5, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(6, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(7, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(8, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(9, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(10, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(11, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(12, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(13, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(14, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(15, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(16, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(17, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(18, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(19, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(20, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(21, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(22, MIN_800_MV, STEP_12_5_MV),
+ regulator_desc_s2mps14_ldo(23, MIN_800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(24, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_ldo(25, MIN_1800_MV, STEP_25_MV),
+ regulator_desc_s2mps14_buck(1, MIN_600_MV, STEP_6_25_MV,
+ S2MPS14_BUCK1235_START_SEL),
+ regulator_desc_s2mps14_buck(2, MIN_600_MV, STEP_6_25_MV,
+ S2MPS14_BUCK1235_START_SEL),
+ regulator_desc_s2mps14_buck(3, MIN_600_MV, STEP_6_25_MV,
+ S2MPS14_BUCK1235_START_SEL),
+ regulator_desc_s2mps14_buck(4, MIN_1400_MV, STEP_12_5_MV,
+ S2MPS14_BUCK4_START_SEL),
+ regulator_desc_s2mps14_buck(5, MIN_600_MV, STEP_6_25_MV,
+ S2MPS14_BUCK1235_START_SEL),
+};
+
+static const struct regulator_ops s2mps15_reg_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .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,
+};
+
+static const struct regulator_ops s2mps15_reg_buck_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .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_s2mps15_ldo(num, range) { \
+ .name = "LDO"#num, \
+ .id = S2MPS15_LDO##num, \
+ .ops = &s2mps15_reg_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = range, \
+ .n_linear_ranges = ARRAY_SIZE(range), \
+ .n_voltages = S2MPS15_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPS15_REG_L1CTRL + num - 1, \
+ .vsel_mask = S2MPS15_LDO_VSEL_MASK, \
+ .enable_reg = S2MPS15_REG_L1CTRL + num - 1, \
+ .enable_mask = S2MPS15_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps15_buck(num, range) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS15_BUCK##num, \
+ .ops = &s2mps15_reg_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = range, \
+ .n_linear_ranges = ARRAY_SIZE(range), \
+ .ramp_delay = 12500, \
+ .n_voltages = S2MPS15_BUCK_N_VOLTAGES, \
+ .vsel_reg = S2MPS15_REG_B1CTRL2 + ((num - 1) * 2), \
+ .vsel_mask = S2MPS15_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS15_REG_B1CTRL1 + ((num - 1) * 2), \
+ .enable_mask = S2MPS15_ENABLE_MASK \
+}
+
+/* voltage range for s2mps15 LDO 3, 5, 15, 16, 18, 20, 23 and 27 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges1[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0xc, 0x38, 25000),
+};
+
+/* voltage range for s2mps15 LDO 2, 6, 14, 17, 19, 21, 24 and 25 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges2[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x0, 0x3f, 25000),
+};
+
+/* voltage range for s2mps15 LDO 4, 11, 12, 13, 22 and 26 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges3[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x0, 0x34, 12500),
+};
+
+/* voltage range for s2mps15 LDO 7, 8, 9 and 10 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges4[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x10, 0x20, 25000),
+};
+
+/* voltage range for s2mps15 LDO 1 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges5[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x0, 0x20, 12500),
+};
+
+/* voltage range for s2mps15 BUCK 1, 2, 3, 4, 5, 6 and 7 */
+static const struct regulator_linear_range s2mps15_buck_voltage_ranges1[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x20, 0xc0, 6250),
+};
+
+/* voltage range for s2mps15 BUCK 8, 9 and 10 */
+static const struct regulator_linear_range s2mps15_buck_voltage_ranges2[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0x20, 0x78, 12500),
+};
+
+static const struct regulator_desc s2mps15_regulators[] = {
+ regulator_desc_s2mps15_ldo(1, s2mps15_ldo_voltage_ranges5),
+ regulator_desc_s2mps15_ldo(2, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(3, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(4, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(5, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(6, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(7, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(8, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(9, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(10, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(11, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(12, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(13, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(14, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(15, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(16, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(17, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(18, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(19, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(20, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(21, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(22, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(23, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(24, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(25, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(26, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(27, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_buck(1, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(2, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(3, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(4, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(5, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(6, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(7, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(8, s2mps15_buck_voltage_ranges2),
+ regulator_desc_s2mps15_buck(9, s2mps15_buck_voltage_ranges2),
+ regulator_desc_s2mps15_buck(10, s2mps15_buck_voltage_ranges2),
+};
+
+static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11,
+ struct regulator_dev *rdev)
+{
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, S2MPS14_ENABLE_EXT_CONTROL);
+}
+
+static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev,
+ struct of_regulator_match *rdata, struct s2mps11_info *s2mps11)
+{
+ int *gpio = s2mps11->ext_control_gpio;
+ unsigned int i;
+ unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11,
+ S2MPS14_LDO12 };
+
+ for (i = 0; i < ARRAY_SIZE(valid_regulators); i++) {
+ unsigned int reg = valid_regulators[i];
+
+ if (!rdata[reg].init_data || !rdata[reg].of_node)
+ continue;
+
+ gpio[reg] = of_get_named_gpio(rdata[reg].of_node,
+ "samsung,ext-control-gpios", 0);
+ if (gpio_is_valid(gpio[reg]))
+ dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n",
+ gpio[reg], reg, rdata[reg].name);
+ }
+}
+
+static int s2mps11_pmic_dt_parse(struct platform_device *pdev,
+ struct of_regulator_match *rdata, struct s2mps11_info *s2mps11,
+ unsigned int rdev_num)
+{
+ struct device_node *reg_np;
+
+ reg_np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
+ if (!reg_np) {
+ dev_err(&pdev->dev, "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
+
+ of_regulator_match(&pdev->dev, reg_np, rdata, rdev_num);
+ if (s2mps11->dev_type == S2MPS14X)
+ s2mps14_pmic_dt_parse_ext_control_gpio(pdev, rdata, s2mps11);
+
+ of_node_put(reg_np);
+
+ return 0;
+}
+
+static int s2mpu02_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ unsigned int ramp_val, ramp_shift, ramp_reg;
+
+ switch (rdev_get_id(rdev)) {
+ case S2MPU02_BUCK1:
+ ramp_shift = S2MPU02_BUCK1_RAMP_SHIFT;
+ break;
+ case S2MPU02_BUCK2:
+ ramp_shift = S2MPU02_BUCK2_RAMP_SHIFT;
+ break;
+ case S2MPU02_BUCK3:
+ ramp_shift = S2MPU02_BUCK3_RAMP_SHIFT;
+ break;
+ case S2MPU02_BUCK4:
+ ramp_shift = S2MPU02_BUCK4_RAMP_SHIFT;
+ break;
+ default:
+ return 0;
+ }
+ ramp_reg = S2MPU02_REG_RAMP1;
+ ramp_val = get_ramp_delay(ramp_delay);
+
+ return regmap_update_bits(rdev->regmap, ramp_reg,
+ S2MPU02_BUCK1234_RAMP_MASK << ramp_shift,
+ ramp_val << ramp_shift);
+}
+
+static const struct regulator_ops s2mpu02_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = s2mps14_regulator_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 = s2mps14_regulator_set_suspend_disable,
+};
+
+static const struct regulator_ops s2mpu02_buck_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = s2mps14_regulator_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 = s2mps14_regulator_set_suspend_disable,
+ .set_ramp_delay = s2mpu02_set_ramp_delay,
+};
+
+#define regulator_desc_s2mpu02_ldo1(num) { \
+ .name = "LDO"#num, \
+ .id = S2MPU02_LDO##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_LDO_MIN_900MV, \
+ .uV_step = S2MPU02_LDO_STEP_12_5MV, \
+ .linear_min_sel = S2MPU02_LDO_GROUP1_START_SEL, \
+ .n_voltages = S2MPU02_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPU02_REG_L1CTRL, \
+ .vsel_mask = S2MPU02_LDO_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_L1CTRL, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_ldo2(num) { \
+ .name = "LDO"#num, \
+ .id = S2MPU02_LDO##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_LDO_MIN_1050MV, \
+ .uV_step = S2MPU02_LDO_STEP_25MV, \
+ .linear_min_sel = S2MPU02_LDO_GROUP2_START_SEL, \
+ .n_voltages = S2MPU02_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPU02_REG_L2CTRL1, \
+ .vsel_mask = S2MPU02_LDO_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_L2CTRL1, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_ldo3(num) { \
+ .name = "LDO"#num, \
+ .id = S2MPU02_LDO##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_LDO_MIN_900MV, \
+ .uV_step = S2MPU02_LDO_STEP_12_5MV, \
+ .linear_min_sel = S2MPU02_LDO_GROUP1_START_SEL, \
+ .n_voltages = S2MPU02_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPU02_REG_L3CTRL + num - 3, \
+ .vsel_mask = S2MPU02_LDO_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_L3CTRL + num - 3, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_ldo4(num) { \
+ .name = "LDO"#num, \
+ .id = S2MPU02_LDO##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_LDO_MIN_1050MV, \
+ .uV_step = S2MPU02_LDO_STEP_25MV, \
+ .linear_min_sel = S2MPU02_LDO_GROUP2_START_SEL, \
+ .n_voltages = S2MPU02_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPU02_REG_L3CTRL + num - 3, \
+ .vsel_mask = S2MPU02_LDO_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_L3CTRL + num - 3, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_ldo5(num) { \
+ .name = "LDO"#num, \
+ .id = S2MPU02_LDO##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_LDO_MIN_1600MV, \
+ .uV_step = S2MPU02_LDO_STEP_50MV, \
+ .linear_min_sel = S2MPU02_LDO_GROUP3_START_SEL, \
+ .n_voltages = S2MPU02_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPU02_REG_L3CTRL + num - 3, \
+ .vsel_mask = S2MPU02_LDO_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_L3CTRL + num - 3, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mpu02_buck1234(num) { \
+ .name = "BUCK"#num, \
+ .id = S2MPU02_BUCK##num, \
+ .ops = &s2mpu02_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_BUCK1234_MIN_600MV, \
+ .uV_step = S2MPU02_BUCK1234_STEP_6_25MV, \
+ .n_voltages = S2MPU02_BUCK_N_VOLTAGES, \
+ .linear_min_sel = S2MPU02_BUCK1234_START_SEL, \
+ .ramp_delay = S2MPU02_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPU02_REG_B1CTRL2 + (num - 1) * 2, \
+ .vsel_mask = S2MPU02_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_B1CTRL1 + (num - 1) * 2, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_buck5(num) { \
+ .name = "BUCK"#num, \
+ .id = S2MPU02_BUCK##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_BUCK5_MIN_1081_25MV, \
+ .uV_step = S2MPU02_BUCK5_STEP_6_25MV, \
+ .n_voltages = S2MPU02_BUCK_N_VOLTAGES, \
+ .linear_min_sel = S2MPU02_BUCK5_START_SEL, \
+ .ramp_delay = S2MPU02_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPU02_REG_B5CTRL2, \
+ .vsel_mask = S2MPU02_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_B5CTRL1, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_buck6(num) { \
+ .name = "BUCK"#num, \
+ .id = S2MPU02_BUCK##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_BUCK6_MIN_1700MV, \
+ .uV_step = S2MPU02_BUCK6_STEP_2_50MV, \
+ .n_voltages = S2MPU02_BUCK_N_VOLTAGES, \
+ .linear_min_sel = S2MPU02_BUCK6_START_SEL, \
+ .ramp_delay = S2MPU02_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPU02_REG_B6CTRL2, \
+ .vsel_mask = S2MPU02_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_B6CTRL1, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+#define regulator_desc_s2mpu02_buck7(num) { \
+ .name = "BUCK"#num, \
+ .id = S2MPU02_BUCK##num, \
+ .ops = &s2mpu02_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = S2MPU02_BUCK7_MIN_900MV, \
+ .uV_step = S2MPU02_BUCK7_STEP_6_25MV, \
+ .n_voltages = S2MPU02_BUCK_N_VOLTAGES, \
+ .linear_min_sel = S2MPU02_BUCK7_START_SEL, \
+ .ramp_delay = S2MPU02_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPU02_REG_B7CTRL2, \
+ .vsel_mask = S2MPU02_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPU02_REG_B7CTRL1, \
+ .enable_mask = S2MPU02_ENABLE_MASK \
+}
+
+static const struct regulator_desc s2mpu02_regulators[] = {
+ regulator_desc_s2mpu02_ldo1(1),
+ regulator_desc_s2mpu02_ldo2(2),
+ regulator_desc_s2mpu02_ldo4(3),
+ regulator_desc_s2mpu02_ldo5(4),
+ regulator_desc_s2mpu02_ldo4(5),
+ regulator_desc_s2mpu02_ldo3(6),
+ regulator_desc_s2mpu02_ldo3(7),
+ regulator_desc_s2mpu02_ldo4(8),
+ regulator_desc_s2mpu02_ldo5(9),
+ regulator_desc_s2mpu02_ldo3(10),
+ regulator_desc_s2mpu02_ldo4(11),
+ regulator_desc_s2mpu02_ldo5(12),
+ regulator_desc_s2mpu02_ldo5(13),
+ regulator_desc_s2mpu02_ldo5(14),
+ regulator_desc_s2mpu02_ldo5(15),
+ regulator_desc_s2mpu02_ldo5(16),
+ regulator_desc_s2mpu02_ldo4(17),
+ regulator_desc_s2mpu02_ldo5(18),
+ regulator_desc_s2mpu02_ldo3(19),
+ regulator_desc_s2mpu02_ldo4(20),
+ regulator_desc_s2mpu02_ldo5(21),
+ regulator_desc_s2mpu02_ldo5(22),
+ regulator_desc_s2mpu02_ldo5(23),
+ regulator_desc_s2mpu02_ldo4(24),
+ regulator_desc_s2mpu02_ldo5(25),
+ regulator_desc_s2mpu02_ldo4(26),
+ regulator_desc_s2mpu02_ldo5(27),
+ regulator_desc_s2mpu02_ldo5(28),
+ regulator_desc_s2mpu02_buck1234(1),
+ regulator_desc_s2mpu02_buck1234(2),
+ regulator_desc_s2mpu02_buck1234(3),
+ regulator_desc_s2mpu02_buck1234(4),
+ regulator_desc_s2mpu02_buck5(5),
+ regulator_desc_s2mpu02_buck6(6),
+ regulator_desc_s2mpu02_buck7(7),
+};
+
+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 = NULL;
+ struct of_regulator_match *rdata = NULL;
+ struct regulator_config config = { };
+ struct s2mps11_info *s2mps11;
+ unsigned int rdev_num = 0;
+ int i, ret = 0;
+ const struct regulator_desc *regulators;
+
+ s2mps11 = devm_kzalloc(&pdev->dev, sizeof(struct s2mps11_info),
+ GFP_KERNEL);
+ if (!s2mps11)
+ return -ENOMEM;
+
+ s2mps11->dev_type = platform_get_device_id(pdev)->driver_data;
+ switch (s2mps11->dev_type) {
+ case S2MPS11X:
+ rdev_num = ARRAY_SIZE(s2mps11_regulators);
+ regulators = s2mps11_regulators;
+ BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps11_regulators));
+ break;
+ case S2MPS13X:
+ rdev_num = ARRAY_SIZE(s2mps13_regulators);
+ regulators = s2mps13_regulators;
+ BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps13_regulators));
+ break;
+ case S2MPS14X:
+ rdev_num = ARRAY_SIZE(s2mps14_regulators);
+ regulators = s2mps14_regulators;
+ BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps14_regulators));
+ break;
+ case S2MPS15X:
+ rdev_num = ARRAY_SIZE(s2mps15_regulators);
+ regulators = s2mps15_regulators;
+ BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps15_regulators));
+ break;
+ case S2MPU02:
+ rdev_num = ARRAY_SIZE(s2mpu02_regulators);
+ regulators = s2mpu02_regulators;
+ BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu02_regulators));
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device type: %u\n",
+ s2mps11->dev_type);
+ return -EINVAL;
+ }
+
+ s2mps11->ext_control_gpio = devm_kmalloc_array(&pdev->dev,
+ rdev_num, sizeof(*s2mps11->ext_control_gpio),
+ GFP_KERNEL);
+ if (!s2mps11->ext_control_gpio)
+ return -ENOMEM;
+ /*
+ * 0 is a valid GPIO so initialize all GPIO-s to negative value
+ * to indicate that external control won't be used for this regulator.
+ */
+ for (i = 0; i < rdev_num; i++)
+ s2mps11->ext_control_gpio[i] = -EINVAL;
+
+ if (!iodev->dev->of_node) {
+ if (iodev->pdata) {
+ pdata = iodev->pdata;
+ goto common_reg;
+ } else {
+ dev_err(pdev->dev.parent,
+ "Platform data or DT node not supplied\n");
+ return -ENODEV;
+ }
+ }
+
+ rdata = kcalloc(rdev_num, sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return -ENOMEM;
+
+ for (i = 0; i < rdev_num; i++)
+ rdata[i].name = regulators[i].name;
+
+ ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11, rdev_num);
+ if (ret)
+ goto out;
+
+common_reg:
+ platform_set_drvdata(pdev, s2mps11);
+
+ config.dev = &pdev->dev;
+ config.regmap = iodev->regmap_pmic;
+ config.driver_data = s2mps11;
+ config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config.ena_gpio_initialized = true;
+ for (i = 0; i < rdev_num; i++) {
+ struct regulator_dev *regulator;
+
+ if (pdata) {
+ config.init_data = pdata->regulators[i].initdata;
+ config.of_node = pdata->regulators[i].reg_node;
+ } else {
+ config.init_data = rdata[i].init_data;
+ config.of_node = rdata[i].of_node;
+ }
+ config.ena_gpio = s2mps11->ext_control_gpio[i];
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &regulators[i], &config);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+ dev_err(&pdev->dev, "regulator init failed for %d\n",
+ i);
+ goto out;
+ }
+
+ if (gpio_is_valid(s2mps11->ext_control_gpio[i])) {
+ ret = s2mps14_pmic_enable_ext_control(s2mps11,
+ regulator);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to enable GPIO control over %s: %d\n",
+ regulator->desc->name, ret);
+ goto out;
+ }
+ }
+ }
+
+out:
+ kfree(rdata);
+
+ return ret;
+}
+
+static const struct platform_device_id s2mps11_pmic_id[] = {
+ { "s2mps11-regulator", S2MPS11X},
+ { "s2mps13-regulator", S2MPS13X},
+ { "s2mps14-regulator", S2MPS14X},
+ { "s2mps15-regulator", S2MPS15X},
+ { "s2mpu02-regulator", S2MPU02},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id);
+
+static struct platform_driver s2mps11_pmic_driver = {
+ .driver = {
+ .name = "s2mps11-pmic",
+ },
+ .probe = s2mps11_pmic_probe,
+ .id_table = s2mps11_pmic_id,
+};
+
+module_platform_driver(s2mps11_pmic_driver);
+
+/* Module information */
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("SAMSUNG S2MPS11/S2MPS14/S2MPS15/S2MPU02 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
new file mode 100644
index 000000000..4818df3f8
--- /dev/null
+++ b/drivers/regulator/s5m8767.c
@@ -0,0 +1,1015 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2011 Samsung Electronics Co., Ltd
+// http://www.samsung.com
+
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s5m8767.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+
+#define S5M8767_OPMODE_NORMAL_MODE 0x1
+
+struct s5m8767_info {
+ struct device *dev;
+ struct sec_pmic_dev *iodev;
+ int num_regulators;
+ 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] = &buck_voltage_val3,
+ [S5M8767_BUCK8] = &buck_voltage_val3,
+ [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 s5m8767_info *s5m8767, int reg_id,
+ int *reg, int *enable_ctrl)
+{
+ int i;
+ unsigned int mode;
+
+ 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)
+ return -EINVAL;
+
+ *enable_ctrl = s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
+
+ return 0;
+}
+
+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 const struct regulator_ops s5m8767_ops = {
+ .list_voltage = regulator_list_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 = s5m8767_set_voltage_sel,
+ .set_voltage_time_sel = s5m8767_set_voltage_time_sel,
+};
+
+static const struct regulator_ops s5m8767_buck78_ops = {
+ .list_voltage = regulator_list_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,
+};
+
+#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),
+};
+
+/*
+ * Enable GPIO control over BUCK9 in regulator_config for that regulator.
+ */
+static void s5m8767_regulator_config_ext_control(struct s5m8767_info *s5m8767,
+ struct sec_regulator_data *rdata,
+ struct regulator_config *config)
+{
+ int i, mode = 0;
+
+ if (rdata->id != S5M8767_BUCK9)
+ return;
+
+ /* Check if opmode for regulator matches S5M8767_ENCTRL_USE_GPIO */
+ for (i = 0; i < s5m8767->num_regulators; i++) {
+ const struct sec_opmode_data *opmode = &s5m8767->opmode[i];
+ if (opmode->id == rdata->id) {
+ mode = s5m8767_opmode_reg[rdata->id][opmode->mode];
+ break;
+ }
+ }
+ if (mode != S5M8767_ENCTRL_USE_GPIO) {
+ dev_warn(s5m8767->dev,
+ "ext-control for %s: mismatched op_mode (%x), ignoring\n",
+ rdata->reg_node->name, mode);
+ return;
+ }
+
+ if (!rdata->ext_control_gpiod) {
+ dev_warn(s5m8767->dev,
+ "ext-control for %s: GPIO not valid, ignoring\n",
+ rdata->reg_node->name);
+ return;
+ }
+
+ config->ena_gpiod = rdata->ext_control_gpiod;
+}
+
+/*
+ * Turn on GPIO control over BUCK9.
+ */
+static int s5m8767_enable_ext_control(struct s5m8767_info *s5m8767,
+ struct regulator_dev *rdev)
+{
+ int id = rdev_get_id(rdev);
+ int ret, reg, enable_ctrl;
+
+ if (id != S5M8767_BUCK9)
+ return -EINVAL;
+
+ ret = s5m8767_get_register(s5m8767, id, &reg, &enable_ctrl);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ reg, S5M8767_ENCTRL_MASK,
+ S5M8767_ENCTRL_USE_GPIO << S5M8767_ENCTRL_SHIFT);
+}
+
+
+#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 = 8, 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_get_child_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_kcalloc(&pdev->dev,
+ pdata->num_regulators, sizeof(*rdata),
+ GFP_KERNEL);
+ if (!rdata) {
+ of_node_put(regulators_np);
+ return -ENOMEM;
+ }
+
+ rmode = devm_kcalloc(&pdev->dev,
+ pdata->num_regulators, sizeof(*rmode),
+ GFP_KERNEL);
+ if (!rmode) {
+ of_node_put(regulators_np);
+ 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->ext_control_gpiod = devm_gpiod_get_from_of_node(&pdev->dev,
+ reg_np,
+ "s5m8767,pmic-ext-control-gpios",
+ 0,
+ GPIOD_OUT_HIGH,
+ "s5m8767");
+ if (IS_ERR(rdata->ext_control_gpiod))
+ return PTR_ERR(rdata->ext_control_gpiod);
+
+ rdata->id = i;
+ rdata->initdata = of_get_regulator_init_data(
+ &pdev->dev, reg_np,
+ &regulators[i]);
+ 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 %pOF\n",
+ reg_np);
+
+ rmode->mode = S5M8767_OPMODE_NORMAL_MODE;
+ }
+ rmode++;
+ }
+
+ of_node_put(regulators_np);
+
+ if (of_get_property(pmic_np, "s5m8767,pmic-buck2-uses-gpio-dvs", NULL)) {
+ pdata->buck2_gpiodvs = true;
+
+ 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_get_property(pmic_np, "s5m8767,pmic-buck3-uses-gpio-dvs", NULL)) {
+ pdata->buck3_gpiodvs = true;
+
+ 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_get_property(pmic_np, "s5m8767,pmic-buck4-uses-gpio-dvs", NULL)) {
+ pdata->buck4_gpiodvs = true;
+
+ 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;
+ }
+ }
+
+ 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");
+ }
+ }
+ }
+
+ ret = s5m8767_pmic_dt_parse_ds_gpio(iodev, pdata, pmic_np);
+ if (ret)
+ return -EINVAL;
+
+ if (of_get_property(pmic_np, "s5m8767,pmic-buck2-ramp-enable", NULL))
+ pdata->buck2_ramp_enable = true;
+
+ if (of_get_property(pmic_np, "s5m8767,pmic-buck3-ramp-enable", NULL))
+ pdata->buck3_ramp_enable = true;
+
+ if (of_get_property(pmic_np, "s5m8767,pmic-buck4-ramp-enable", NULL))
+ pdata->buck4_ramp_enable = true;
+
+ if (pdata->buck2_ramp_enable || pdata->buck3_ramp_enable
+ || pdata->buck4_ramp_enable) {
+ if (of_property_read_u32(pmic_np, "s5m8767,pmic-buck-ramp-delay",
+ &pdata->buck_ramp_delay))
+ pdata->buck_ramp_delay = 0;
+ }
+
+ 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 s5m8767_info *s5m8767;
+ int i, ret, 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;
+
+ 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);
+
+ regmap_write(s5m8767->iodev->regmap_pmic, S5M8767_REG_BUCK2DVS2,
+ buck_init);
+
+ buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
+ pdata->buck3_init);
+
+ regmap_write(s5m8767->iodev->regmap_pmic, S5M8767_REG_BUCK3DVS2,
+ buck_init);
+
+ buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
+ pdata->buck4_init);
+
+ regmap_write(s5m8767->iodev->regmap_pmic, 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);
+
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK2CTRL, 1 << 1,
+ (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1));
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK3CTRL, 1 << 1,
+ (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1));
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK4CTRL, 1 << 1,
+ (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1));
+
+ /* Initialize GPIO DVS registers */
+ for (i = 0; i < 8; i++) {
+ if (s5m8767->buck2_gpiodvs) {
+ regmap_write(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK2DVS1 + i,
+ s5m8767->buck2_vol[i]);
+ }
+
+ if (s5m8767->buck3_gpiodvs) {
+ regmap_write(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK3DVS1 + i,
+ s5m8767->buck3_vol[i]);
+ }
+
+ if (s5m8767->buck4_gpiodvs) {
+ regmap_write(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_BUCK4DVS1 + i,
+ s5m8767->buck4_vol[i]);
+ }
+ }
+
+ if (s5m8767->buck2_ramp)
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_DVSRAMP, 0x08, 0x08);
+
+ if (s5m8767->buck3_ramp)
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_DVSRAMP, 0x04, 0x04);
+
+ if (s5m8767->buck4_ramp)
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_DVSRAMP, 0x02, 0x02);
+
+ if (s5m8767->buck2_ramp || s5m8767->buck3_ramp
+ || s5m8767->buck4_ramp) {
+ unsigned int val;
+ switch (s5m8767->ramp_delay) {
+ case 5:
+ val = S5M8767_DVS_BUCK_RAMP_5;
+ break;
+ case 10:
+ val = S5M8767_DVS_BUCK_RAMP_10;
+ break;
+ case 25:
+ val = S5M8767_DVS_BUCK_RAMP_25;
+ break;
+ case 50:
+ val = S5M8767_DVS_BUCK_RAMP_50;
+ break;
+ case 100:
+ val = S5M8767_DVS_BUCK_RAMP_100;
+ break;
+ default:
+ val = S5M8767_DVS_BUCK_RAMP_10;
+ }
+ regmap_update_bits(s5m8767->iodev->regmap_pmic,
+ S5M8767_REG_DVSRAMP,
+ S5M8767_DVS_BUCK_RAMP_MASK,
+ val << S5M8767_DVS_BUCK_RAMP_SHIFT);
+ }
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ const struct sec_voltage_desc *desc;
+ int id = pdata->regulators[i].id;
+ int enable_reg, enable_val;
+ struct regulator_dev *rdev;
+
+ 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;
+
+ ret = s5m8767_get_register(s5m8767, id, &enable_reg,
+ &enable_val);
+ if (ret) {
+ dev_err(s5m8767->dev, "error reading registers\n");
+ return ret;
+ }
+ regulators[id].enable_reg = enable_reg;
+ regulators[id].enable_mask = S5M8767_ENCTRL_MASK;
+ regulators[id].enable_val = enable_val;
+ }
+
+ config.dev = s5m8767->dev;
+ config.init_data = pdata->regulators[i].initdata;
+ config.driver_data = s5m8767;
+ config.regmap = iodev->regmap_pmic;
+ config.of_node = pdata->regulators[i].reg_node;
+ config.ena_gpiod = NULL;
+ if (pdata->regulators[i].ext_control_gpiod)
+ s5m8767_regulator_config_ext_control(s5m8767,
+ &pdata->regulators[i], &config);
+
+ rdev = devm_regulator_register(&pdev->dev, &regulators[id],
+ &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(s5m8767->dev, "regulator init failed for %d\n",
+ id);
+ return ret;
+ }
+
+ if (pdata->regulators[i].ext_control_gpiod) {
+ ret = s5m8767_enable_ext_control(s5m8767, rdev);
+ if (ret < 0) {
+ dev_err(s5m8767->dev,
+ "failed to enable gpio control over %s: %d\n",
+ rdev->desc->name, ret);
+ return ret;
+ }
+ }
+ }
+
+ 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",
+ },
+ .probe = s5m8767_pmic_probe,
+ .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 <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("SAMSUNG S5M8767 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/sc2731-regulator.c b/drivers/regulator/sc2731-regulator.c
new file mode 100644
index 000000000..eb2bdf060
--- /dev/null
+++ b/drivers/regulator/sc2731-regulator.c
@@ -0,0 +1,256 @@
+ //SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 Spreadtrum Communications Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+/*
+ * SC2731 regulator lock register
+ */
+#define SC2731_PWR_WR_PROT 0xf0c
+#define SC2731_WR_UNLOCK_VALUE 0x6e7f
+
+/*
+ * SC2731 enable register
+ */
+#define SC2731_POWER_PD_SW 0xc28
+#define SC2731_LDO_CAMA0_PD 0xcfc
+#define SC2731_LDO_CAMA1_PD 0xd04
+#define SC2731_LDO_CAMMOT_PD 0xd0c
+#define SC2731_LDO_VLDO_PD 0xd6c
+#define SC2731_LDO_EMMCCORE_PD 0xd2c
+#define SC2731_LDO_SDCORE_PD 0xd74
+#define SC2731_LDO_SDIO_PD 0xd70
+#define SC2731_LDO_WIFIPA_PD 0xd4c
+#define SC2731_LDO_USB33_PD 0xd5c
+#define SC2731_LDO_CAMD0_PD 0xd7c
+#define SC2731_LDO_CAMD1_PD 0xd84
+#define SC2731_LDO_CON_PD 0xd8c
+#define SC2731_LDO_CAMIO_PD 0xd94
+#define SC2731_LDO_SRAM_PD 0xd78
+
+/*
+ * SC2731 enable mask
+ */
+#define SC2731_DCDC_CPU0_PD_MASK BIT(4)
+#define SC2731_DCDC_CPU1_PD_MASK BIT(3)
+#define SC2731_DCDC_RF_PD_MASK BIT(11)
+#define SC2731_LDO_CAMA0_PD_MASK BIT(0)
+#define SC2731_LDO_CAMA1_PD_MASK BIT(0)
+#define SC2731_LDO_CAMMOT_PD_MASK BIT(0)
+#define SC2731_LDO_VLDO_PD_MASK BIT(0)
+#define SC2731_LDO_EMMCCORE_PD_MASK BIT(0)
+#define SC2731_LDO_SDCORE_PD_MASK BIT(0)
+#define SC2731_LDO_SDIO_PD_MASK BIT(0)
+#define SC2731_LDO_WIFIPA_PD_MASK BIT(0)
+#define SC2731_LDO_USB33_PD_MASK BIT(0)
+#define SC2731_LDO_CAMD0_PD_MASK BIT(0)
+#define SC2731_LDO_CAMD1_PD_MASK BIT(0)
+#define SC2731_LDO_CON_PD_MASK BIT(0)
+#define SC2731_LDO_CAMIO_PD_MASK BIT(0)
+#define SC2731_LDO_SRAM_PD_MASK BIT(0)
+
+/*
+ * SC2731 vsel register
+ */
+#define SC2731_DCDC_CPU0_VOL 0xc54
+#define SC2731_DCDC_CPU1_VOL 0xc64
+#define SC2731_DCDC_RF_VOL 0xcb8
+#define SC2731_LDO_CAMA0_VOL 0xd00
+#define SC2731_LDO_CAMA1_VOL 0xd08
+#define SC2731_LDO_CAMMOT_VOL 0xd10
+#define SC2731_LDO_VLDO_VOL 0xd28
+#define SC2731_LDO_EMMCCORE_VOL 0xd30
+#define SC2731_LDO_SDCORE_VOL 0xd38
+#define SC2731_LDO_SDIO_VOL 0xd40
+#define SC2731_LDO_WIFIPA_VOL 0xd50
+#define SC2731_LDO_USB33_VOL 0xd60
+#define SC2731_LDO_CAMD0_VOL 0xd80
+#define SC2731_LDO_CAMD1_VOL 0xd88
+#define SC2731_LDO_CON_VOL 0xd90
+#define SC2731_LDO_CAMIO_VOL 0xd98
+#define SC2731_LDO_SRAM_VOL 0xdB0
+
+/*
+ * SC2731 vsel register mask
+ */
+#define SC2731_DCDC_CPU0_VOL_MASK GENMASK(8, 0)
+#define SC2731_DCDC_CPU1_VOL_MASK GENMASK(8, 0)
+#define SC2731_DCDC_RF_VOL_MASK GENMASK(8, 0)
+#define SC2731_LDO_CAMA0_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_CAMA1_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_CAMMOT_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_VLDO_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_EMMCCORE_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_SDCORE_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_SDIO_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_WIFIPA_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_USB33_VOL_MASK GENMASK(7, 0)
+#define SC2731_LDO_CAMD0_VOL_MASK GENMASK(6, 0)
+#define SC2731_LDO_CAMD1_VOL_MASK GENMASK(6, 0)
+#define SC2731_LDO_CON_VOL_MASK GENMASK(6, 0)
+#define SC2731_LDO_CAMIO_VOL_MASK GENMASK(6, 0)
+#define SC2731_LDO_SRAM_VOL_MASK GENMASK(6, 0)
+
+enum sc2731_regulator_id {
+ SC2731_BUCK_CPU0,
+ SC2731_BUCK_CPU1,
+ SC2731_BUCK_RF,
+ SC2731_LDO_CAMA0,
+ SC2731_LDO_CAMA1,
+ SC2731_LDO_CAMMOT,
+ SC2731_LDO_VLDO,
+ SC2731_LDO_EMMCCORE,
+ SC2731_LDO_SDCORE,
+ SC2731_LDO_SDIO,
+ SC2731_LDO_WIFIPA,
+ SC2731_LDO_USB33,
+ SC2731_LDO_CAMD0,
+ SC2731_LDO_CAMD1,
+ SC2731_LDO_CON,
+ SC2731_LDO_CAMIO,
+ SC2731_LDO_SRAM,
+};
+
+static const struct regulator_ops sc2731_regu_linear_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+#define SC2731_REGU_LINEAR(_id, en_reg, en_mask, vreg, vmask, \
+ vstep, vmin, vmax) { \
+ .name = #_id, \
+ .of_match = of_match_ptr(#_id), \
+ .ops = &sc2731_regu_linear_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = SC2731_##_id, \
+ .owner = THIS_MODULE, \
+ .min_uV = vmin, \
+ .n_voltages = ((vmax) - (vmin)) / (vstep) + 1, \
+ .uV_step = vstep, \
+ .enable_is_inverted = true, \
+ .enable_val = 0, \
+ .enable_reg = en_reg, \
+ .enable_mask = en_mask, \
+ .vsel_reg = vreg, \
+ .vsel_mask = vmask, \
+}
+
+static struct regulator_desc regulators[] = {
+ SC2731_REGU_LINEAR(BUCK_CPU0, SC2731_POWER_PD_SW,
+ SC2731_DCDC_CPU0_PD_MASK, SC2731_DCDC_CPU0_VOL,
+ SC2731_DCDC_CPU0_VOL_MASK, 3125, 400000, 1996875),
+ SC2731_REGU_LINEAR(BUCK_CPU1, SC2731_POWER_PD_SW,
+ SC2731_DCDC_CPU1_PD_MASK, SC2731_DCDC_CPU1_VOL,
+ SC2731_DCDC_CPU1_VOL_MASK, 3125, 400000, 1996875),
+ SC2731_REGU_LINEAR(BUCK_RF, SC2731_POWER_PD_SW, SC2731_DCDC_RF_PD_MASK,
+ SC2731_DCDC_RF_VOL, SC2731_DCDC_RF_VOL_MASK,
+ 3125, 600000, 2196875),
+ SC2731_REGU_LINEAR(LDO_CAMA0, SC2731_LDO_CAMA0_PD,
+ SC2731_LDO_CAMA0_PD_MASK, SC2731_LDO_CAMA0_VOL,
+ SC2731_LDO_CAMA0_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_CAMA1, SC2731_LDO_CAMA1_PD,
+ SC2731_LDO_CAMA1_PD_MASK, SC2731_LDO_CAMA1_VOL,
+ SC2731_LDO_CAMA1_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_CAMMOT, SC2731_LDO_CAMMOT_PD,
+ SC2731_LDO_CAMMOT_PD_MASK, SC2731_LDO_CAMMOT_VOL,
+ SC2731_LDO_CAMMOT_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_VLDO, SC2731_LDO_VLDO_PD,
+ SC2731_LDO_VLDO_PD_MASK, SC2731_LDO_VLDO_VOL,
+ SC2731_LDO_VLDO_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_EMMCCORE, SC2731_LDO_EMMCCORE_PD,
+ SC2731_LDO_EMMCCORE_PD_MASK, SC2731_LDO_EMMCCORE_VOL,
+ SC2731_LDO_EMMCCORE_VOL_MASK, 10000, 1200000,
+ 3750000),
+ SC2731_REGU_LINEAR(LDO_SDCORE, SC2731_LDO_SDCORE_PD,
+ SC2731_LDO_SDCORE_PD_MASK, SC2731_LDO_SDCORE_VOL,
+ SC2731_LDO_SDCORE_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_SDIO, SC2731_LDO_SDIO_PD,
+ SC2731_LDO_SDIO_PD_MASK, SC2731_LDO_SDIO_VOL,
+ SC2731_LDO_SDIO_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_WIFIPA, SC2731_LDO_WIFIPA_PD,
+ SC2731_LDO_WIFIPA_PD_MASK, SC2731_LDO_WIFIPA_VOL,
+ SC2731_LDO_WIFIPA_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_USB33, SC2731_LDO_USB33_PD,
+ SC2731_LDO_USB33_PD_MASK, SC2731_LDO_USB33_VOL,
+ SC2731_LDO_USB33_VOL_MASK, 10000, 1200000, 3750000),
+ SC2731_REGU_LINEAR(LDO_CAMD0, SC2731_LDO_CAMD0_PD,
+ SC2731_LDO_CAMD0_PD_MASK, SC2731_LDO_CAMD0_VOL,
+ SC2731_LDO_CAMD0_VOL_MASK, 6250, 1000000, 1793750),
+ SC2731_REGU_LINEAR(LDO_CAMD1, SC2731_LDO_CAMD1_PD,
+ SC2731_LDO_CAMD1_PD_MASK, SC2731_LDO_CAMD1_VOL,
+ SC2731_LDO_CAMD1_VOL_MASK, 6250, 1000000, 1793750),
+ SC2731_REGU_LINEAR(LDO_CON, SC2731_LDO_CON_PD,
+ SC2731_LDO_CON_PD_MASK, SC2731_LDO_CON_VOL,
+ SC2731_LDO_CON_VOL_MASK, 6250, 1000000, 1793750),
+ SC2731_REGU_LINEAR(LDO_CAMIO, SC2731_LDO_CAMIO_PD,
+ SC2731_LDO_CAMIO_PD_MASK, SC2731_LDO_CAMIO_VOL,
+ SC2731_LDO_CAMIO_VOL_MASK, 6250, 1000000, 1793750),
+ SC2731_REGU_LINEAR(LDO_SRAM, SC2731_LDO_SRAM_PD,
+ SC2731_LDO_SRAM_PD_MASK, SC2731_LDO_SRAM_VOL,
+ SC2731_LDO_SRAM_VOL_MASK, 6250, 1000000, 1793750),
+};
+
+static int sc2731_regulator_unlock(struct regmap *regmap)
+{
+ return regmap_write(regmap, SC2731_PWR_WR_PROT,
+ SC2731_WR_UNLOCK_VALUE);
+}
+
+static int sc2731_regulator_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ struct regmap *regmap;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap) {
+ dev_err(&pdev->dev, "failed to get regmap.\n");
+ return -ENODEV;
+ }
+
+ ret = sc2731_regulator_unlock(regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to release regulator lock\n");
+ return ret;
+ }
+
+ config.dev = &pdev->dev;
+ config.regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ regulators[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver sc2731_regulator_driver = {
+ .driver = {
+ .name = "sc27xx-regulator",
+ },
+ .probe = sc2731_regulator_probe,
+};
+
+module_platform_driver(sc2731_regulator_driver);
+
+MODULE_AUTHOR("Chen Junhui <erick.chen@spreadtrum.com>");
+MODULE_DESCRIPTION("Spreadtrum SC2731 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/sky81452-regulator.c b/drivers/regulator/sky81452-regulator.c
new file mode 100644
index 000000000..647860611
--- /dev/null
+++ b/drivers/regulator/sky81452-regulator.c
@@ -0,0 +1,102 @@
+/*
+ * sky81452-regulator.c SKY81452 regulator driver
+ *
+ * Copyright 2014 Skyworks Solutions Inc.
+ * Author : Gyungoh Yoo <jack.yoo@skyworksinc.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+/* registers */
+#define SKY81452_REG1 0x01
+#define SKY81452_REG3 0x03
+
+/* bit mask */
+#define SKY81452_LEN 0x40
+#define SKY81452_LOUT 0x1F
+
+static struct regulator_ops sky81452_reg_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_linear_range sky81452_reg_ranges[] = {
+ REGULATOR_LINEAR_RANGE(4500000, 0, 14, 250000),
+ REGULATOR_LINEAR_RANGE(9000000, 15, 31, 1000000),
+};
+
+static const struct regulator_desc sky81452_reg = {
+ .name = "LOUT",
+ .of_match = of_match_ptr("lout"),
+ .regulators_node = of_match_ptr("regulator"),
+ .ops = &sky81452_reg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = SKY81452_LOUT + 1,
+ .linear_ranges = sky81452_reg_ranges,
+ .n_linear_ranges = ARRAY_SIZE(sky81452_reg_ranges),
+ .vsel_reg = SKY81452_REG3,
+ .vsel_mask = SKY81452_LOUT,
+ .enable_reg = SKY81452_REG1,
+ .enable_mask = SKY81452_LEN,
+};
+
+static int sky81452_reg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct regulator_init_data *init_data = dev_get_platdata(dev);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+
+ config.dev = dev->parent;
+ config.init_data = init_data;
+ config.of_node = dev->of_node;
+ config.regmap = dev_get_drvdata(dev->parent);
+
+ rdev = devm_regulator_register(dev, &sky81452_reg, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(rdev));
+ return PTR_ERR(rdev);
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static struct platform_driver sky81452_reg_driver = {
+ .driver = {
+ .name = "sky81452-regulator",
+ },
+ .probe = sky81452_reg_probe,
+};
+
+module_platform_driver(sky81452_reg_driver);
+
+MODULE_DESCRIPTION("Skyworks SKY81452 Regulator driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
new file mode 100644
index 000000000..e0a9c445e
--- /dev/null
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+/* STM32 VREFBUF registers */
+#define STM32_VREFBUF_CSR 0x00
+
+/* STM32 VREFBUF CSR bitfields */
+#define STM32_VRS GENMASK(6, 4)
+#define STM32_VRR BIT(3)
+#define STM32_HIZ BIT(1)
+#define STM32_ENVR BIT(0)
+
+struct stm32_vrefbuf {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static const unsigned int stm32_vrefbuf_voltages[] = {
+ /* Matches resp. VRS = 000b, 001b, 010b, 011b */
+ 2500000, 2048000, 1800000, 1500000,
+};
+
+static int stm32_vrefbuf_enable(struct regulator_dev *rdev)
+{
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+ u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+ int ret;
+
+ val = (val & ~STM32_HIZ) | STM32_ENVR;
+ writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+ /*
+ * Vrefbuf startup time depends on external capacitor: wait here for
+ * VRR to be set. That means output has reached expected value.
+ * ~650us sleep should be enough for caps up to 1.5uF. Use 10ms as
+ * arbitrary timeout.
+ */
+ ret = readl_poll_timeout(priv->base + STM32_VREFBUF_CSR, val,
+ val & STM32_VRR, 650, 10000);
+ if (ret) {
+ dev_err(&rdev->dev, "stm32 vrefbuf timed out!\n");
+ val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+ val = (val & ~STM32_ENVR) | STM32_HIZ;
+ writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+ }
+
+ return ret;
+}
+
+static int stm32_vrefbuf_disable(struct regulator_dev *rdev)
+{
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+ u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+ val = (val & ~STM32_ENVR) | STM32_HIZ;
+ writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+ return 0;
+}
+
+static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev)
+{
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+ return readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR;
+}
+
+static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned sel)
+{
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+ u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+ val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel);
+ writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+ return 0;
+}
+
+static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+ u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+ return FIELD_GET(STM32_VRS, val);
+}
+
+static const struct regulator_ops stm32_vrefbuf_volt_ops = {
+ .enable = stm32_vrefbuf_enable,
+ .disable = stm32_vrefbuf_disable,
+ .is_enabled = stm32_vrefbuf_is_enabled,
+ .get_voltage_sel = stm32_vrefbuf_get_voltage_sel,
+ .set_voltage_sel = stm32_vrefbuf_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_table,
+};
+
+static const struct regulator_desc stm32_vrefbuf_regu = {
+ .name = "vref",
+ .supply_name = "vdda",
+ .volt_table = stm32_vrefbuf_voltages,
+ .n_voltages = ARRAY_SIZE(stm32_vrefbuf_voltages),
+ .ops = &stm32_vrefbuf_volt_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+};
+
+static int stm32_vrefbuf_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct stm32_vrefbuf *priv;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "clk prepare failed with error %d\n", ret);
+ return ret;
+ }
+
+ config.dev = &pdev->dev;
+ config.driver_data = priv;
+ config.of_node = pdev->dev.of_node;
+ config.init_data = of_get_regulator_init_data(&pdev->dev,
+ pdev->dev.of_node,
+ &stm32_vrefbuf_regu);
+
+ rdev = regulator_register(&stm32_vrefbuf_regu, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&pdev->dev, "register failed with error %d\n", ret);
+ goto err_clk_dis;
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+
+err_clk_dis:
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static int stm32_vrefbuf_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+ regulator_unregister(rdev);
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+};
+
+static const struct of_device_id stm32_vrefbuf_of_match[] = {
+ { .compatible = "st,stm32-vrefbuf", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match);
+
+static struct platform_driver stm32_vrefbuf_driver = {
+ .probe = stm32_vrefbuf_probe,
+ .remove = stm32_vrefbuf_remove,
+ .driver = {
+ .name = "stm32-vrefbuf",
+ .of_match_table = of_match_ptr(stm32_vrefbuf_of_match),
+ },
+};
+module_platform_driver(stm32_vrefbuf_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 VREFBUF driver");
+MODULE_ALIAS("platform:stm32-vrefbuf");
diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c
new file mode 100644
index 000000000..342f5da79
--- /dev/null
+++ b/drivers/regulator/stw481x-vmmc.c
@@ -0,0 +1,104 @@
+/*
+ * Regulator driver for STw4810/STw4811 VMMC regulator.
+ *
+ * Copyright (C) 2013 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/mfd/stw481x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+static const unsigned int stw481x_vmmc_voltages[] = {
+ 1800000,
+ 1800000,
+ 2850000,
+ 3000000,
+ 1850000,
+ 2600000,
+ 2700000,
+ 3300000,
+};
+
+static struct regulator_ops stw481x_vmmc_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static struct regulator_desc vmmc_regulator = {
+ .name = "VMMC",
+ .id = 0,
+ .ops = &stw481x_vmmc_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(stw481x_vmmc_voltages),
+ .volt_table = stw481x_vmmc_voltages,
+ .enable_time = 200, /* FIXME: look this up */
+ .enable_reg = STW_CONF1,
+ .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS,
+ .enable_val = STW_CONF1_PDN_VMMC,
+ .vsel_reg = STW_CONF1,
+ .vsel_mask = STW_CONF1_VMMC_MASK,
+};
+
+static int stw481x_vmmc_regulator_probe(struct platform_device *pdev)
+{
+ struct stw481x *stw481x = dev_get_platdata(&pdev->dev);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int ret;
+
+ /* First disable the external VMMC if it's active */
+ ret = regmap_update_bits(stw481x->map, STW_CONF2,
+ STW_CONF2_VMMC_EXT, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "could not disable external VMMC\n");
+ return ret;
+ }
+
+ /* Register VMMC regulator */
+ config.dev = &pdev->dev;
+ config.driver_data = stw481x;
+ config.regmap = stw481x->map;
+ config.of_node = pdev->dev.of_node;
+ config.init_data = of_get_regulator_init_data(&pdev->dev,
+ pdev->dev.of_node,
+ &vmmc_regulator);
+
+ rdev = devm_regulator_register(&pdev->dev, &vmmc_regulator, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "error initializing STw481x VMMC regulator\n");
+ return PTR_ERR(rdev);
+ }
+
+ dev_info(&pdev->dev, "initialized STw481x VMMC regulator\n");
+ return 0;
+}
+
+static const struct of_device_id stw481x_vmmc_match[] = {
+ { .compatible = "st,stw481x-vmmc", },
+ {},
+};
+
+static struct platform_driver stw481x_vmmc_regulator_driver = {
+ .driver = {
+ .name = "stw481x-vmmc-regulator",
+ .of_match_table = stw481x_vmmc_match,
+ },
+ .probe = stw481x_vmmc_regulator_probe,
+};
+
+module_platform_driver(stw481x_vmmc_regulator_driver);
diff --git a/drivers/regulator/sy8106a-regulator.c b/drivers/regulator/sy8106a-regulator.c
new file mode 100644
index 000000000..65fbd1f0b
--- /dev/null
+++ b/drivers/regulator/sy8106a-regulator.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// sy8106a-regulator.c - Regulator device driver for SY8106A
+//
+// Copyright (C) 2016 Ondřej Jirman <megous@megous.com>
+// Copyright (c) 2017-2018 Icenowy Zheng <icenowy@aosc.io>
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define SY8106A_REG_VOUT1_SEL 0x01
+#define SY8106A_REG_VOUT_COM 0x02
+#define SY8106A_REG_VOUT1_SEL_MASK 0x7f
+#define SY8106A_DISABLE_REG BIT(0)
+/*
+ * The I2C controlled voltage will only work when this bit is set; otherwise
+ * it will behave like a fixed regulator.
+ */
+#define SY8106A_GO_BIT BIT(7)
+
+struct sy8106a {
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ u32 fixed_voltage;
+};
+
+static const struct regmap_config sy8106a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct regulator_ops sy8106a_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ /* Enabling/disabling the regulator is not yet implemented */
+};
+
+/* Default limits measured in millivolts */
+#define SY8106A_MIN_MV 680
+#define SY8106A_MAX_MV 1950
+#define SY8106A_STEP_MV 10
+
+static const struct regulator_desc sy8106a_reg = {
+ .name = "SY8106A",
+ .id = 0,
+ .ops = &sy8106a_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = ((SY8106A_MAX_MV - SY8106A_MIN_MV) / SY8106A_STEP_MV) + 1,
+ .min_uV = (SY8106A_MIN_MV * 1000),
+ .uV_step = (SY8106A_STEP_MV * 1000),
+ .vsel_reg = SY8106A_REG_VOUT1_SEL,
+ .vsel_mask = SY8106A_REG_VOUT1_SEL_MASK,
+ /*
+ * This ramp_delay is a conservative default value which works on
+ * H3/H5 boards VDD-CPUX situations.
+ */
+ .ramp_delay = 200,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * I2C driver interface functions
+ */
+static int sy8106a_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct sy8106a *chip;
+ struct device *dev = &i2c->dev;
+ struct regulator_dev *rdev = NULL;
+ struct regulator_config config = { };
+ unsigned int reg, vsel;
+ int error;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct sy8106a), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ error = of_property_read_u32(dev->of_node, "silergy,fixed-microvolt",
+ &chip->fixed_voltage);
+ if (error)
+ return error;
+
+ if (chip->fixed_voltage < SY8106A_MIN_MV * 1000 ||
+ chip->fixed_voltage > SY8106A_MAX_MV * 1000)
+ return -EINVAL;
+
+ chip->regmap = devm_regmap_init_i2c(i2c, &sy8106a_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", error);
+ return error;
+ }
+
+ config.dev = &i2c->dev;
+ config.regmap = chip->regmap;
+ config.driver_data = chip;
+
+ config.of_node = dev->of_node;
+ config.init_data = of_get_regulator_init_data(dev, dev->of_node,
+ &sy8106a_reg);
+
+ if (!config.init_data)
+ return -ENOMEM;
+
+ /* Ensure GO_BIT is enabled when probing */
+ error = regmap_read(chip->regmap, SY8106A_REG_VOUT1_SEL, &reg);
+ if (error)
+ return error;
+
+ if (!(reg & SY8106A_GO_BIT)) {
+ vsel = (chip->fixed_voltage / 1000 - SY8106A_MIN_MV) /
+ SY8106A_STEP_MV;
+
+ error = regmap_write(chip->regmap, SY8106A_REG_VOUT1_SEL,
+ vsel | SY8106A_GO_BIT);
+ if (error)
+ return error;
+ }
+
+ /* Probe regulator */
+ rdev = devm_regulator_register(&i2c->dev, &sy8106a_reg, &config);
+ if (IS_ERR(rdev)) {
+ error = PTR_ERR(rdev);
+ dev_err(&i2c->dev, "Failed to register SY8106A regulator: %d\n", error);
+ return error;
+ }
+
+ chip->rdev = rdev;
+
+ i2c_set_clientdata(i2c, chip);
+
+ return 0;
+}
+
+static const struct of_device_id sy8106a_i2c_of_match[] = {
+ { .compatible = "silergy,sy8106a" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sy8106a_i2c_of_match);
+
+static const struct i2c_device_id sy8106a_i2c_id[] = {
+ { "sy8106a", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sy8106a_i2c_id);
+
+static struct i2c_driver sy8106a_regulator_driver = {
+ .driver = {
+ .name = "sy8106a",
+ .of_match_table = of_match_ptr(sy8106a_i2c_of_match),
+ },
+ .probe = sy8106a_i2c_probe,
+ .id_table = sy8106a_i2c_id,
+};
+
+module_i2c_driver(sy8106a_regulator_driver);
+
+MODULE_AUTHOR("Ondřej Jirman <megous@megous.com>");
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
+MODULE_DESCRIPTION("Regulator device driver for Silergy SY8106A");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c
new file mode 100644
index 000000000..016330f90
--- /dev/null
+++ b/drivers/regulator/ti-abb-regulator.c
@@ -0,0 +1,902 @@
+/*
+ * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Mike Turquette <mturquette@ti.com>
+ *
+ * Copyright (C) 2012-2013 Texas Instruments, Inc.
+ * Andrii Tseglytskyi <andrii.tseglytskyi@ti.com>
+ * Nishanth Menon <nm@ti.com>
+ *
+ * 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 "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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+/*
+ * ABB LDO operating states:
+ * NOMINAL_OPP: bypasses the ABB LDO
+ * FAST_OPP: sets ABB LDO to Forward Body-Bias
+ * SLOW_OPP: sets ABB LDO to Reverse Body-Bias
+ */
+#define TI_ABB_NOMINAL_OPP 0
+#define TI_ABB_FAST_OPP 1
+#define TI_ABB_SLOW_OPP 3
+
+/**
+ * struct ti_abb_info - ABB information per voltage setting
+ * @opp_sel: one of TI_ABB macro
+ * @vset: (optional) vset value that LDOVBB needs to be overriden with.
+ *
+ * Array of per voltage entries organized in the same order as regulator_desc's
+ * volt_table list. (selector is used to index from this array)
+ */
+struct ti_abb_info {
+ u32 opp_sel;
+ u32 vset;
+};
+
+/**
+ * struct ti_abb_reg - Register description for ABB block
+ * @setup_off: setup register offset from base
+ * @control_off: control register offset from base
+ * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask
+ * @fbb_sel_mask: setup register- FBB sel mask
+ * @rbb_sel_mask: setup register- RBB sel mask
+ * @sr2_en_mask: setup register- enable mask
+ * @opp_change_mask: control register - mask to trigger LDOVBB change
+ * @opp_sel_mask: control register - mask for mode to operate
+ */
+struct ti_abb_reg {
+ u32 setup_off;
+ u32 control_off;
+
+ /* Setup register fields */
+ u32 sr2_wtcnt_value_mask;
+ u32 fbb_sel_mask;
+ u32 rbb_sel_mask;
+ u32 sr2_en_mask;
+
+ /* Control register fields */
+ u32 opp_change_mask;
+ u32 opp_sel_mask;
+};
+
+/**
+ * struct ti_abb - ABB instance data
+ * @rdesc: regulator descriptor
+ * @clk: clock(usually sysclk) supplying ABB block
+ * @base: base address of ABB block
+ * @setup_reg: setup register of ABB block
+ * @control_reg: control register of ABB block
+ * @int_base: interrupt register base address
+ * @efuse_base: (optional) efuse base address for ABB modes
+ * @ldo_base: (optional) LDOVBB vset override base address
+ * @regs: pointer to struct ti_abb_reg for ABB block
+ * @txdone_mask: mask on int_base for tranxdone interrupt
+ * @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB
+ * vset with value from efuse
+ * @ldovbb_vset_mask: mask to ldo_base for providing the VSET override
+ * @info: array to per voltage ABB configuration
+ * @current_info_idx: current index to info
+ * @settling_time: SoC specific settling time for LDO VBB
+ */
+struct ti_abb {
+ struct regulator_desc rdesc;
+ struct clk *clk;
+ void __iomem *base;
+ void __iomem *setup_reg;
+ void __iomem *control_reg;
+ void __iomem *int_base;
+ void __iomem *efuse_base;
+ void __iomem *ldo_base;
+
+ const struct ti_abb_reg *regs;
+ u32 txdone_mask;
+ u32 ldovbb_override_mask;
+ u32 ldovbb_vset_mask;
+
+ struct ti_abb_info *info;
+ int current_info_idx;
+
+ u32 settling_time;
+};
+
+/**
+ * ti_abb_rmw() - handy wrapper to set specific register bits
+ * @mask: mask for register field
+ * @value: value shifted to mask location and written
+ * @reg: register address
+ *
+ * Return: final register value (may be unused)
+ */
+static inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg)
+{
+ u32 val;
+
+ val = readl(reg);
+ val &= ~mask;
+ val |= (value << __ffs(mask)) & mask;
+ writel(val, reg);
+
+ return val;
+}
+
+/**
+ * ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status
+ * @abb: pointer to the abb instance
+ *
+ * Return: true or false
+ */
+static inline bool ti_abb_check_txdone(const struct ti_abb *abb)
+{
+ return !!(readl(abb->int_base) & abb->txdone_mask);
+}
+
+/**
+ * ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status
+ * @abb: pointer to the abb instance
+ */
+static inline void ti_abb_clear_txdone(const struct ti_abb *abb)
+{
+ writel(abb->txdone_mask, abb->int_base);
+};
+
+/**
+ * ti_abb_wait_tranx() - waits for ABB tranxdone event
+ * @dev: device
+ * @abb: pointer to the abb instance
+ *
+ * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time.
+ */
+static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb)
+{
+ int timeout = 0;
+ bool status;
+
+ while (timeout++ <= abb->settling_time) {
+ status = ti_abb_check_txdone(abb);
+ if (status)
+ return 0;
+
+ udelay(1);
+ }
+
+ dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
+ __func__, timeout, readl(abb->int_base));
+ return -ETIMEDOUT;
+}
+
+/**
+ * ti_abb_clear_all_txdone() - clears ABB tranxdone event
+ * @dev: device
+ * @abb: pointer to the abb instance
+ *
+ * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time.
+ */
+static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb)
+{
+ int timeout = 0;
+ bool status;
+
+ while (timeout++ <= abb->settling_time) {
+ ti_abb_clear_txdone(abb);
+
+ status = ti_abb_check_txdone(abb);
+ if (!status)
+ return 0;
+
+ udelay(1);
+ }
+
+ dev_warn_ratelimited(dev, "%s:TRANXDONE timeout(%duS) int=0x%08x\n",
+ __func__, timeout, readl(abb->int_base));
+ return -ETIMEDOUT;
+}
+
+/**
+ * ti_abb_program_ldovbb() - program LDOVBB register for override value
+ * @dev: device
+ * @abb: pointer to the abb instance
+ * @info: ABB info to program
+ */
+static void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb,
+ struct ti_abb_info *info)
+{
+ u32 val;
+
+ val = readl(abb->ldo_base);
+ /* clear up previous values */
+ val &= ~(abb->ldovbb_override_mask | abb->ldovbb_vset_mask);
+
+ switch (info->opp_sel) {
+ case TI_ABB_SLOW_OPP:
+ case TI_ABB_FAST_OPP:
+ val |= abb->ldovbb_override_mask;
+ val |= info->vset << __ffs(abb->ldovbb_vset_mask);
+ break;
+ }
+
+ writel(val, abb->ldo_base);
+}
+
+/**
+ * ti_abb_set_opp() - Setup ABB and LDO VBB for required bias
+ * @rdev: regulator device
+ * @abb: pointer to the abb instance
+ * @info: ABB info to program
+ *
+ * Return: 0 on success or appropriate error value when fails
+ */
+static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb,
+ struct ti_abb_info *info)
+{
+ const struct ti_abb_reg *regs = abb->regs;
+ struct device *dev = &rdev->dev;
+ int ret;
+
+ ret = ti_abb_clear_all_txdone(dev, abb);
+ if (ret)
+ goto out;
+
+ ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, abb->setup_reg);
+
+ switch (info->opp_sel) {
+ case TI_ABB_SLOW_OPP:
+ ti_abb_rmw(regs->rbb_sel_mask, 1, abb->setup_reg);
+ break;
+ case TI_ABB_FAST_OPP:
+ ti_abb_rmw(regs->fbb_sel_mask, 1, abb->setup_reg);
+ break;
+ }
+
+ /* program next state of ABB ldo */
+ ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, abb->control_reg);
+
+ /*
+ * program LDO VBB vset override if needed for !bypass mode
+ * XXX: Do not switch sequence - for !bypass, LDO override reset *must*
+ * be performed *before* switch to bias mode else VBB glitches.
+ */
+ if (abb->ldo_base && info->opp_sel != TI_ABB_NOMINAL_OPP)
+ ti_abb_program_ldovbb(dev, abb, info);
+
+ /* Initiate ABB ldo change */
+ ti_abb_rmw(regs->opp_change_mask, 1, abb->control_reg);
+
+ /* Wait for ABB LDO to complete transition to new Bias setting */
+ ret = ti_abb_wait_txdone(dev, abb);
+ if (ret)
+ goto out;
+
+ ret = ti_abb_clear_all_txdone(dev, abb);
+ if (ret)
+ goto out;
+
+ /*
+ * Reset LDO VBB vset override bypass mode
+ * XXX: Do not switch sequence - for bypass, LDO override reset *must*
+ * be performed *after* switch to bypass else VBB glitches.
+ */
+ if (abb->ldo_base && info->opp_sel == TI_ABB_NOMINAL_OPP)
+ ti_abb_program_ldovbb(dev, abb, info);
+
+out:
+ return ret;
+}
+
+/**
+ * ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO
+ * @rdev: regulator device
+ * @sel: selector to index into required ABB LDO settings (maps to
+ * regulator descriptor's volt_table)
+ *
+ * Return: 0 on success or appropriate error value when fails
+ */
+static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct ti_abb *abb = rdev_get_drvdata(rdev);
+ struct device *dev = &rdev->dev;
+ struct ti_abb_info *info, *oinfo;
+ int ret = 0;
+
+ if (!abb) {
+ dev_err_ratelimited(dev, "%s: No regulator drvdata\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (!desc->n_voltages || !abb->info) {
+ dev_err_ratelimited(dev,
+ "%s: No valid voltage table entries?\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (sel >= desc->n_voltages) {
+ dev_err(dev, "%s: sel idx(%d) >= n_voltages(%d)\n", __func__,
+ sel, desc->n_voltages);
+ return -EINVAL;
+ }
+
+ /* If we are in the same index as we were, nothing to do here! */
+ if (sel == abb->current_info_idx) {
+ dev_dbg(dev, "%s: Already at sel=%d\n", __func__, sel);
+ return ret;
+ }
+
+ info = &abb->info[sel];
+ /*
+ * When Linux kernel is starting up, we are'nt sure of the
+ * Bias configuration that bootloader has configured.
+ * So, we get to know the actual setting the first time
+ * we are asked to transition.
+ */
+ if (abb->current_info_idx == -EINVAL)
+ goto just_set_abb;
+
+ /* If data is exactly the same, then just update index, no change */
+ oinfo = &abb->info[abb->current_info_idx];
+ if (!memcmp(info, oinfo, sizeof(*info))) {
+ dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__,
+ sel, abb->current_info_idx);
+ goto out;
+ }
+
+just_set_abb:
+ ret = ti_abb_set_opp(rdev, abb, info);
+
+out:
+ if (!ret)
+ abb->current_info_idx = sel;
+ else
+ dev_err_ratelimited(dev,
+ "%s: Volt[%d] idx[%d] mode[%d] Fail(%d)\n",
+ __func__, desc->volt_table[sel], sel,
+ info->opp_sel, ret);
+ return ret;
+}
+
+/**
+ * ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting
+ * @rdev: regulator device
+ *
+ * Return: 0 on success or appropriate error value when fails
+ */
+static int ti_abb_get_voltage_sel(struct regulator_dev *rdev)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct ti_abb *abb = rdev_get_drvdata(rdev);
+ struct device *dev = &rdev->dev;
+
+ if (!abb) {
+ dev_err_ratelimited(dev, "%s: No regulator drvdata\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (!desc->n_voltages || !abb->info) {
+ dev_err_ratelimited(dev,
+ "%s: No valid voltage table entries?\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (abb->current_info_idx >= (int)desc->n_voltages) {
+ dev_err(dev, "%s: Corrupted data? idx(%d) >= n_voltages(%d)\n",
+ __func__, abb->current_info_idx, desc->n_voltages);
+ return -EINVAL;
+ }
+
+ return abb->current_info_idx;
+}
+
+/**
+ * ti_abb_init_timings() - setup ABB clock timing for the current platform
+ * @dev: device
+ * @abb: pointer to the abb instance
+ *
+ * Return: 0 if timing is updated, else returns error result.
+ */
+static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb)
+{
+ u32 clock_cycles;
+ u32 clk_rate, sr2_wt_cnt_val, cycle_rate;
+ const struct ti_abb_reg *regs = abb->regs;
+ int ret;
+ char *pname = "ti,settling-time";
+
+ /* read device tree properties */
+ ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time);
+ if (ret) {
+ dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
+ return ret;
+ }
+
+ /* ABB LDO cannot be settle in 0 time */
+ if (!abb->settling_time) {
+ dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
+ return -EINVAL;
+ }
+
+ pname = "ti,clock-cycles";
+ ret = of_property_read_u32(dev->of_node, pname, &clock_cycles);
+ if (ret) {
+ dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
+ return ret;
+ }
+ /* ABB LDO cannot be settle in 0 clock cycles */
+ if (!clock_cycles) {
+ dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
+ return -EINVAL;
+ }
+
+ abb->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(abb->clk)) {
+ ret = PTR_ERR(abb->clk);
+ dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret);
+ return ret;
+ }
+
+ /*
+ * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a
+ * transition and must be programmed with the correct time at boot.
+ * The value programmed into the register is the number of SYS_CLK
+ * clock cycles that match a given wall time profiled for the ldo.
+ * This value depends on:
+ * settling time of ldo in micro-seconds (varies per OMAP family)
+ * # of clock cycles per SYS_CLK period (varies per OMAP family)
+ * the SYS_CLK frequency in MHz (varies per board)
+ * The formula is:
+ *
+ * ldo settling time (in micro-seconds)
+ * SR2_WTCNT_VALUE = ------------------------------------------
+ * (# system clock cycles) * (sys_clk period)
+ *
+ * Put another way:
+ *
+ * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate))
+ *
+ * To avoid dividing by zero multiply both "# clock cycles" and
+ * "settling time" by 10 such that the final result is the one we want.
+ */
+
+ /* Convert SYS_CLK rate to MHz & prevent divide by zero */
+ clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000);
+
+ /* Calculate cycle rate */
+ cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate);
+
+ /* Calulate SR2_WTCNT_VALUE */
+ sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate);
+
+ dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__,
+ clk_get_rate(abb->clk), sr2_wt_cnt_val);
+
+ ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg);
+
+ return 0;
+}
+
+/**
+ * ti_abb_init_table() - Initialize ABB table from device tree
+ * @dev: device
+ * @abb: pointer to the abb instance
+ * @rinit_data: regulator initdata
+ *
+ * Return: 0 on success or appropriate error value when fails
+ */
+static int ti_abb_init_table(struct device *dev, struct ti_abb *abb,
+ struct regulator_init_data *rinit_data)
+{
+ struct ti_abb_info *info;
+ const u32 num_values = 6;
+ char *pname = "ti,abb_info";
+ u32 i;
+ unsigned int *volt_table;
+ int num_entries, min_uV = INT_MAX, max_uV = 0;
+ struct regulation_constraints *c = &rinit_data->constraints;
+
+ /*
+ * Each abb_info is a set of n-tuple, where n is num_values, consisting
+ * of voltage and a set of detection logic for ABB information for that
+ * voltage to apply.
+ */
+ num_entries = of_property_count_u32_elems(dev->of_node, pname);
+ if (num_entries < 0) {
+ dev_err(dev, "No '%s' property?\n", pname);
+ return num_entries;
+ }
+
+ if (!num_entries || (num_entries % num_values)) {
+ dev_err(dev, "All '%s' list entries need %d vals\n", pname,
+ num_values);
+ return -EINVAL;
+ }
+ num_entries /= num_values;
+
+ info = devm_kcalloc(dev, num_entries, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ abb->info = info;
+
+ volt_table = devm_kcalloc(dev, num_entries, sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!volt_table)
+ return -ENOMEM;
+
+ abb->rdesc.n_voltages = num_entries;
+ abb->rdesc.volt_table = volt_table;
+ /* We do not know where the OPP voltage is at the moment */
+ abb->current_info_idx = -EINVAL;
+
+ for (i = 0; i < num_entries; i++, info++, volt_table++) {
+ u32 efuse_offset, rbb_mask, fbb_mask, vset_mask;
+ u32 efuse_val;
+
+ /* NOTE: num_values should equal to entries picked up here */
+ of_property_read_u32_index(dev->of_node, pname, i * num_values,
+ volt_table);
+ of_property_read_u32_index(dev->of_node, pname,
+ i * num_values + 1, &info->opp_sel);
+ of_property_read_u32_index(dev->of_node, pname,
+ i * num_values + 2, &efuse_offset);
+ of_property_read_u32_index(dev->of_node, pname,
+ i * num_values + 3, &rbb_mask);
+ of_property_read_u32_index(dev->of_node, pname,
+ i * num_values + 4, &fbb_mask);
+ of_property_read_u32_index(dev->of_node, pname,
+ i * num_values + 5, &vset_mask);
+
+ dev_dbg(dev,
+ "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n",
+ i, *volt_table, info->opp_sel, efuse_offset, rbb_mask,
+ fbb_mask, vset_mask);
+
+ /* Find min/max for voltage set */
+ if (min_uV > *volt_table)
+ min_uV = *volt_table;
+ if (max_uV < *volt_table)
+ max_uV = *volt_table;
+
+ if (!abb->efuse_base) {
+ /* Ignore invalid data, but warn to help cleanup */
+ if (efuse_offset || rbb_mask || fbb_mask || vset_mask)
+ dev_err(dev, "prop '%s': v=%d,bad efuse/mask\n",
+ pname, *volt_table);
+ goto check_abb;
+ }
+
+ efuse_val = readl(abb->efuse_base + efuse_offset);
+
+ /* Use ABB recommendation from Efuse */
+ if (efuse_val & rbb_mask)
+ info->opp_sel = TI_ABB_SLOW_OPP;
+ else if (efuse_val & fbb_mask)
+ info->opp_sel = TI_ABB_FAST_OPP;
+ else if (rbb_mask || fbb_mask)
+ info->opp_sel = TI_ABB_NOMINAL_OPP;
+
+ dev_dbg(dev,
+ "[%d]v=%d efusev=0x%x final ABB=%d\n",
+ i, *volt_table, efuse_val, info->opp_sel);
+
+ /* Use recommended Vset bits from Efuse */
+ if (!abb->ldo_base) {
+ if (vset_mask)
+ dev_err(dev, "prop'%s':v=%d vst=%x LDO base?\n",
+ pname, *volt_table, vset_mask);
+ continue;
+ }
+ info->vset = (efuse_val & vset_mask) >> __ffs(vset_mask);
+ dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset);
+check_abb:
+ switch (info->opp_sel) {
+ case TI_ABB_NOMINAL_OPP:
+ case TI_ABB_FAST_OPP:
+ case TI_ABB_SLOW_OPP:
+ /* Valid values */
+ break;
+ default:
+ dev_err(dev, "%s:[%d]v=%d, ABB=%d is invalid! Abort!\n",
+ __func__, i, *volt_table, info->opp_sel);
+ return -EINVAL;
+ }
+ }
+
+ /* Setup the min/max voltage constraints from the supported list */
+ c->min_uV = min_uV;
+ c->max_uV = max_uV;
+
+ return 0;
+}
+
+static struct regulator_ops ti_abb_reg_ops = {
+ .list_voltage = regulator_list_voltage_table,
+
+ .set_voltage_sel = ti_abb_set_voltage_sel,
+ .get_voltage_sel = ti_abb_get_voltage_sel,
+};
+
+/* Default ABB block offsets, IF this changes in future, create new one */
+static const struct ti_abb_reg abb_regs_v1 = {
+ /* WARNING: registers are wrongly documented in TRM */
+ .setup_off = 0x04,
+ .control_off = 0x00,
+
+ .sr2_wtcnt_value_mask = (0xff << 8),
+ .fbb_sel_mask = (0x01 << 2),
+ .rbb_sel_mask = (0x01 << 1),
+ .sr2_en_mask = (0x01 << 0),
+
+ .opp_change_mask = (0x01 << 2),
+ .opp_sel_mask = (0x03 << 0),
+};
+
+static const struct ti_abb_reg abb_regs_v2 = {
+ .setup_off = 0x00,
+ .control_off = 0x04,
+
+ .sr2_wtcnt_value_mask = (0xff << 8),
+ .fbb_sel_mask = (0x01 << 2),
+ .rbb_sel_mask = (0x01 << 1),
+ .sr2_en_mask = (0x01 << 0),
+
+ .opp_change_mask = (0x01 << 2),
+ .opp_sel_mask = (0x03 << 0),
+};
+
+static const struct ti_abb_reg abb_regs_generic = {
+ .sr2_wtcnt_value_mask = (0xff << 8),
+ .fbb_sel_mask = (0x01 << 2),
+ .rbb_sel_mask = (0x01 << 1),
+ .sr2_en_mask = (0x01 << 0),
+
+ .opp_change_mask = (0x01 << 2),
+ .opp_sel_mask = (0x03 << 0),
+};
+
+static const struct of_device_id ti_abb_of_match[] = {
+ {.compatible = "ti,abb-v1", .data = &abb_regs_v1},
+ {.compatible = "ti,abb-v2", .data = &abb_regs_v2},
+ {.compatible = "ti,abb-v3", .data = &abb_regs_generic},
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, ti_abb_of_match);
+
+/**
+ * ti_abb_probe() - Initialize an ABB ldo instance
+ * @pdev: ABB platform device
+ *
+ * Initializes an individual ABB LDO for required Body-Bias. ABB is used to
+ * addional bias supply to SoC modules for power savings or mandatory stability
+ * configuration at certain Operating Performance Points(OPPs).
+ *
+ * Return: 0 on success or appropriate error value when fails
+ */
+static int ti_abb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct resource *res;
+ struct ti_abb *abb;
+ struct regulator_init_data *initdata = NULL;
+ struct regulator_dev *rdev = NULL;
+ struct regulator_desc *desc;
+ struct regulation_constraints *c;
+ struct regulator_config config = { };
+ char *pname;
+ int ret = 0;
+
+ match = of_match_device(ti_abb_of_match, dev);
+ if (!match) {
+ /* We do not expect this to happen */
+ dev_err(dev, "%s: Unable to match device\n", __func__);
+ return -ENODEV;
+ }
+ if (!match->data) {
+ dev_err(dev, "%s: Bad data in match\n", __func__);
+ return -EINVAL;
+ }
+
+ abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL);
+ if (!abb)
+ return -ENOMEM;
+ abb->regs = match->data;
+
+ /* Map ABB resources */
+ if (abb->regs->setup_off || abb->regs->control_off) {
+ pname = "base-address";
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+ abb->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(abb->base))
+ return PTR_ERR(abb->base);
+
+ abb->setup_reg = abb->base + abb->regs->setup_off;
+ abb->control_reg = abb->base + abb->regs->control_off;
+
+ } else {
+ pname = "control-address";
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+ abb->control_reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(abb->control_reg))
+ return PTR_ERR(abb->control_reg);
+
+ pname = "setup-address";
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+ abb->setup_reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(abb->setup_reg))
+ return PTR_ERR(abb->setup_reg);
+ }
+
+ pname = "int-address";
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+ if (!res) {
+ dev_err(dev, "Missing '%s' IO resource\n", pname);
+ return -ENODEV;
+ }
+ /*
+ * We may have shared interrupt register offsets which are
+ * write-1-to-clear between domains ensuring exclusivity.
+ */
+ abb->int_base = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (!abb->int_base) {
+ dev_err(dev, "Unable to map '%s'\n", pname);
+ return -ENOMEM;
+ }
+
+ /* Map Optional resources */
+ pname = "efuse-address";
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+ if (!res) {
+ dev_dbg(dev, "Missing '%s' IO resource\n", pname);
+ ret = -ENODEV;
+ goto skip_opt;
+ }
+
+ /*
+ * We may have shared efuse register offsets which are read-only
+ * between domains
+ */
+ abb->efuse_base = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (!abb->efuse_base) {
+ dev_err(dev, "Unable to map '%s'\n", pname);
+ return -ENOMEM;
+ }
+
+ pname = "ldo-address";
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
+ if (!res) {
+ dev_dbg(dev, "Missing '%s' IO resource\n", pname);
+ ret = -ENODEV;
+ goto skip_opt;
+ }
+ abb->ldo_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(abb->ldo_base))
+ return PTR_ERR(abb->ldo_base);
+
+ /* IF ldo_base is set, the following are mandatory */
+ pname = "ti,ldovbb-override-mask";
+ ret =
+ of_property_read_u32(pdev->dev.of_node, pname,
+ &abb->ldovbb_override_mask);
+ if (ret) {
+ dev_err(dev, "Missing '%s' (%d)\n", pname, ret);
+ return ret;
+ }
+ if (!abb->ldovbb_override_mask) {
+ dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
+ return -EINVAL;
+ }
+
+ pname = "ti,ldovbb-vset-mask";
+ ret =
+ of_property_read_u32(pdev->dev.of_node, pname,
+ &abb->ldovbb_vset_mask);
+ if (ret) {
+ dev_err(dev, "Missing '%s' (%d)\n", pname, ret);
+ return ret;
+ }
+ if (!abb->ldovbb_vset_mask) {
+ dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
+ return -EINVAL;
+ }
+
+skip_opt:
+ pname = "ti,tranxdone-status-mask";
+ ret =
+ of_property_read_u32(pdev->dev.of_node, pname,
+ &abb->txdone_mask);
+ if (ret) {
+ dev_err(dev, "Missing '%s' (%d)\n", pname, ret);
+ return ret;
+ }
+ if (!abb->txdone_mask) {
+ dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
+ return -EINVAL;
+ }
+
+ initdata = of_get_regulator_init_data(dev, pdev->dev.of_node,
+ &abb->rdesc);
+ if (!initdata) {
+ dev_err(dev, "%s: Unable to alloc regulator init data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* init ABB opp_sel table */
+ ret = ti_abb_init_table(dev, abb, initdata);
+ if (ret)
+ return ret;
+
+ /* init ABB timing */
+ ret = ti_abb_init_timings(dev, abb);
+ if (ret)
+ return ret;
+
+ desc = &abb->rdesc;
+ desc->name = dev_name(dev);
+ desc->owner = THIS_MODULE;
+ desc->type = REGULATOR_VOLTAGE;
+ desc->ops = &ti_abb_reg_ops;
+
+ c = &initdata->constraints;
+ if (desc->n_voltages > 1)
+ c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
+ c->always_on = true;
+
+ config.dev = dev;
+ config.init_data = initdata;
+ config.driver_data = abb;
+ config.of_node = pdev->dev.of_node;
+
+ rdev = devm_regulator_register(dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "%s: failed to register regulator(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ /* Enable the ldo if not already done by bootloader */
+ ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->setup_reg);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:ti_abb");
+
+static struct platform_driver ti_abb_driver = {
+ .probe = ti_abb_probe,
+ .driver = {
+ .name = "ti_abb",
+ .of_match_table = of_match_ptr(ti_abb_of_match),
+ },
+};
+module_platform_driver(ti_abb_driver);
+
+MODULE_DESCRIPTION("Texas Instruments ABB LDO regulator driver");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c
new file mode 100644
index 000000000..c139890c1
--- /dev/null
+++ b/drivers/regulator/tps51632-regulator.c
@@ -0,0 +1,388 @@
+/*
+ * 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 <ldewangan@nvidia.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.
+ *
+ * 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 <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/tps51632-regulator.h>
+#include <linux/slab.h>
+
+/* 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_VOLTAGE 500000
+#define TPS51632_MAX_VOLTAGE 1520000
+#define TPS51632_VOLTAGE_STEP_10mV 10000
+#define TPS51632_VOLTAGE_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_VOLTAGE, \
+ TPS51632_VOLTAGE_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;
+ int ret;
+
+ if (ramp_delay == 0)
+ bit = 0;
+ else
+ bit = DIV_ROUND_UP(ramp_delay, 6000) - 1;
+
+ 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,
+ const struct regulator_desc *desc)
+{
+ struct tps51632_regulator_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node,
+ desc);
+ 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_VOLTAGE;
+ pdata->max_voltage_uV = pdata->reg_init_data->constraints.max_uV ? :
+ TPS51632_MAX_VOLTAGE;
+ return pdata;
+}
+#else
+static struct tps51632_regulator_platform_data *
+ of_get_tps51632_platform_data(struct device *dev,
+ const struct regulator_desc *desc)
+{
+ 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;
+ }
+ }
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->dev = &client->dev;
+ tps->desc.name = client->name;
+ tps->desc.id = 0;
+ tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY;
+ tps->desc.min_uV = TPS51632_MIN_VOLTAGE;
+ tps->desc.uV_step = TPS51632_VOLTAGE_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;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata && client->dev.of_node)
+ pdata = of_get_tps51632_platform_data(&client->dev, &tps->desc);
+ if (!pdata) {
+ dev_err(&client->dev, "No Platform data\n");
+ return -EINVAL;
+ }
+
+ if (pdata->enable_pwm_dvfs) {
+ if ((pdata->base_voltage_uV < TPS51632_MIN_VOLTAGE) ||
+ (pdata->base_voltage_uV > TPS51632_MAX_VOLTAGE)) {
+ dev_err(&client->dev, "Invalid base_voltage_uV setting\n");
+ return -EINVAL;
+ }
+
+ if ((pdata->max_voltage_uV) &&
+ ((pdata->max_voltage_uV < TPS51632_MIN_VOLTAGE) ||
+ (pdata->max_voltage_uV > TPS51632_MAX_VOLTAGE))) {
+ dev_err(&client->dev, "Invalid max_voltage_uV setting\n");
+ return -EINVAL;
+ }
+ }
+
+ 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 = devm_regulator_register(&client->dev, &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 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",
+ .of_match_table = of_match_ptr(tps51632_of_match),
+ },
+ .probe = tps51632_probe,
+ .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 <ldewangan@nvidia.com>");
+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 000000000..584ef3ded
--- /dev/null
+++ b/drivers/regulator/tps6105x-regulator.c
@@ -0,0 +1,114 @@
+/*
+ * 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 <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6105x.h>
+
+static const unsigned int tps6105x_voltages[] = {
+ 4500000,
+ 5000000,
+ 5250000,
+ 5000000, /* There is an additional 5V */
+};
+
+static struct regulator_ops tps6105x_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .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,
+ .vsel_reg = TPS6105X_REG_0,
+ .vsel_mask = TPS6105X_REG0_VOLTAGE_MASK,
+ .enable_reg = TPS6105X_REG_0,
+ .enable_mask = TPS6105X_REG0_MODE_MASK,
+ .enable_val = TPS6105X_REG0_MODE_VOLTAGE <<
+ TPS6105X_REG0_MODE_SHIFT,
+};
+
+/*
+ * 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;
+ config.regmap = tps6105x->regmap;
+
+ /* Register regulator with framework */
+ tps6105x->regulator = devm_regulator_register(&pdev->dev,
+ &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 struct platform_driver tps6105x_regulator_driver = {
+ .driver = {
+ .name = "tps6105x-regulator",
+ },
+ .probe = tps6105x_regulator_probe,
+};
+
+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 <linus.walleij@linaro.org>");
+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 000000000..f6a6d36a6
--- /dev/null
+++ b/drivers/regulator/tps62360-regulator.c
@@ -0,0 +1,539 @@
+/*
+ * tps62360.c -- TI tps62360
+ *
+ * Driver for processor core supply tps62360, tps62361B, tps62362 and tps62363.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.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.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/tps62360.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+/* 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,
+ const struct regulator_desc *desc)
+{
+ struct tps62360_regulator_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node,
+ desc);
+ 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 = dev_get_platdata(&client->dev);
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->desc.name = client->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;
+
+ 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)(long)match->data;
+ if (!pdata)
+ pdata = of_get_tps62360_platform_data(&client->dev,
+ &tps->desc);
+ } else if (id) {
+ chip_id = id->driver_data;
+ } else {
+ dev_err(&client->dev, "No device tree match or id table match found\n");
+ return -ENODEV;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "%s(): Platform data not found\n",
+ __func__);
+ return -EIO;
+ }
+
+ 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->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 = devm_regulator_register(&client->dev, &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;
+}
+
+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",
+ .of_match_table = of_match_ptr(tps62360_of_match),
+ },
+ .probe = tps62360_probe,
+ .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 <ldewangan@nvidia.com>");
+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 000000000..5ca6d2130
--- /dev/null
+++ b/drivers/regulator/tps65023-regulator.c
@@ -0,0 +1,365 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+/* 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
+
+#define TPS65023_REGULATOR_DCDC(_num, _t, _em) \
+ { \
+ .name = "VDCDC"#_num, \
+ .of_match = of_match_ptr("VDCDC"#_num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = TPS65023_DCDC_##_num, \
+ .n_voltages = ARRAY_SIZE(_t), \
+ .ops = &tps65023_dcdc_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .volt_table = _t, \
+ .vsel_reg = TPS65023_REG_DEF_CORE, \
+ .vsel_mask = ARRAY_SIZE(_t) - 1, \
+ .enable_mask = _em, \
+ .enable_reg = TPS65023_REG_REG_CTRL, \
+ .apply_reg = TPS65023_REG_CON_CTRL2, \
+ .apply_bit = TPS65023_REG_CTRL2_GO, \
+ } \
+
+#define TPS65023_REGULATOR_LDO(_num, _t, _vm) \
+ { \
+ .name = "LDO"#_num, \
+ .of_match = of_match_ptr("LDO"#_num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = TPS65023_LDO_##_num, \
+ .n_voltages = ARRAY_SIZE(_t), \
+ .ops = &tps65023_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .volt_table = _t, \
+ .vsel_reg = TPS65023_REG_LDO_CTRL, \
+ .vsel_mask = _vm, \
+ .enable_mask = 1 << (_num), \
+ .enable_reg = TPS65023_REG_REG_CTRL, \
+ } \
+
+/* 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,
+};
+
+/* PMIC details */
+struct tps_pmic {
+ struct regulator_dev *rdev[TPS65023_NUM_REGULATOR];
+ const struct tps_driver_data *driver_data;
+ struct regmap *regmap;
+};
+
+/* Struct passed as driver data */
+struct tps_driver_data {
+ const struct regulator_desc *desc;
+ 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->driver_data->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->driver_data->core_regulator)
+ return -EINVAL;
+
+ return regulator_set_voltage_sel_regmap(dev, selector);
+}
+
+/* Operations permitted on VDCDCx */
+static const 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 const 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 const struct regmap_config tps65023_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct regulator_desc tps65020_regulators[] = {
+ TPS65023_REGULATOR_DCDC(1, DCDC_FIXED_3300000_VSEL_table, 0x20),
+ TPS65023_REGULATOR_DCDC(2, DCDC_FIXED_1800000_VSEL_table, 0x10),
+ TPS65023_REGULATOR_DCDC(3, VCORE_VSEL_table, 0x08),
+ TPS65023_REGULATOR_LDO(1, TPS65020_LDO_VSEL_table, 0x07),
+ TPS65023_REGULATOR_LDO(2, TPS65020_LDO_VSEL_table, 0x70),
+};
+
+static const struct regulator_desc tps65021_regulators[] = {
+ TPS65023_REGULATOR_DCDC(1, DCDC_FIXED_3300000_VSEL_table, 0x20),
+ TPS65023_REGULATOR_DCDC(2, DCDC_FIXED_1800000_VSEL_table, 0x10),
+ TPS65023_REGULATOR_DCDC(3, VCORE_VSEL_table, 0x08),
+ TPS65023_REGULATOR_LDO(1, TPS65023_LDO1_VSEL_table, 0x07),
+ TPS65023_REGULATOR_LDO(2, TPS65023_LDO2_VSEL_table, 0x70),
+};
+
+static const struct regulator_desc tps65023_regulators[] = {
+ TPS65023_REGULATOR_DCDC(1, VCORE_VSEL_table, 0x20),
+ TPS65023_REGULATOR_DCDC(2, DCDC_FIXED_3300000_VSEL_table, 0x10),
+ TPS65023_REGULATOR_DCDC(3, DCDC_FIXED_1800000_VSEL_table, 0x08),
+ TPS65023_REGULATOR_LDO(1, TPS65023_LDO1_VSEL_table, 0x07),
+ TPS65023_REGULATOR_LDO(2, TPS65023_LDO2_VSEL_table, 0x70),
+};
+
+static struct tps_driver_data tps65020_drv_data = {
+ .desc = tps65020_regulators,
+ .core_regulator = TPS65023_DCDC_3,
+};
+
+static struct tps_driver_data tps65021_drv_data = {
+ .desc = tps65021_regulators,
+ .core_regulator = TPS65023_DCDC_3,
+};
+
+static struct tps_driver_data tps65023_drv_data = {
+ .desc = tps65023_regulators,
+ .core_regulator = TPS65023_DCDC_1,
+};
+
+static int tps_65023_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = dev_get_platdata(&client->dev);
+ struct regulator_config config = { };
+ struct tps_pmic *tps;
+ int i;
+ int error;
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->driver_data = (struct tps_driver_data *)id->driver_data;
+
+ 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 */
+ config.dev = &client->dev;
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+
+ for (i = 0; i < TPS65023_NUM_REGULATOR; i++) {
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ /* Register the regulators */
+ tps->rdev[i] = devm_regulator_register(&client->dev,
+ &tps->driver_data->desc[i], &config);
+ if (IS_ERR(tps->rdev[i])) {
+ dev_err(&client->dev, "failed to register %s\n",
+ id->name);
+ return PTR_ERR(tps->rdev[i]);
+ }
+ }
+
+ 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, 0);
+
+ return 0;
+}
+
+static const struct of_device_id tps65023_of_match[] = {
+ { .compatible = "ti,tps65020", .data = &tps65020_drv_data},
+ { .compatible = "ti,tps65021", .data = &tps65021_drv_data},
+ { .compatible = "ti,tps65023", .data = &tps65023_drv_data},
+ {},
+};
+MODULE_DEVICE_TABLE(of, tps65023_of_match);
+
+static const struct i2c_device_id tps_65023_id[] = {
+ {
+ .name = "tps65023",
+ .driver_data = (kernel_ulong_t)&tps65023_drv_data
+ }, {
+ .name = "tps65021",
+ .driver_data = (kernel_ulong_t)&tps65021_drv_data
+ }, {
+ .name = "tps65020",
+ .driver_data = (kernel_ulong_t)&tps65020_drv_data
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps_65023_id);
+
+static struct i2c_driver tps_65023_i2c_driver = {
+ .driver = {
+ .name = "tps65023",
+ .of_match_table = of_match_ptr(tps65023_of_match),
+ },
+ .probe = tps_65023_probe,
+ .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 000000000..c179a3a22
--- /dev/null
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -0,0 +1,541 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/tps6507x.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/mfd/tps6507x.h>
+#include <linux/regulator/of_regulator.h>
+
+/* 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<<shift) ? 1 : 0;
+}
+
+static int tps6507x_pmic_enable(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_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,
+};
+
+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;
+ 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)
+ return NULL;
+
+ regulators = of_get_child_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);
+ of_node_put(regulators);
+ 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)
+ 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(&reg_data[idx], matches[idx].init_data,
+ sizeof(struct regulator_init_data));
+
+ }
+
+ return tps_board;
+}
+
+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 (IS_ENABLED(CONFIG_OF) && !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 = devm_regulator_register(&pdev->dev, &tps->desc[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps6507x_dev->dev,
+ "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+
+ /* Save regulator for cleanup */
+ tps->rdev[i] = rdev;
+ }
+
+ tps6507x_dev->pmic = tps;
+ platform_set_drvdata(pdev, tps6507x_dev);
+
+ return 0;
+}
+
+static struct platform_driver tps6507x_pmic_driver = {
+ .driver = {
+ .name = "tps6507x-pmic",
+ },
+ .probe = tps6507x_pmic_probe,
+};
+
+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/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c
new file mode 100644
index 000000000..5a5e9b5bf
--- /dev/null
+++ b/drivers/regulator/tps65086-regulator.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Author: Andrew F. Davis <afd@ti.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65912 driver
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/tps65086.h>
+
+enum tps65086_regulators { BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, LDOA1,
+ LDOA2, LDOA3, SWA1, SWB1, SWB2, VTT };
+
+#define TPS65086_REGULATOR(_name, _of, _id, _nv, _vr, _vm, _er, _em, _lr, _dr, _dm) \
+ [_id] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .of_parse_cb = tps65086_of_parse_cb, \
+ .id = _id, \
+ .ops = &reg_ops, \
+ .n_voltages = _nv, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ .volt_table = NULL, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = ARRAY_SIZE(_lr), \
+ }, \
+ .decay_reg = _dr, \
+ .decay_mask = _dm, \
+ }
+
+#define TPS65086_SWITCH(_name, _of, _id, _er, _em) \
+ [_id] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .of_parse_cb = tps65086_of_parse_cb, \
+ .id = _id, \
+ .ops = &switch_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ }, \
+ }
+
+struct tps65086_regulator {
+ struct regulator_desc desc;
+ unsigned int decay_reg;
+ unsigned int decay_mask;
+};
+
+static const struct regulator_linear_range tps65086_10mv_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(410000, 0x1, 0x7F, 10000),
+};
+
+static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0),
+ REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000),
+};
+
+static const struct regulator_linear_range tps65086_buck345_25mv_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(425000, 0x1, 0x7F, 25000),
+};
+
+static const struct regulator_linear_range tps65086_ldoa1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1350000, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 0x1, 0x7, 100000),
+ REGULATOR_LINEAR_RANGE(2300000, 0x8, 0xB, 100000),
+ REGULATOR_LINEAR_RANGE(2850000, 0xC, 0xD, 150000),
+ REGULATOR_LINEAR_RANGE(3300000, 0xE, 0xE, 0),
+};
+
+static const struct regulator_linear_range tps65086_ldoa23_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x0, 0xD, 50000),
+ REGULATOR_LINEAR_RANGE(1400000, 0xE, 0xF, 100000),
+};
+
+/* Operations permitted on regulators */
+static struct regulator_ops reg_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+};
+
+/* Operations permitted on load switches */
+static struct regulator_ops switch_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static int tps65086_of_parse_cb(struct device_node *dev,
+ const struct regulator_desc *desc,
+ struct regulator_config *config);
+
+static struct tps65086_regulator regulators[] = {
+ TPS65086_REGULATOR("BUCK1", "buck1", BUCK1, 0x80, TPS65086_BUCK1CTRL,
+ BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(0),
+ tps65086_10mv_ranges, TPS65086_BUCK1CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK2", "buck2", BUCK2, 0x80, TPS65086_BUCK2CTRL,
+ BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(1),
+ tps65086_10mv_ranges, TPS65086_BUCK2CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK3", "buck3", BUCK3, 0x80, TPS65086_BUCK3VID,
+ BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(2),
+ tps65086_10mv_ranges, TPS65086_BUCK3DECAY,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK4", "buck4", BUCK4, 0x80, TPS65086_BUCK4VID,
+ BUCK_VID_MASK, TPS65086_BUCK4CTRL, BIT(0),
+ tps65086_10mv_ranges, TPS65086_BUCK4VID,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK5", "buck5", BUCK5, 0x80, TPS65086_BUCK5VID,
+ BUCK_VID_MASK, TPS65086_BUCK5CTRL, BIT(0),
+ tps65086_10mv_ranges, TPS65086_BUCK5CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK6", "buck6", BUCK6, 0x80, TPS65086_BUCK6VID,
+ BUCK_VID_MASK, TPS65086_BUCK6CTRL, BIT(0),
+ tps65086_10mv_ranges, TPS65086_BUCK6CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("LDOA1", "ldoa1", LDOA1, 0xF, TPS65086_LDOA1CTRL,
+ VDOA1_VID_MASK, TPS65086_LDOA1CTRL, BIT(0),
+ tps65086_ldoa1_ranges, 0, 0),
+ TPS65086_REGULATOR("LDOA2", "ldoa2", LDOA2, 0x10, TPS65086_LDOA2VID,
+ VDOA23_VID_MASK, TPS65086_LDOA2CTRL, BIT(0),
+ tps65086_ldoa23_ranges, 0, 0),
+ TPS65086_REGULATOR("LDOA3", "ldoa3", LDOA3, 0x10, TPS65086_LDOA3VID,
+ VDOA23_VID_MASK, TPS65086_LDOA3CTRL, BIT(0),
+ tps65086_ldoa23_ranges, 0, 0),
+ TPS65086_SWITCH("SWA1", "swa1", SWA1, TPS65086_SWVTT_EN, BIT(5)),
+ TPS65086_SWITCH("SWB1", "swb1", SWB1, TPS65086_SWVTT_EN, BIT(6)),
+ TPS65086_SWITCH("SWB2", "swb2", SWB2, TPS65086_SWVTT_EN, BIT(7)),
+ TPS65086_SWITCH("VTT", "vtt", VTT, TPS65086_SWVTT_EN, BIT(4)),
+};
+
+static int tps65086_of_parse_cb(struct device_node *node,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ int ret;
+
+ /* Check for 25mV step mode */
+ if (of_property_read_bool(node, "ti,regulator-step-size-25mv")) {
+ switch (desc->id) {
+ case BUCK1:
+ case BUCK2:
+ case BUCK6:
+ regulators[desc->id].desc.linear_ranges =
+ tps65086_buck126_25mv_ranges;
+ regulators[desc->id].desc.n_linear_ranges =
+ ARRAY_SIZE(tps65086_buck126_25mv_ranges);
+ break;
+ case BUCK3:
+ case BUCK4:
+ case BUCK5:
+ regulators[desc->id].desc.linear_ranges =
+ tps65086_buck345_25mv_ranges;
+ regulators[desc->id].desc.n_linear_ranges =
+ ARRAY_SIZE(tps65086_buck345_25mv_ranges);
+ break;
+ default:
+ dev_warn(config->dev, "25mV step mode only valid for BUCK regulators\n");
+ }
+ }
+
+ /* Check for decay mode */
+ if (desc->id <= BUCK6 && of_property_read_bool(node, "ti,regulator-decay")) {
+ ret = regmap_write_bits(config->regmap,
+ regulators[desc->id].decay_reg,
+ regulators[desc->id].decay_mask,
+ regulators[desc->id].decay_mask);
+ if (ret) {
+ dev_err(config->dev, "Error setting decay\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tps65086_regulator_probe(struct platform_device *pdev)
+{
+ struct tps65086 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i;
+
+ platform_set_drvdata(pdev, tps);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = tps->dev->of_node;
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i].desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id tps65086_regulator_id_table[] = {
+ { "tps65086-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65086_regulator_id_table);
+
+static struct platform_driver tps65086_regulator_driver = {
+ .driver = {
+ .name = "tps65086-regulator",
+ },
+ .probe = tps65086_regulator_probe,
+ .id_table = tps65086_regulator_id_table,
+};
+module_platform_driver(tps65086_regulator_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TPS65086 Regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c
new file mode 100644
index 000000000..edaef9e4d
--- /dev/null
+++ b/drivers/regulator/tps65090-regulator.c
@@ -0,0 +1,531 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/tps65090.h>
+
+#define MAX_CTRL_READ_TRIES 5
+#define MAX_FET_ENABLE_TRIES 1000
+
+#define CTRL_EN_BIT 0 /* Regulator enable bit, active high */
+#define CTRL_WT_BIT 2 /* Regulator wait time 0 bit */
+#define CTRL_PG_BIT 4 /* Regulator power good bit, 1=good */
+#define CTRL_TO_BIT 7 /* Regulator timeout bit, 1=wait */
+
+#define MAX_OVERCURRENT_WAIT 3 /* Overcurrent wait must be <= this */
+
+/**
+ * struct tps65090_regulator - Per-regulator data for a tps65090 regulator
+ *
+ * @dev: Pointer to our device.
+ * @desc: The struct regulator_desc for the regulator.
+ * @rdev: The struct regulator_dev for the regulator.
+ * @overcurrent_wait_valid: True if overcurrent_wait is valid.
+ * @overcurrent_wait: For FETs, the value to put in the WTFET bitfield.
+ */
+
+struct tps65090_regulator {
+ struct device *dev;
+ struct regulator_desc *desc;
+ struct regulator_dev *rdev;
+ bool overcurrent_wait_valid;
+ int overcurrent_wait;
+};
+
+static struct regulator_ops tps65090_ext_control_ops = {
+};
+
+/**
+ * tps65090_reg_set_overcurrent_wait - Setup overcurrent wait
+ *
+ * This will set the overcurrent wait time based on what's in the regulator
+ * info.
+ *
+ * @ri: Overall regulator data
+ * @rdev: Regulator device
+ *
+ * Return: 0 if no error, non-zero if there was an error writing the register.
+ */
+static int tps65090_reg_set_overcurrent_wait(struct tps65090_regulator *ri,
+ struct regulator_dev *rdev)
+{
+ int ret;
+
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ MAX_OVERCURRENT_WAIT << CTRL_WT_BIT,
+ ri->overcurrent_wait << CTRL_WT_BIT);
+ if (ret) {
+ dev_err(&rdev->dev, "Error updating overcurrent wait %#x\n",
+ rdev->desc->enable_reg);
+ }
+
+ return ret;
+}
+
+/**
+ * tps65090_try_enable_fet - Try to enable a FET
+ *
+ * @rdev: Regulator device
+ *
+ * Return: 0 if ok, -ENOTRECOVERABLE if the FET power good bit did not get
+ * set, or some other -ve value if another error occurred (e.g. i2c error)
+ */
+static int tps65090_try_enable_fet(struct regulator_dev *rdev)
+{
+ unsigned int control;
+ int ret, i;
+
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ rdev->desc->enable_mask);
+ if (ret < 0) {
+ dev_err(&rdev->dev, "Error in updating reg %#x\n",
+ rdev->desc->enable_reg);
+ return ret;
+ }
+
+ for (i = 0; i < MAX_CTRL_READ_TRIES; i++) {
+ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg,
+ &control);
+ if (ret < 0)
+ return ret;
+
+ if (!(control & BIT(CTRL_TO_BIT)))
+ break;
+
+ usleep_range(1000, 1500);
+ }
+ if (!(control & BIT(CTRL_PG_BIT)))
+ return -ENOTRECOVERABLE;
+
+ return 0;
+}
+
+/**
+ * tps65090_fet_enable - Enable a FET, trying a few times if it fails
+ *
+ * Some versions of the tps65090 have issues when turning on the FETs.
+ * This function goes through several steps to ensure the best chance of the
+ * FET going on. Specifically:
+ * - We'll make sure that we bump the "overcurrent wait" to the maximum, which
+ * increases the chances that we'll turn on properly.
+ * - We'll retry turning the FET on multiple times (turning off in between).
+ *
+ * @rdev: Regulator device
+ *
+ * Return: 0 if ok, non-zero if it fails.
+ */
+static int tps65090_fet_enable(struct regulator_dev *rdev)
+{
+ int ret, tries;
+
+ /*
+ * Try enabling multiple times until we succeed since sometimes the
+ * first try times out.
+ */
+ tries = 0;
+ while (true) {
+ ret = tps65090_try_enable_fet(rdev);
+ if (!ret)
+ break;
+ if (ret != -ENOTRECOVERABLE || tries == MAX_FET_ENABLE_TRIES)
+ goto err;
+
+ /* Try turning the FET off (and then on again) */
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, 0);
+ if (ret)
+ goto err;
+
+ tries++;
+ }
+
+ if (tries)
+ dev_warn(&rdev->dev, "reg %#x enable ok after %d tries\n",
+ rdev->desc->enable_reg, tries);
+
+ return 0;
+err:
+ dev_warn(&rdev->dev, "reg %#x enable failed\n", rdev->desc->enable_reg);
+ WARN_ON(1);
+
+ return ret;
+}
+
+static struct regulator_ops tps65090_reg_control_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops tps65090_fet_control_ops = {
+ .enable = tps65090_fet_enable,
+ .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, _en_bits, _nvolt, _volt, _ops) \
+{ \
+ .name = "TPS65090_RAILS"#_id, \
+ .supply_name = _sname, \
+ .id = TPS65090_REGULATOR_##_id, \
+ .n_voltages = _nvolt, \
+ .ops = &_ops, \
+ .fixed_uV = _volt, \
+ .enable_reg = _en_reg, \
+ .enable_val = _en_bits, \
+ .enable_mask = _en_bits, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+}
+
+#define tps65090_REG_FIXEDV(_id, _sname, en_reg, _en_bits, _volt, _ops) \
+ tps65090_REG_DESC(_id, _sname, en_reg, _en_bits, 1, _volt, _ops)
+
+#define tps65090_REG_SWITCH(_id, _sname, en_reg, _en_bits, _ops) \
+ tps65090_REG_DESC(_id, _sname, en_reg, _en_bits, 0, 0, _ops)
+
+static struct regulator_desc tps65090_regulator_desc[] = {
+ tps65090_REG_FIXEDV(DCDC1, "vsys1", 0x0C, BIT(CTRL_EN_BIT), 5000000,
+ tps65090_reg_control_ops),
+ tps65090_REG_FIXEDV(DCDC2, "vsys2", 0x0D, BIT(CTRL_EN_BIT), 3300000,
+ tps65090_reg_control_ops),
+ tps65090_REG_SWITCH(DCDC3, "vsys3", 0x0E, BIT(CTRL_EN_BIT),
+ tps65090_reg_control_ops),
+
+ tps65090_REG_SWITCH(FET1, "infet1", 0x0F,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+ tps65090_REG_SWITCH(FET2, "infet2", 0x10,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+ tps65090_REG_SWITCH(FET3, "infet3", 0x11,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+ tps65090_REG_SWITCH(FET4, "infet4", 0x12,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+ tps65090_REG_SWITCH(FET5, "infet5", 0x13,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+ tps65090_REG_SWITCH(FET6, "infet6", 0x14,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+ tps65090_REG_SWITCH(FET7, "infet7", 0x15,
+ BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+ tps65090_fet_control_ops),
+
+ tps65090_REG_FIXEDV(LDO1, "vsys-l1", 0, 0, 5000000,
+ tps65090_ldo_ops),
+ tps65090_REG_FIXEDV(LDO2, "vsys-l2", 0, 0, 3300000,
+ 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);
+}
+
+#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)
+ return ERR_PTR(-ENOMEM);
+
+ reg_pdata = devm_kcalloc(&pdev->dev,
+ TPS65090_REGULATOR_MAX, sizeof(*reg_pdata),
+ GFP_KERNEL);
+ if (!reg_pdata)
+ return ERR_PTR(-ENOMEM);
+
+ regulators = of_get_child_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));
+ of_node_put(regulators);
+ 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 = &reg_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) {
+ enum gpiod_flags gflags;
+
+ if (ri_data->constraints.always_on ||
+ ri_data->constraints.boot_on)
+ gflags = GPIOD_OUT_HIGH;
+ else
+ gflags = GPIOD_OUT_LOW;
+
+ rpdata->gpiod = devm_gpiod_get_from_of_node(&pdev->dev,
+ tps65090_matches[idx].of_node,
+ "dcdc-ext-control-gpios", 0,
+ gflags,
+ "tps65090");
+ if (IS_ERR(rpdata->gpiod))
+ return ERR_CAST(rpdata->gpiod);
+ if (!rpdata->gpiod)
+ dev_err(&pdev->dev,
+ "could not find DCDC external control GPIO\n");
+ }
+
+ if (of_property_read_u32(tps65090_matches[idx].of_node,
+ "ti,overcurrent-wait",
+ &rpdata->overcurrent_wait) == 0)
+ rpdata->overcurrent_wait_valid = true;
+
+ 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_kcalloc(&pdev->dev,
+ TPS65090_REGULATOR_MAX, sizeof(*pmic),
+ GFP_KERNEL);
+ if (!pmic)
+ 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];
+ if (tps_pdata) {
+ ri->overcurrent_wait_valid =
+ tps_pdata->overcurrent_wait_valid;
+ ri->overcurrent_wait = tps_pdata->overcurrent_wait;
+ }
+
+ /*
+ * 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) {
+ config.ena_gpiod = tps_pdata->gpiod;
+ 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");
+ return ret;
+ }
+ }
+ }
+
+ 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 = devm_regulator_register(&pdev->dev, ri->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ ri->desc->name);
+ return PTR_ERR(rdev);
+ }
+ ri->rdev = rdev;
+
+ if (ri->overcurrent_wait_valid) {
+ ret = tps65090_reg_set_overcurrent_wait(ri, rdev);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* 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)
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, pmic);
+ return 0;
+}
+
+static struct platform_driver tps65090_regulator_driver = {
+ .driver = {
+ .name = "tps65090-pmic",
+ },
+ .probe = tps65090_regulator_probe,
+};
+
+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 <vbyravarasu@nvidia.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65090-pmic");
diff --git a/drivers/regulator/tps65132-regulator.c b/drivers/regulator/tps65132-regulator.c
new file mode 100644
index 000000000..73978dd44
--- /dev/null
+++ b/drivers/regulator/tps65132-regulator.c
@@ -0,0 +1,284 @@
+/*
+ * TI TPS65132 Regulator driver
+ *
+ * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Venkat Reddy Talla <vreddytalla@nvidia.com>
+ * Laxman Dewangan <ldewangan@nvidia.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.
+ *
+ * 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 <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#define TPS65132_REG_VPOS 0x00
+#define TPS65132_REG_VNEG 0x01
+#define TPS65132_REG_APPS_DISP_DISN 0x03
+#define TPS65132_REG_CONTROL 0x0FF
+
+#define TPS65132_VOUT_MASK 0x1F
+#define TPS65132_VOUT_N_VOLTAGE 0x15
+#define TPS65132_VOUT_VMIN 4000000
+#define TPS65132_VOUT_VMAX 6000000
+#define TPS65132_VOUT_STEP 100000
+
+#define TPS65132_REG_APPS_DIS_VPOS BIT(0)
+#define TPS65132_REG_APPS_DIS_VNEG BIT(1)
+
+#define TPS65132_REGULATOR_ID_VPOS 0
+#define TPS65132_REGULATOR_ID_VNEG 1
+#define TPS65132_MAX_REGULATORS 2
+
+#define TPS65132_ACT_DIS_TIME_SLACK 1000
+
+struct tps65132_reg_pdata {
+ struct gpio_desc *en_gpiod;
+ struct gpio_desc *act_dis_gpiod;
+ unsigned int act_dis_time_us;
+ int ena_gpio_state;
+};
+
+struct tps65132_regulator {
+ struct device *dev;
+ struct regmap *rmap;
+ struct regulator_desc *rdesc[TPS65132_MAX_REGULATORS];
+ struct tps65132_reg_pdata reg_pdata[TPS65132_MAX_REGULATORS];
+ struct regulator_dev *rdev[TPS65132_MAX_REGULATORS];
+};
+
+static int tps65132_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+ int ret;
+
+ if (!IS_ERR(rpdata->en_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->en_gpiod, 1);
+ rpdata->ena_gpio_state = 1;
+ }
+
+ /* Hardware automatically enable discharge bit in enable */
+ if (rdev->constraints->active_discharge ==
+ REGULATOR_ACTIVE_DISCHARGE_DISABLE) {
+ ret = regulator_set_active_discharge_regmap(rdev, false);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to disable active discharge: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tps65132_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+
+ if (!IS_ERR(rpdata->en_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->en_gpiod, 0);
+ rpdata->ena_gpio_state = 0;
+ }
+
+ if (!IS_ERR(rpdata->act_dis_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 1);
+ usleep_range(rpdata->act_dis_time_us, rpdata->act_dis_time_us +
+ TPS65132_ACT_DIS_TIME_SLACK);
+ gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 0);
+ }
+
+ return 0;
+}
+
+static int tps65132_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+
+ if (!IS_ERR(rpdata->en_gpiod))
+ return rpdata->ena_gpio_state;
+
+ return 1;
+}
+
+static struct regulator_ops tps65132_regulator_ops = {
+ .enable = tps65132_regulator_enable,
+ .disable = tps65132_regulator_disable,
+ .is_enabled = tps65132_regulator_is_enabled,
+ .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_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static int tps65132_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct tps65132_regulator *tps = config->driver_data;
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[desc->id];
+ int ret;
+
+ rpdata->en_gpiod = devm_fwnode_get_index_gpiod_from_child(tps->dev,
+ "enable", 0, &np->fwnode, 0, "enable");
+ if (IS_ERR(rpdata->en_gpiod)) {
+ ret = PTR_ERR(rpdata->en_gpiod);
+
+ /* Ignore the error other than probe defer */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ return 0;
+ }
+
+ rpdata->act_dis_gpiod = devm_fwnode_get_index_gpiod_from_child(
+ tps->dev, "active-discharge", 0,
+ &np->fwnode, 0, "active-discharge");
+ if (IS_ERR(rpdata->act_dis_gpiod)) {
+ ret = PTR_ERR(rpdata->act_dis_gpiod);
+
+ /* Ignore the error other than probe defer */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return 0;
+ }
+
+ ret = of_property_read_u32(np, "ti,active-discharge-time-us",
+ &rpdata->act_dis_time_us);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to read active discharge time:%d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define TPS65132_REGULATOR_DESC(_id, _name) \
+ [TPS65132_REGULATOR_ID_##_id] = { \
+ .name = "tps65132-"#_name, \
+ .supply_name = "vin", \
+ .id = TPS65132_REGULATOR_ID_##_id, \
+ .of_match = of_match_ptr(#_name), \
+ .of_parse_cb = tps65132_of_parse_cb, \
+ .ops = &tps65132_regulator_ops, \
+ .n_voltages = TPS65132_VOUT_N_VOLTAGE, \
+ .min_uV = TPS65132_VOUT_VMIN, \
+ .uV_step = TPS65132_VOUT_STEP, \
+ .enable_time = 500, \
+ .vsel_mask = TPS65132_VOUT_MASK, \
+ .vsel_reg = TPS65132_REG_##_id, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = TPS65132_REG_APPS_DIS_##_id, \
+ .active_discharge_mask = TPS65132_REG_APPS_DIS_##_id, \
+ .active_discharge_reg = TPS65132_REG_APPS_DISP_DISN, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+static struct regulator_desc tps_regs_desc[TPS65132_MAX_REGULATORS] = {
+ TPS65132_REGULATOR_DESC(VPOS, outp),
+ TPS65132_REGULATOR_DESC(VNEG, outn),
+};
+
+static const struct regmap_range tps65132_no_reg_ranges[] = {
+ regmap_reg_range(TPS65132_REG_APPS_DISP_DISN + 1,
+ TPS65132_REG_CONTROL - 1),
+};
+
+static const struct regmap_access_table tps65132_no_reg_table = {
+ .no_ranges = tps65132_no_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(tps65132_no_reg_ranges),
+};
+
+static const struct regmap_config tps65132_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPS65132_REG_CONTROL,
+ .cache_type = REGCACHE_NONE,
+ .rd_table = &tps65132_no_reg_table,
+ .wr_table = &tps65132_no_reg_table,
+};
+
+static int tps65132_probe(struct i2c_client *client,
+ const struct i2c_device_id *client_id)
+{
+ struct device *dev = &client->dev;
+ struct tps65132_regulator *tps;
+ struct regulator_config config = { };
+ int id;
+ int ret;
+
+ tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->rmap = devm_regmap_init_i2c(client, &tps65132_regmap_config);
+ if (IS_ERR(tps->rmap)) {
+ ret = PTR_ERR(tps->rmap);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = dev;
+
+ for (id = 0; id < TPS65132_MAX_REGULATORS; ++id) {
+ tps->rdesc[id] = &tps_regs_desc[id];
+
+ config.regmap = tps->rmap;
+ config.dev = dev;
+ config.driver_data = tps;
+
+ tps->rdev[id] = devm_regulator_register(dev,
+ tps->rdesc[id], &config);
+ if (IS_ERR(tps->rdev[id])) {
+ ret = PTR_ERR(tps->rdev[id]);
+ dev_err(dev, "regulator %s register failed: %d\n",
+ tps->rdesc[id]->name, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct i2c_device_id tps65132_id[] = {
+ {.name = "tps65132",},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, tps65132_id);
+
+static struct i2c_driver tps65132_i2c_driver = {
+ .driver = {
+ .name = "tps65132",
+ },
+ .probe = tps65132_probe,
+ .id_table = tps65132_id,
+};
+
+module_i2c_driver(tps65132_i2c_driver);
+
+MODULE_DESCRIPTION("tps65132 regulator driver");
+MODULE_AUTHOR("Venkat Reddy Talla <vreddytalla@nvidia.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c
new file mode 100644
index 000000000..d84fab616
--- /dev/null
+++ b/drivers/regulator/tps65217-regulator.c
@@ -0,0 +1,286 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps65217.h>
+
+#define TPS65217_REGULATOR(_name, _id, _of_match, _ops, _n, _vr, _vm, _em, \
+ _t, _lr, _nlr, _sr, _sm) \
+ { \
+ .name = _name, \
+ .id = _id, \
+ .of_match = of_match_ptr(_of_match), \
+ .regulators_node= of_match_ptr("regulators"), \
+ .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, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = _nlr, \
+ .bypass_reg = _sr, \
+ .bypass_mask = _sm, \
+ } \
+
+static const unsigned int LDO1_VSEL_table[] = {
+ 1000000, 1100000, 1200000, 1250000,
+ 1300000, 1350000, 1400000, 1500000,
+ 1600000, 1800000, 2500000, 2750000,
+ 2800000, 3000000, 3100000, 3300000,
+};
+
+static const struct regulator_linear_range tps65217_uv1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 24, 25000),
+ REGULATOR_LINEAR_RANGE(1550000, 25, 30, 50000),
+ REGULATOR_LINEAR_RANGE(1850000, 31, 52, 50000),
+ REGULATOR_LINEAR_RANGE(3000000, 53, 55, 100000),
+ REGULATOR_LINEAR_RANGE(3300000, 56, 62, 0),
+};
+
+static const struct regulator_linear_range tps65217_uv2_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0, 8, 50000),
+ REGULATOR_LINEAR_RANGE(2000000, 9, 13, 100000),
+ REGULATOR_LINEAR_RANGE(2450000, 14, 31, 50000),
+};
+
+static int tps65217_pmic_enable(struct regulator_dev *dev)
+{
+ struct tps65217 *tps = rdev_get_drvdata(dev);
+ 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);
+ 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_set_suspend_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;
+
+ return tps65217_clear_bits(tps, dev->desc->bypass_reg,
+ dev->desc->bypass_mask,
+ TPS65217_PROTECT_L1);
+}
+
+static int tps65217_pmic_set_suspend_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;
+
+ if (!tps->strobes[rid])
+ return -EINVAL;
+
+ return tps65217_set_bits(tps, dev->desc->bypass_reg,
+ dev->desc->bypass_mask,
+ tps->strobes[rid], TPS65217_PROTECT_L1);
+}
+
+/* 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 = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_suspend_enable = tps65217_pmic_set_suspend_enable,
+ .set_suspend_disable = tps65217_pmic_set_suspend_disable,
+};
+
+/* 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,
+ .map_voltage = regulator_map_voltage_ascend,
+ .set_suspend_enable = tps65217_pmic_set_suspend_enable,
+ .set_suspend_disable = tps65217_pmic_set_suspend_disable,
+};
+
+static const struct regulator_desc regulators[] = {
+ TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, "dcdc1",
+ tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC1,
+ TPS65217_DEFDCDCX_DCDC_MASK, TPS65217_ENABLE_DC1_EN,
+ NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges), TPS65217_REG_SEQ1,
+ TPS65217_SEQ1_DC1_SEQ_MASK),
+ TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, "dcdc2",
+ tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC2,
+ TPS65217_DEFDCDCX_DCDC_MASK, TPS65217_ENABLE_DC2_EN,
+ NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges), TPS65217_REG_SEQ1,
+ TPS65217_SEQ1_DC2_SEQ_MASK),
+ TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, "dcdc3",
+ tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC3,
+ TPS65217_DEFDCDCX_DCDC_MASK, TPS65217_ENABLE_DC3_EN,
+ NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges), TPS65217_REG_SEQ2,
+ TPS65217_SEQ2_DC3_SEQ_MASK),
+ TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, "ldo1",
+ tps65217_pmic_ldo1_ops, 16, TPS65217_REG_DEFLDO1,
+ TPS65217_DEFLDO1_LDO1_MASK, TPS65217_ENABLE_LDO1_EN,
+ LDO1_VSEL_table, NULL, 0, TPS65217_REG_SEQ2,
+ TPS65217_SEQ2_LDO1_SEQ_MASK),
+ TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, "ldo2", tps65217_pmic_ops,
+ 64, TPS65217_REG_DEFLDO2,
+ TPS65217_DEFLDO2_LDO2_MASK, TPS65217_ENABLE_LDO2_EN,
+ NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges), TPS65217_REG_SEQ3,
+ TPS65217_SEQ3_LDO2_SEQ_MASK),
+ TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, "ldo3", tps65217_pmic_ops,
+ 32, TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK,
+ TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN,
+ NULL, tps65217_uv2_ranges,
+ ARRAY_SIZE(tps65217_uv2_ranges), TPS65217_REG_SEQ3,
+ TPS65217_SEQ3_LDO3_SEQ_MASK),
+ TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, "ldo4", tps65217_pmic_ops,
+ 32, TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK,
+ TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN,
+ NULL, tps65217_uv2_ranges,
+ ARRAY_SIZE(tps65217_uv2_ranges), TPS65217_REG_SEQ4,
+ TPS65217_SEQ4_LDO4_SEQ_MASK),
+};
+
+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_dev *rdev;
+ struct regulator_config config = { };
+ int i, ret;
+ unsigned int val;
+
+ /* Allocate memory for strobes */
+ tps->strobes = devm_kcalloc(&pdev->dev,
+ TPS65217_NUM_REGULATOR, sizeof(u8),
+ GFP_KERNEL);
+ if (!tps->strobes)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, tps);
+
+ for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
+ /* Register the regulators */
+ config.dev = tps->dev;
+ if (pdata)
+ config.init_data = pdata->tps65217_init_data[i];
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+
+ /* Store default strobe info */
+ ret = tps65217_reg_read(tps, regulators[i].bypass_reg, &val);
+ tps->strobes[i] = val & regulators[i].bypass_mask;
+ }
+
+ return 0;
+}
+
+static struct platform_driver tps65217_regulator_driver = {
+ .driver = {
+ .name = "tps65217-pmic",
+ },
+ .probe = tps65217_regulator_probe,
+};
+
+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 <anilkumar@ti.com>");
+MODULE_DESCRIPTION("TPS65217 voltage regulator driver");
+MODULE_ALIAS("platform:tps65217-pmic");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c
new file mode 100644
index 000000000..6209beee1
--- /dev/null
+++ b/drivers/regulator/tps65218-regulator.c
@@ -0,0 +1,371 @@
+/*
+ * tps65218-regulator.c
+ *
+ * Regulator driver for TPS65218 PMIC
+ *
+ * Copyright (C) 2014 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps65218.h>
+
+#define TPS65218_REGULATOR(_name, _of, _id, _type, _ops, _n, _vr, _vm, _er, \
+ _em, _cr, _cm, _lr, _nlr, _delay, _fuv, _sr, _sm) \
+ { \
+ .name = _name, \
+ .of_match = _of, \
+ .id = _id, \
+ .ops = &_ops, \
+ .n_voltages = _n, \
+ .type = _type, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .csel_reg = _cr, \
+ .csel_mask = _cm, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ .volt_table = NULL, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = _nlr, \
+ .ramp_delay = _delay, \
+ .fixed_uV = _fuv, \
+ .bypass_reg = _sr, \
+ .bypass_mask = _sm, \
+ } \
+
+static const struct regulator_linear_range dcdc1_dcdc2_ranges[] = {
+ REGULATOR_LINEAR_RANGE(850000, 0x0, 0x32, 10000),
+ REGULATOR_LINEAR_RANGE(1375000, 0x33, 0x3f, 25000),
+};
+
+static const struct regulator_linear_range ldo1_dcdc3_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0x0, 0x1a, 25000),
+ REGULATOR_LINEAR_RANGE(1600000, 0x1b, 0x3f, 50000),
+};
+
+static const struct regulator_linear_range dcdc4_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1175000, 0x0, 0xf, 25000),
+ REGULATOR_LINEAR_RANGE(1600000, 0x10, 0x34, 50000),
+};
+
+static int tps65218_pmic_set_voltage_sel(struct regulator_dev *dev,
+ unsigned selector)
+{
+ int ret;
+ struct tps65218 *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 = tps65218_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask,
+ selector, TPS65218_PROTECT_L1);
+
+ /* Set GO bit for DCDC1/2 to initiate voltage transistion */
+ switch (rid) {
+ case TPS65218_DCDC_1:
+ case TPS65218_DCDC_2:
+ ret = tps65218_set_bits(tps, TPS65218_REG_CONTRL_SLEW_RATE,
+ TPS65218_SLEW_RATE_GO,
+ TPS65218_SLEW_RATE_GO,
+ TPS65218_PROTECT_L1);
+ break;
+ }
+
+ return ret;
+}
+
+static int tps65218_pmic_enable(struct regulator_dev *dev)
+{
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+ int rid = rdev_get_id(dev);
+
+ if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1)
+ return -EINVAL;
+
+ /* Enable the regulator and password protection is level 1 */
+ return tps65218_set_bits(tps, dev->desc->enable_reg,
+ dev->desc->enable_mask, dev->desc->enable_mask,
+ TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_disable(struct regulator_dev *dev)
+{
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+ int rid = rdev_get_id(dev);
+
+ if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1)
+ return -EINVAL;
+
+ /* Disable the regulator and password protection is level 1 */
+ return tps65218_clear_bits(tps, dev->desc->enable_reg,
+ dev->desc->enable_mask, TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_set_suspend_enable(struct regulator_dev *dev)
+{
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+ unsigned int rid = rdev_get_id(dev);
+
+ if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1)
+ return -EINVAL;
+
+ return tps65218_clear_bits(tps, dev->desc->bypass_reg,
+ dev->desc->bypass_mask,
+ TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_set_suspend_disable(struct regulator_dev *dev)
+{
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+ unsigned int rid = rdev_get_id(dev);
+
+ if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1)
+ return -EINVAL;
+
+ /*
+ * Certain revisions of TPS65218 will need to have DCDC3 regulator
+ * enabled always, otherwise an immediate system reboot will occur
+ * during poweroff.
+ */
+ if (rid == TPS65218_DCDC_3 && tps->rev == TPS65218_REV_2_1)
+ return 0;
+
+ if (!tps->strobes[rid]) {
+ if (rid == TPS65218_DCDC_3)
+ tps->strobes[rid] = 3;
+ else
+ return -EINVAL;
+ }
+
+ return tps65218_set_bits(tps, dev->desc->bypass_reg,
+ dev->desc->bypass_mask,
+ tps->strobes[rid], TPS65218_PROTECT_L1);
+}
+
+/* Operations permitted on DCDC1, DCDC2 */
+static struct regulator_ops tps65218_dcdc12_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = tps65218_pmic_enable,
+ .disable = tps65218_pmic_disable,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = tps65218_pmic_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_suspend_enable = tps65218_pmic_set_suspend_enable,
+ .set_suspend_disable = tps65218_pmic_set_suspend_disable,
+};
+
+/* Operations permitted on DCDC3, DCDC4 and LDO1 */
+static struct regulator_ops tps65218_ldo1_dcdc34_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = tps65218_pmic_enable,
+ .disable = tps65218_pmic_disable,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = tps65218_pmic_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_suspend_enable = tps65218_pmic_set_suspend_enable,
+ .set_suspend_disable = tps65218_pmic_set_suspend_disable,
+};
+
+static const int ls3_currents[] = { 100, 200, 500, 1000 };
+
+static int tps65218_pmic_set_input_current_lim(struct regulator_dev *dev,
+ int lim_uA)
+{
+ unsigned int index = 0;
+ unsigned int num_currents = ARRAY_SIZE(ls3_currents);
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+
+ while (index < num_currents && ls3_currents[index] != lim_uA)
+ index++;
+
+ if (index == num_currents)
+ return -EINVAL;
+
+ return tps65218_set_bits(tps, dev->desc->csel_reg, dev->desc->csel_mask,
+ index << 2, TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_set_current_limit(struct regulator_dev *dev,
+ int min_uA, int max_uA)
+{
+ int index = 0;
+ unsigned int num_currents = ARRAY_SIZE(ls3_currents);
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+
+ while (index < num_currents && ls3_currents[index] < max_uA)
+ index++;
+
+ index--;
+
+ if (index < 0 || ls3_currents[index] < min_uA)
+ return -EINVAL;
+
+ return tps65218_set_bits(tps, dev->desc->csel_reg, dev->desc->csel_mask,
+ index << 2, TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_get_current_limit(struct regulator_dev *dev)
+{
+ int retval;
+ unsigned int index;
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+
+ retval = regmap_read(tps->regmap, dev->desc->csel_reg, &index);
+ if (retval < 0)
+ return retval;
+
+ index = (index & dev->desc->csel_mask) >> 2;
+
+ return ls3_currents[index];
+}
+
+static struct regulator_ops tps65218_ls3_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = tps65218_pmic_enable,
+ .disable = tps65218_pmic_disable,
+ .set_input_current_limit = tps65218_pmic_set_input_current_lim,
+ .set_current_limit = tps65218_pmic_set_current_limit,
+ .get_current_limit = tps65218_pmic_get_current_limit,
+};
+
+/* Operations permitted on DCDC5, DCDC6 */
+static struct regulator_ops tps65218_dcdc56_pmic_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = tps65218_pmic_enable,
+ .disable = tps65218_pmic_disable,
+ .set_suspend_enable = tps65218_pmic_set_suspend_enable,
+ .set_suspend_disable = tps65218_pmic_set_suspend_disable,
+};
+
+static const struct regulator_desc regulators[] = {
+ TPS65218_REGULATOR("DCDC1", "regulator-dcdc1", TPS65218_DCDC_1,
+ REGULATOR_VOLTAGE, tps65218_dcdc12_ops, 64,
+ TPS65218_REG_CONTROL_DCDC1,
+ TPS65218_CONTROL_DCDC1_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC1_EN, 0, 0, dcdc1_dcdc2_ranges,
+ 2, 4000, 0, TPS65218_REG_SEQ3,
+ TPS65218_SEQ3_DC1_SEQ_MASK),
+ TPS65218_REGULATOR("DCDC2", "regulator-dcdc2", TPS65218_DCDC_2,
+ REGULATOR_VOLTAGE, tps65218_dcdc12_ops, 64,
+ TPS65218_REG_CONTROL_DCDC2,
+ TPS65218_CONTROL_DCDC2_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC2_EN, 0, 0, dcdc1_dcdc2_ranges,
+ 2, 4000, 0, TPS65218_REG_SEQ3,
+ TPS65218_SEQ3_DC2_SEQ_MASK),
+ TPS65218_REGULATOR("DCDC3", "regulator-dcdc3", TPS65218_DCDC_3,
+ REGULATOR_VOLTAGE, tps65218_ldo1_dcdc34_ops, 64,
+ TPS65218_REG_CONTROL_DCDC3,
+ TPS65218_CONTROL_DCDC3_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC3_EN, 0, 0, ldo1_dcdc3_ranges, 2,
+ 0, 0, TPS65218_REG_SEQ4, TPS65218_SEQ4_DC3_SEQ_MASK),
+ TPS65218_REGULATOR("DCDC4", "regulator-dcdc4", TPS65218_DCDC_4,
+ REGULATOR_VOLTAGE, tps65218_ldo1_dcdc34_ops, 53,
+ TPS65218_REG_CONTROL_DCDC4,
+ TPS65218_CONTROL_DCDC4_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC4_EN, 0, 0, dcdc4_ranges, 2,
+ 0, 0, TPS65218_REG_SEQ4, TPS65218_SEQ4_DC4_SEQ_MASK),
+ TPS65218_REGULATOR("DCDC5", "regulator-dcdc5", TPS65218_DCDC_5,
+ REGULATOR_VOLTAGE, tps65218_dcdc56_pmic_ops, 1, -1,
+ -1, TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC5_EN, 0,
+ 0, NULL, 0, 0, 1000000, TPS65218_REG_SEQ5,
+ TPS65218_SEQ5_DC5_SEQ_MASK),
+ TPS65218_REGULATOR("DCDC6", "regulator-dcdc6", TPS65218_DCDC_6,
+ REGULATOR_VOLTAGE, tps65218_dcdc56_pmic_ops, 1, -1,
+ -1, TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC6_EN, 0,
+ 0, NULL, 0, 0, 1800000, TPS65218_REG_SEQ5,
+ TPS65218_SEQ5_DC6_SEQ_MASK),
+ TPS65218_REGULATOR("LDO1", "regulator-ldo1", TPS65218_LDO_1,
+ REGULATOR_VOLTAGE, tps65218_ldo1_dcdc34_ops, 64,
+ TPS65218_REG_CONTROL_LDO1,
+ TPS65218_CONTROL_LDO1_MASK, TPS65218_REG_ENABLE2,
+ TPS65218_ENABLE2_LDO1_EN, 0, 0, ldo1_dcdc3_ranges,
+ 2, 0, 0, TPS65218_REG_SEQ6,
+ TPS65218_SEQ6_LDO1_SEQ_MASK),
+ TPS65218_REGULATOR("LS3", "regulator-ls3", TPS65218_LS_3,
+ REGULATOR_CURRENT, tps65218_ls3_ops, 0, 0, 0,
+ TPS65218_REG_ENABLE2, TPS65218_ENABLE2_LS3_EN,
+ TPS65218_REG_CONFIG2, TPS65218_CONFIG2_LS3ILIM_MASK,
+ NULL, 0, 0, 0, 0, 0),
+};
+
+static int tps65218_regulator_probe(struct platform_device *pdev)
+{
+ struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ int i, ret;
+ unsigned int val;
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = tps->dev->of_node;
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+
+ /* Allocate memory for strobes */
+ tps->strobes = devm_kcalloc(&pdev->dev,
+ TPS65218_NUM_REGULATOR, sizeof(u8),
+ GFP_KERNEL);
+ if (!tps->strobes)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+
+ ret = regmap_read(tps->regmap, regulators[i].bypass_reg, &val);
+ if (ret)
+ return ret;
+
+ tps->strobes[i] = val & regulators[i].bypass_mask;
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id tps65218_regulator_id_table[] = {
+ { "tps65218-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65218_regulator_id_table);
+
+static struct platform_driver tps65218_regulator_driver = {
+ .driver = {
+ .name = "tps65218-pmic",
+ },
+ .probe = tps65218_regulator_probe,
+ .id_table = tps65218_regulator_id_table,
+};
+
+module_platform_driver(tps65218_regulator_driver);
+
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
+MODULE_DESCRIPTION("TPS65218 voltage regulator driver");
+MODULE_ALIAS("platform:tps65218-pmic");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c
new file mode 100644
index 000000000..67cac2682
--- /dev/null
+++ b/drivers/regulator/tps6524x-regulator.c
@@ -0,0 +1,640 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#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 = &in;
+ 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_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 i;
+
+ init_data = dev_get_platdata(dev);
+ 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)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, hw);
+
+ memset(hw, 0, sizeof(struct tps6524x));
+ hw->dev = dev;
+ hw->spi = 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 = &regulator_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] = devm_regulator_register(dev, &hw->desc[i],
+ &config);
+ if (IS_ERR(hw->rdev[i]))
+ return PTR_ERR(hw->rdev[i]);
+ }
+
+ return 0;
+}
+
+static struct spi_driver pmic_driver = {
+ .probe = pmic_probe,
+ .driver = {
+ .name = "tps6524x",
+ },
+};
+
+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 000000000..ba3dae7b2
--- /dev/null
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -0,0 +1,545 @@
+/*
+ * Regulator driver for TI TPS6586x
+ *
+ * Copyright (C) 2010 Compulab Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/tps6586x.h>
+
+/* 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 struct regulator_ops tps6586x_rw_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_rw_linear_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .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_ro_regulator_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_ascend,
+ .get_voltage_sel = regulator_get_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_ldo_voltages[] = {
+ 1250000, 1500000, 1800000, 2500000, 2700000, 2850000, 3100000, 3300000,
+};
+
+static const unsigned int tps658640_rtc_voltages[] = {
+ 2500000, 2850000, 3100000, 3300000,
+};
+
+#define TPS6586X_REGULATOR(_id, _ops, _pin_name, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit) \
+ .desc = { \
+ .supply_name = _pin_name, \
+ .name = "REG-" #_id, \
+ .ops = &tps6586x_## _ops ## _regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = TPS6586X_ID_##_id, \
+ .n_voltages = ARRAY_SIZE(vdata##_voltages), \
+ .volt_table = 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_REGULATOR_LINEAR(_id, _ops, _pin_name, n_volt, min_uv, \
+ uv_step, vreg, shift, nbits, ereg0, \
+ ebit0, ereg1, ebit1, goreg, gobit) \
+ .desc = { \
+ .supply_name = _pin_name, \
+ .name = "REG-" #_id, \
+ .ops = &tps6586x_## _ops ## _regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = TPS6586X_ID_##_id, \
+ .n_voltages = n_volt, \
+ .min_uV = min_uv, \
+ .uV_step = uv_step, \
+ .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, rw, _pname, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, 0, 0) \
+}
+
+#define TPS6586X_LDO_LINEAR(_id, _pname, n_volt, min_uv, uv_step, vreg, \
+ shift, nbits, ereg0, ebit0, ereg1, ebit1) \
+{ \
+ TPS6586X_REGULATOR_LINEAR(_id, rw_linear, _pname, n_volt, \
+ min_uv, uv_step, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, 0, 0) \
+}
+
+#define TPS6586X_FIXED_LDO(_id, _pname, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1) \
+{ \
+ TPS6586X_REGULATOR(_id, ro, _pname, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, 0, 0) \
+}
+
+#define TPS6586X_DVM(_id, _pname, n_volt, min_uv, uv_step, vreg, shift, \
+ nbits, ereg0, ebit0, ereg1, ebit1, goreg, gobit) \
+{ \
+ TPS6586X_REGULATOR_LINEAR(_id, rw_linear, _pname, n_volt, \
+ min_uv, uv_step, 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", tps6586x_ldo0, SUPPLYV1, 5, 3, ENC, 0,
+ END, 0),
+ TPS6586X_LDO(LDO_3, "vinldo23", tps6586x_ldo, SUPPLYV4, 0, 3, ENC, 2,
+ END, 2),
+ TPS6586X_LDO(LDO_5, "REG-SYS", tps6586x_ldo, SUPPLYV6, 0, 3, ENE, 6,
+ ENE, 6),
+ TPS6586X_LDO(LDO_6, "vinldo678", tps6586x_ldo, SUPPLYV3, 0, 3, ENC, 4,
+ END, 4),
+ TPS6586X_LDO(LDO_7, "vinldo678", tps6586x_ldo, SUPPLYV3, 3, 3, ENC, 5,
+ END, 5),
+ TPS6586X_LDO(LDO_8, "vinldo678", tps6586x_ldo, SUPPLYV2, 5, 3, ENC, 6,
+ END, 6),
+ TPS6586X_LDO(LDO_9, "vinldo9", tps6586x_ldo, SUPPLYV6, 3, 3, ENE, 7,
+ ENE, 7),
+ TPS6586X_LDO(LDO_RTC, "REG-SYS", tps6586x_ldo, SUPPLYV4, 3, 3, V4, 7,
+ V4, 7),
+ TPS6586X_LDO_LINEAR(LDO_1, "vinldo01", 32, 725000, 25000, SUPPLYV1,
+ 0, 5, ENC, 1, END, 1),
+ TPS6586X_LDO_LINEAR(SM_2, "vin-sm2", 32, 3000000, 50000, SUPPLYV2,
+ 0, 5, ENC, 7, END, 7),
+ TPS6586X_DVM(LDO_2, "vinldo23", 32, 725000, 25000, LDO2BV1, 0, 5,
+ ENA, 3, ENB, 3, TPS6586X_VCC2, BIT(6)),
+ TPS6586X_DVM(LDO_4, "vinldo4", 32, 1700000, 25000, LDO4V1, 0, 5,
+ ENC, 3, END, 3, TPS6586X_VCC1, BIT(6)),
+ TPS6586X_DVM(SM_0, "vin-sm0", 32, 725000, 25000, SM0V1, 0, 5,
+ ENA, 1, ENB, 1, TPS6586X_VCC1, BIT(2)),
+ TPS6586X_DVM(SM_1, "vin-sm1", 32, 725000, 25000, SM1V1, 0, 5,
+ ENA, 0, ENB, 0, TPS6586X_VCC1, BIT(0)),
+};
+
+static struct tps6586x_regulator tps658623_regulator[] = {
+ TPS6586X_LDO_LINEAR(SM_2, "vin-sm2", 32, 1700000, 25000, SUPPLYV2,
+ 0, 5, ENC, 7, END, 7),
+};
+
+static struct tps6586x_regulator tps658640_regulator[] = {
+ TPS6586X_LDO(LDO_3, "vinldo23", tps6586x_ldo0, SUPPLYV4, 0, 3,
+ ENC, 2, END, 2),
+ TPS6586X_LDO(LDO_5, "REG-SYS", tps6586x_ldo0, SUPPLYV6, 0, 3,
+ ENE, 6, ENE, 6),
+ TPS6586X_LDO(LDO_6, "vinldo678", tps6586x_ldo0, SUPPLYV3, 0, 3,
+ ENC, 4, END, 4),
+ TPS6586X_LDO(LDO_7, "vinldo678", tps6586x_ldo0, SUPPLYV3, 3, 3,
+ ENC, 5, END, 5),
+ TPS6586X_LDO(LDO_8, "vinldo678", tps6586x_ldo0, SUPPLYV2, 5, 3,
+ ENC, 6, END, 6),
+ TPS6586X_LDO(LDO_9, "vinldo9", tps6586x_ldo0, SUPPLYV6, 3, 3,
+ ENE, 7, ENE, 7),
+ TPS6586X_LDO_LINEAR(SM_2, "vin-sm2", 32, 2150000, 50000, SUPPLYV2,
+ 0, 5, ENC, 7, END, 7),
+
+ TPS6586X_FIXED_LDO(LDO_RTC, "REG-SYS", tps658640_rtc, SUPPLYV4, 3, 2,
+ V4, 7, V4, 7),
+};
+
+static struct tps6586x_regulator tps658643_regulator[] = {
+ TPS6586X_LDO_LINEAR(SM_2, "vin-sm2", 32, 1025000, 25000, SUPPLYV2,
+ 0, 5, ENC, 7, END, 7),
+};
+
+/*
+ * 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 struct tps6586x_regulator *find_regulator_info(int id, int version)
+{
+ struct tps6586x_regulator *ri;
+ struct tps6586x_regulator *table = NULL;
+ int num;
+ int i;
+
+ switch (version) {
+ case TPS658623:
+ case TPS658624:
+ table = tps658623_regulator;
+ num = ARRAY_SIZE(tps658623_regulator);
+ break;
+ case TPS658640:
+ case TPS658640v2:
+ table = tps658640_regulator;
+ num = ARRAY_SIZE(tps658640_regulator);
+ break;
+ case TPS658643:
+ table = tps658643_regulator;
+ num = ARRAY_SIZE(tps658643_regulator);
+ break;
+ }
+
+ /* Search version specific table first */
+ if (table) {
+ for (i = 0; i < num; i++) {
+ ri = &table[i];
+ if (ri->desc.id == id)
+ return ri;
+ }
+ }
+
+ 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_get_child_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)
+ return NULL;
+
+ for (i = 0; i < num; i++) {
+ uintptr_t id;
+ if (!tps6586x_matches[i].init_data)
+ continue;
+
+ pdata->reg_init_data[i] = tps6586x_matches[i].init_data;
+ id = (uintptr_t)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 version;
+ 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;
+ }
+
+ version = tps6586x_get_version(pdev->dev.parent);
+
+ for (id = 0; id < TPS6586X_ID_MAX_REGULATOR; ++id) {
+ reg_data = pdata->reg_init_data[id];
+
+ ri = find_regulator_info(id, version);
+
+ if (!ri) {
+ dev_err(&pdev->dev, "invalid regulator ID specified\n");
+ return -EINVAL;
+ }
+
+ err = tps6586x_regulator_preinit(pdev->dev.parent, ri);
+ if (err) {
+ dev_err(&pdev->dev,
+ "regulator %d preinit failed, e %d\n", id, err);
+ return err;
+ }
+
+ 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 = devm_regulator_register(&pdev->dev, &ri->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ ri->desc.name);
+ return PTR_ERR(rdev);
+ }
+
+ 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);
+ return err;
+ }
+ }
+ }
+
+ platform_set_drvdata(pdev, rdev);
+ return 0;
+}
+
+static struct platform_driver tps6586x_regulator_driver = {
+ .driver = {
+ .name = "tps6586x-regulator",
+ },
+ .probe = tps6586x_regulator_probe,
+};
+
+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 <mike@compulab.co.il>");
+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 000000000..5ebb6ee73
--- /dev/null
+++ b/drivers/regulator/tps65910-regulator.c
@@ -0,0 +1,1284 @@
+/*
+ * tps65910.c -- TI tps65910
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65910.h>
+#include <linux/regulator/of_regulator.h>
+
+#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,
+};
+
+/* supported BBCH voltages in microvolts */
+static const unsigned int VBB_VSEL_table[] = {
+ 3000000, 2520000, 3150000, 5000000,
+};
+
+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,
+ },
+ {
+ .name = "vbb",
+ .vin_name = "vcc7",
+ .n_voltages = ARRAY_SIZE(VBB_VSEL_table),
+ .voltage_table = VBB_VSEL_table,
+ },
+};
+
+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;
+ case TPS65910_REG_VBB:
+ return TPS65910_BBCH;
+ 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;
+ case TPS65910_REG_VBB:
+ value &= BBCH_BBSEL_MASK;
+ value >>= BBCH_BBSEL_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);
+ case TPS65910_REG_VBB:
+ return tps65910_reg_update_bits(pmic->mfd, reg, BBCH_BBSEL_MASK,
+ selector << BBCH_BBSEL_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);
+ case TPS65910_REG_VBB:
+ return tps65910_reg_update_bits(pmic->mfd, reg, BBCH_BBSEL_MASK,
+ selector << BBCH_BBSEL_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_vbb = {
+ .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_iterate,
+};
+
+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 setting 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] },
+ { .name = "vbb", .driver_data = (void *) &tps65910_regs[13] },
+};
+
+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)
+ return NULL;
+
+ np = pdev->dev.parent->of_node;
+ regulators = of_get_child_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].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_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)
+ return -ENOMEM;
+
+ pmic->mfd = tps65910;
+ platform_set_drvdata(pdev, pmic);
+
+ /* Give control of all register to control port */
+ err = tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
+ DEVCTRL_SR_CTL_I2C_SEL_MASK);
+ if (err < 0)
+ return err;
+
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65910:
+ BUILD_BUG_ON(TPS65910_NUM_REGS < ARRAY_SIZE(tps65910_regs));
+ 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;
+ /* Work around silicon erratum SWCZ010: output programmed
+ * voltage level can go higher than expected or crash
+ * Workaround: use no synchronization of DCDC clocks
+ */
+ tps65910_reg_clear_bits(pmic->mfd, TPS65910_DCDCCTRL,
+ DCDCCTRL_DCDCCKSYNC_MASK);
+ break;
+ case TPS65911:
+ BUILD_BUG_ON(TPS65910_NUM_REGS < ARRAY_SIZE(tps65911_regs));
+ 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_kcalloc(&pdev->dev,
+ pmic->num_regulators,
+ sizeof(struct regulator_desc),
+ GFP_KERNEL);
+ if (!pmic->desc)
+ return -ENOMEM;
+
+ pmic->info = devm_kcalloc(&pdev->dev,
+ pmic->num_regulators,
+ sizeof(struct tps_info *),
+ GFP_KERNEL);
+ if (!pmic->info)
+ return -ENOMEM;
+
+ pmic->rdev = devm_kcalloc(&pdev->dev,
+ pmic->num_regulators,
+ sizeof(struct regulator_dev *),
+ GFP_KERNEL);
+ if (!pmic->rdev)
+ return -ENOMEM;
+
+ for (i = 0; i < pmic->num_regulators; i++, info++) {
+ /* 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 (i == TPS65910_REG_VBB &&
+ tps65910_chip_id(tps65910) == TPS65910) {
+ pmic->desc[i].ops = &tps65910_ops_vbb;
+ pmic->desc[i].volt_table = info->voltage_table;
+ } 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 = pmic_plat_data->tps65910_pmic_init_data[i];
+ config.driver_data = pmic;
+ config.regmap = tps65910->regmap;
+
+ if (tps65910_reg_matches)
+ config.of_node = tps65910_reg_matches[i].of_node;
+
+ rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps65910->dev,
+ "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+
+ /* Save regulator for cleanup */
+ pmic->rdev[i] = rdev;
+ }
+ 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",
+ },
+ .probe = tps65910_probe,
+ .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 <gg@slimlogic.co.uk>");
+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 000000000..276faeddc
--- /dev/null
+++ b/drivers/regulator/tps65912-regulator.c
@@ -0,0 +1,169 @@
+/*
+ * Regulator driver for TI TPS65912x PMICs
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65218 driver and the previous TPS65912 driver by
+ * Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/tps65912.h>
+
+enum tps65912_regulators { DCDC1, DCDC2, DCDC3, DCDC4, LDO1, LDO2, LDO3,
+ LDO4, LDO5, LDO6, LDO7, LDO8, LDO9, LDO10 };
+
+#define TPS65912_REGULATOR(_name, _id, _of_match, _ops, _vr, _er, _lr) \
+ [_id] = { \
+ .name = _name, \
+ .of_match = _of_match, \
+ .regulators_node = "regulators", \
+ .id = _id, \
+ .ops = &_ops, \
+ .n_voltages = 64, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = _er, \
+ .enable_mask = BIT(7), \
+ .volt_table = NULL, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = ARRAY_SIZE(_lr), \
+ }
+
+static const struct regulator_linear_range tps65912_dcdc_ranges[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x0, 0x3f, 50000),
+};
+
+static const struct regulator_linear_range tps65912_ldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 25000),
+ REGULATOR_LINEAR_RANGE(1650000, 0x21, 0x3c, 50000),
+ REGULATOR_LINEAR_RANGE(3100000, 0x3d, 0x3f, 100000),
+};
+
+/* Operations permitted on DCDCx */
+static struct regulator_ops tps65912_ops_dcdc = {
+ .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_range,
+};
+
+/* Operations permitted on LDOx */
+static struct regulator_ops tps65912_ops_ldo = {
+ .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_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static const struct regulator_desc regulators[] = {
+ TPS65912_REGULATOR("DCDC1", DCDC1, "dcdc1", tps65912_ops_dcdc,
+ TPS65912_DCDC1_OP, TPS65912_DCDC1_CTRL,
+ tps65912_dcdc_ranges),
+ TPS65912_REGULATOR("DCDC2", DCDC2, "dcdc2", tps65912_ops_dcdc,
+ TPS65912_DCDC2_OP, TPS65912_DCDC2_CTRL,
+ tps65912_dcdc_ranges),
+ TPS65912_REGULATOR("DCDC3", DCDC3, "dcdc3", tps65912_ops_dcdc,
+ TPS65912_DCDC3_OP, TPS65912_DCDC3_CTRL,
+ tps65912_dcdc_ranges),
+ TPS65912_REGULATOR("DCDC4", DCDC4, "dcdc4", tps65912_ops_dcdc,
+ TPS65912_DCDC4_OP, TPS65912_DCDC4_CTRL,
+ tps65912_dcdc_ranges),
+ TPS65912_REGULATOR("LDO1", LDO1, "ldo1", tps65912_ops_ldo,
+ TPS65912_LDO1_OP, TPS65912_LDO1_AVS,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO2", LDO2, "ldo2", tps65912_ops_ldo,
+ TPS65912_LDO2_OP, TPS65912_LDO2_AVS,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO3", LDO3, "ldo3", tps65912_ops_ldo,
+ TPS65912_LDO3_OP, TPS65912_LDO3_AVS,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO4", LDO4, "ldo4", tps65912_ops_ldo,
+ TPS65912_LDO4_OP, TPS65912_LDO4_AVS,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO5", LDO5, "ldo5", tps65912_ops_ldo,
+ TPS65912_LDO5, TPS65912_LDO5,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO6", LDO6, "ldo6", tps65912_ops_ldo,
+ TPS65912_LDO6, TPS65912_LDO6,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO7", LDO7, "ldo7", tps65912_ops_ldo,
+ TPS65912_LDO7, TPS65912_LDO7,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO8", LDO8, "ldo8", tps65912_ops_ldo,
+ TPS65912_LDO8, TPS65912_LDO8,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO9", LDO9, "ldo9", tps65912_ops_ldo,
+ TPS65912_LDO9, TPS65912_LDO9,
+ tps65912_ldo_ranges),
+ TPS65912_REGULATOR("LDO10", LDO10, "ldo10", tps65912_ops_ldo,
+ TPS65912_LDO10, TPS65912_LDO10,
+ tps65912_ldo_ranges),
+};
+
+static int tps65912_regulator_probe(struct platform_device *pdev)
+{
+ struct tps65912 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i;
+
+ platform_set_drvdata(pdev, tps);
+
+ config.dev = &pdev->dev;
+ config.driver_data = tps;
+ config.dev->of_node = tps->dev->of_node;
+ config.regmap = tps->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id tps65912_regulator_id_table[] = {
+ { "tps65912-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65912_regulator_id_table);
+
+static struct platform_driver tps65912_regulator_driver = {
+ .driver = {
+ .name = "tps65912-regulator",
+ },
+ .probe = tps65912_regulator_probe,
+ .id_table = tps65912_regulator_id_table,
+};
+module_platform_driver(tps65912_regulator_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TPS65912 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c
new file mode 100644
index 000000000..100114740
--- /dev/null
+++ b/drivers/regulator/tps80031-regulator.c
@@ -0,0 +1,768 @@
+/*
+ * 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 <ldewangan@nvidia.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.
+ *
+ * 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 <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps80031.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+/* 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,
+ &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, &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_kcalloc(&pdev->dev,
+ TPS80031_REGULATOR_MAX, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ 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);
+ return ret;
+ }
+
+ 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);
+ return ret;
+ }
+ }
+ rdev = devm_regulator_register(&pdev->dev, &ri->rinfo->desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "register regulator failed %s\n",
+ ri->rinfo->desc.name);
+ return PTR_ERR(rdev);
+ }
+ ri->rdev = rdev;
+ }
+
+ platform_set_drvdata(pdev, pmic);
+ return 0;
+}
+
+static struct platform_driver tps80031_regulator_driver = {
+ .driver = {
+ .name = "tps80031-pmic",
+ },
+ .probe = tps80031_regulator_probe,
+};
+
+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 <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
new file mode 100644
index 000000000..884c7505e
--- /dev/null
+++ b/drivers/regulator/twl-regulator.c
@@ -0,0 +1,674 @@
+/*
+ * 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 <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/twl.h>
+#include <linux/delay.h>
+
+/*
+ * The TWL4030/TW5030/TPS659x0 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;
+
+ /* used by regulator core */
+ struct regulator_desc desc;
+
+ /* chip specific features */
+ unsigned long features;
+
+ /* 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
+
+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;
+}
+
+#define PB_I2C_BUSY BIT(0)
+#define PB_I2C_BWEN BIT(1)
+
+/* Wait until buffer empty/ready to send a word on power bus. */
+static int twl4030_wait_pb_ready(void)
+{
+
+ int ret;
+ int timeout = 10;
+ u8 val;
+
+ do {
+ ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val,
+ TWL4030_PM_MASTER_PB_CFG);
+ if (ret < 0)
+ return ret;
+
+ if (!(val & PB_I2C_BUSY))
+ return 0;
+
+ mdelay(1);
+ timeout--;
+ } while (timeout);
+
+ return -ETIMEDOUT;
+}
+
+/* Send a word over the powerbus */
+static int twl4030_send_pb_msg(unsigned msg)
+{
+ u8 val;
+ int ret;
+
+ /* save powerbus configuration */
+ ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val,
+ TWL4030_PM_MASTER_PB_CFG);
+ if (ret < 0)
+ return ret;
+
+ /* Enable i2c access to powerbus */
+ ret = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val | PB_I2C_BWEN,
+ TWL4030_PM_MASTER_PB_CFG);
+ if (ret < 0)
+ return ret;
+
+ ret = twl4030_wait_pb_ready();
+ if (ret < 0)
+ return ret;
+
+ ret = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, msg >> 8,
+ TWL4030_PM_MASTER_PB_WORD_MSB);
+ if (ret < 0)
+ return ret;
+
+ ret = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, msg & 0xff,
+ TWL4030_PM_MASTER_PB_WORD_LSB);
+ if (ret < 0)
+ return ret;
+
+ ret = twl4030_wait_pb_ready();
+ if (ret < 0)
+ return ret;
+
+ /* Restore powerbus configuration */
+ return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val,
+ TWL4030_PM_MASTER_PB_CFG);
+}
+
+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 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 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 twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ unsigned message;
+
+ /* 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;
+ }
+
+ return twl4030_send_pb_msg(message);
+}
+
+static inline unsigned int twl4030reg_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case RES_STATE_ACTIVE:
+ return REGULATOR_MODE_NORMAL;
+ case RES_STATE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * 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);
+
+ 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;
+
+ 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 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,
+};
+
+/*----------------------------------------------------------------------*/
+
+#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, \
+ .of_map_mode = twl4030reg_map_mode, \
+ }, \
+ }
+
+#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, \
+ .of_map_mode = twl4030reg_map_mode, \
+ }, \
+ }
+
+#define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+ remap_conf) \
+static const struct twlreg_info TWLFIXED_INFO_##label = { \
+ .base = offset, \
+ .id = num, \
+ .remap = remap_conf, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL4030##_REG_##label, \
+ .n_voltages = 1, \
+ .ops = &twl4030fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = mVolts * 1000, \
+ .enable_time = turnon_delay, \
+ .of_map_mode = twl4030reg_map_mode, \
+ }, \
+ }
+
+/*
+ * 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 */
+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);
+
+#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 TWL6032_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6032, 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),
+ 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),
+ {},
+};
+MODULE_DEVICE_TABLE(of, twl_of_match);
+
+static int twlreg_probe(struct platform_device *pdev)
+{
+ int id;
+ struct twlreg_info *info;
+ const struct twlreg_info *template;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+
+ match = of_match_device(twl_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ template = match->data;
+ if (!template)
+ return -ENODEV;
+
+ id = template->desc.id;
+ initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ &template->desc);
+ if (!initdata)
+ return -EINVAL;
+
+ info = devm_kmemdup(&pdev->dev, template, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* 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;
+ }
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = info;
+ config.of_node = pdev->dev.of_node;
+
+ rdev = devm_regulator_register(&pdev->dev, &info->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "can't register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ return PTR_ERR(rdev);
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ 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;
+}
+
+MODULE_ALIAS("platform:twl4030_reg");
+
+static struct platform_driver twlreg_driver = {
+ .probe = twlreg_probe,
+ /* NOTE: short name, to work around driver model truncation of
+ * "twl_regulator.12" (and friends) to "twl_regulator.1".
+ */
+ .driver = {
+ .name = "twl4030_reg",
+ .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("TWL4030 regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/twl6030-regulator.c b/drivers/regulator/twl6030-regulator.c
new file mode 100644
index 000000000..219cbd910
--- /dev/null
+++ b/drivers/regulator/twl6030-regulator.c
@@ -0,0 +1,791 @@
+/*
+ * Split TWL6030 logic from twl-regulator.c:
+ * Copyright (C) 2008 David Brownell
+ *
+ * Copyright (C) 2016 Nicolae Rosia <nicolae.rosia@gmail.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 <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/twl.h>
+#include <linux/delay.h>
+
+struct twlreg_info {
+ /* start of regulator's PM_RECEIVER control register bank */
+ u8 base;
+
+ /* twl resource ID, for resource control state machine */
+ u8 id;
+
+ /* chip constraints on regulator behavior */
+ u16 min_mV;
+
+ u8 flags;
+
+ /* used by regulator core */
+ struct regulator_desc desc;
+
+ /* chip specific features */
+ unsigned long features;
+
+ /* 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
+/* 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)
+
+/* twl6032 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 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 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 & TWL6032_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);
+}
+
+#define PB_I2C_BUSY BIT(0)
+#define PB_I2C_BWEN BIT(1)
+
+
+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 & TWL6032_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 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 & TWL6032_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 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 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 & TWL6032_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);
+}
+
+static int twl6030coresmps_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ return -ENODEV;
+}
+
+static int twl6030coresmps_get_voltage(struct regulator_dev *rdev)
+{
+ 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 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 > 1500000) && (min_uV <= 1800000))
+ vsel = 60;
+ else if ((min_uV > 1350000) && (min_uV <= 1500000))
+ vsel = 59;
+ 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 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) \
+static const struct twlreg_info TWL6030_INFO_##label = { \
+ .base = offset, \
+ .min_mV = min_mVolts, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL6030_REG_##label, \
+ .n_voltages = 32, \
+ .ops = &twl6030ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
+#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts) \
+static const struct twlreg_info TWL6032_INFO_##label = { \
+ .base = offset, \
+ .min_mV = min_mVolts, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL6032_REG_##label, \
+ .n_voltages = 32, \
+ .ops = &twl6030ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
+#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \
+static const struct twlreg_info TWLFIXED_INFO_##label = { \
+ .base = offset, \
+ .id = 0, \
+ .min_mV = mVolts, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL6030##_REG_##label, \
+ .n_voltages = 1, \
+ .ops = &twl6030fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = mVolts * 1000, \
+ .enable_time = turnon_delay, \
+ .of_map_mode = NULL, \
+ }, \
+ }
+
+#define TWL6032_ADJUSTABLE_SMPS(label, offset) \
+static const struct twlreg_info TWLSMPS_INFO_##label = { \
+ .base = offset, \
+ .min_mV = 600, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL6032_REG_##label, \
+ .n_voltages = 63, \
+ .ops = &twlsmps_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
+/* 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);
+TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000);
+TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000);
+TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000);
+TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000);
+TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000);
+/* 6025 are renamed compared to 6030 versions */
+TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000);
+TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000);
+TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000);
+TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000);
+TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000);
+TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000);
+TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000);
+TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000);
+TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000);
+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);
+TWL6032_ADJUSTABLE_SMPS(SMPS3, 0x34);
+TWL6032_ADJUSTABLE_SMPS(SMPS4, 0x10);
+TWL6032_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 TWL6030_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6030, label)
+#define TWL6032_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6032, 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[] = {
+ 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),
+ TWL6032_OF_MATCH("ti,twl6032-ldo2", LDO2),
+ TWL6032_OF_MATCH("ti,twl6032-ldo4", LDO4),
+ TWL6032_OF_MATCH("ti,twl6032-ldo3", LDO3),
+ TWL6032_OF_MATCH("ti,twl6032-ldo5", LDO5),
+ TWL6032_OF_MATCH("ti,twl6032-ldo1", LDO1),
+ TWL6032_OF_MATCH("ti,twl6032-ldo7", LDO7),
+ TWL6032_OF_MATCH("ti,twl6032-ldo6", LDO6),
+ TWL6032_OF_MATCH("ti,twl6032-ldoln", LDOLN),
+ TWL6032_OF_MATCH("ti,twl6032-ldousb", LDOUSB),
+ 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,twl6032-smps3", SMPS3),
+ TWLSMPS_OF_MATCH("ti,twl6032-smps4", SMPS4),
+ TWLSMPS_OF_MATCH("ti,twl6032-vio", VIO),
+ {},
+};
+MODULE_DEVICE_TABLE(of, twl_of_match);
+
+static int twlreg_probe(struct platform_device *pdev)
+{
+ int id;
+ struct twlreg_info *info;
+ const struct twlreg_info *template;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_dev *rdev;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+
+ match = of_match_device(twl_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ template = match->data;
+ if (!template)
+ return -ENODEV;
+
+ id = template->desc.id;
+ initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ &template->desc);
+ if (!initdata)
+ return -EINVAL;
+
+ info = devm_kmemdup(&pdev->dev, template, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* 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 TWL6032_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 TWL6032_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 TWL6032_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 = devm_regulator_register(&pdev->dev, &info->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "can't register %s, %ld\n",
+ info->desc.name, PTR_ERR(rdev));
+ return PTR_ERR(rdev);
+ }
+ platform_set_drvdata(pdev, rdev);
+
+ /* 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;
+}
+
+MODULE_ALIAS("platform:twl6030_reg");
+
+static struct platform_driver twlreg_driver = {
+ .probe = twlreg_probe,
+ /* NOTE: short name, to work around driver model truncation of
+ * "twl_regulator.12" (and friends) to "twl_regulator.1".
+ */
+ .driver = {
+ .name = "twl6030_reg",
+ .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("TWL6030 regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/uniphier-regulator.c b/drivers/regulator/uniphier-regulator.c
new file mode 100644
index 000000000..a2e3654b6
--- /dev/null
+++ b/drivers/regulator/uniphier-regulator.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Regulator controller driver for UniPhier SoC
+// Copyright 2018 Socionext Inc.
+// Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/reset.h>
+
+#define MAX_CLKS 2
+#define MAX_RSTS 2
+
+struct uniphier_regulator_soc_data {
+ int nclks;
+ const char * const *clock_names;
+ int nrsts;
+ const char * const *reset_names;
+ const struct regulator_desc *desc;
+ const struct regmap_config *regconf;
+};
+
+struct uniphier_regulator_priv {
+ struct clk_bulk_data clk[MAX_CLKS];
+ struct reset_control *rst[MAX_RSTS];
+ const struct uniphier_regulator_soc_data *data;
+};
+
+static struct regulator_ops uniphier_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static int uniphier_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct uniphier_regulator_priv *priv;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ struct resource *res;
+ void __iomem *base;
+ const char *name;
+ int i, ret, nr;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = of_device_get_match_data(dev);
+ if (WARN_ON(!priv->data))
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ for (i = 0; i < priv->data->nclks; i++)
+ priv->clk[i].id = priv->data->clock_names[i];
+ ret = devm_clk_bulk_get(dev, priv->data->nclks, priv->clk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < priv->data->nrsts; i++) {
+ name = priv->data->reset_names[i];
+ priv->rst[i] = devm_reset_control_get_shared(dev, name);
+ if (IS_ERR(priv->rst[i]))
+ return PTR_ERR(priv->rst[i]);
+ }
+
+ ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk);
+ if (ret)
+ return ret;
+
+ for (nr = 0; nr < priv->data->nrsts; nr++) {
+ ret = reset_control_deassert(priv->rst[nr]);
+ if (ret)
+ goto out_rst_assert;
+ }
+
+ regmap = devm_regmap_init_mmio(dev, base, priv->data->regconf);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ config.dev = dev;
+ config.driver_data = priv;
+ config.of_node = dev->of_node;
+ config.regmap = regmap;
+ config.init_data = of_get_regulator_init_data(dev, dev->of_node,
+ priv->data->desc);
+ rdev = devm_regulator_register(dev, priv->data->desc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ goto out_rst_assert;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+out_rst_assert:
+ while (nr--)
+ reset_control_assert(priv->rst[nr]);
+
+ clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
+
+ return ret;
+}
+
+static int uniphier_regulator_remove(struct platform_device *pdev)
+{
+ struct uniphier_regulator_priv *priv = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < priv->data->nrsts; i++)
+ reset_control_assert(priv->rst[i]);
+
+ clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
+
+ return 0;
+}
+
+/* USB3 controller data */
+#define USB3VBUS_OFFSET 0x0
+#define USB3VBUS_REG BIT(4)
+#define USB3VBUS_REG_EN BIT(3)
+static const struct regulator_desc uniphier_usb3_regulator_desc = {
+ .name = "vbus",
+ .of_match = of_match_ptr("vbus"),
+ .ops = &uniphier_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .enable_reg = USB3VBUS_OFFSET,
+ .enable_mask = USB3VBUS_REG_EN | USB3VBUS_REG,
+ .enable_val = USB3VBUS_REG_EN | USB3VBUS_REG,
+ .disable_val = USB3VBUS_REG_EN,
+};
+
+static const struct regmap_config uniphier_usb3_regulator_regconf = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 1,
+};
+
+static const char * const uniphier_pro4_clock_reset_names[] = {
+ "gio", "link",
+};
+
+static const struct uniphier_regulator_soc_data uniphier_pro4_usb3_data = {
+ .nclks = ARRAY_SIZE(uniphier_pro4_clock_reset_names),
+ .clock_names = uniphier_pro4_clock_reset_names,
+ .nrsts = ARRAY_SIZE(uniphier_pro4_clock_reset_names),
+ .reset_names = uniphier_pro4_clock_reset_names,
+ .desc = &uniphier_usb3_regulator_desc,
+ .regconf = &uniphier_usb3_regulator_regconf,
+};
+
+static const char * const uniphier_pxs2_clock_reset_names[] = {
+ "link",
+};
+
+static const struct uniphier_regulator_soc_data uniphier_pxs2_usb3_data = {
+ .nclks = ARRAY_SIZE(uniphier_pxs2_clock_reset_names),
+ .clock_names = uniphier_pxs2_clock_reset_names,
+ .nrsts = ARRAY_SIZE(uniphier_pxs2_clock_reset_names),
+ .reset_names = uniphier_pxs2_clock_reset_names,
+ .desc = &uniphier_usb3_regulator_desc,
+ .regconf = &uniphier_usb3_regulator_regconf,
+};
+
+static const struct of_device_id uniphier_regulator_match[] = {
+ /* USB VBUS */
+ {
+ .compatible = "socionext,uniphier-pro4-usb3-regulator",
+ .data = &uniphier_pro4_usb3_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-usb3-regulator",
+ .data = &uniphier_pxs2_usb3_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-usb3-regulator",
+ .data = &uniphier_pxs2_usb3_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-usb3-regulator",
+ .data = &uniphier_pxs2_usb3_data,
+ },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, uniphier_regulator_match);
+
+static struct platform_driver uniphier_regulator_driver = {
+ .probe = uniphier_regulator_probe,
+ .remove = uniphier_regulator_remove,
+ .driver = {
+ .name = "uniphier-regulator",
+ .of_match_table = uniphier_regulator_match,
+ },
+};
+module_platform_driver(uniphier_regulator_driver);
+
+MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
+MODULE_DESCRIPTION("UniPhier Regulator Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
new file mode 100644
index 000000000..765acc11c
--- /dev/null
+++ b/drivers/regulator/userspace-consumer.c
@@ -0,0 +1,186 @@
+/*
+ * userspace-consumer.c
+ *
+ * Copyright 2009 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based of virtual consumer driver:
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.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 <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/userspace-consumer.h>
+#include <linux/slab.h>
+
+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 = dev_get_platdata(&pdev->dev);
+ 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 <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c
new file mode 100644
index 000000000..78de00203
--- /dev/null
+++ b/drivers/regulator/vctrl-regulator.c
@@ -0,0 +1,546 @@
+/*
+ * Driver for voltage controller regulators
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/sort.h>
+
+struct vctrl_voltage_range {
+ int min_uV;
+ int max_uV;
+};
+
+struct vctrl_voltage_ranges {
+ struct vctrl_voltage_range ctrl;
+ struct vctrl_voltage_range out;
+};
+
+struct vctrl_voltage_table {
+ int ctrl;
+ int out;
+ int ovp_min_sel;
+};
+
+struct vctrl_data {
+ struct regulator_dev *rdev;
+ struct regulator_desc desc;
+ struct regulator *ctrl_reg;
+ bool enabled;
+ unsigned int min_slew_down_rate;
+ unsigned int ovp_threshold;
+ struct vctrl_voltage_ranges vrange;
+ struct vctrl_voltage_table *vtable;
+ unsigned int sel;
+};
+
+static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV)
+{
+ struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+ struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+ return ctrl->min_uV +
+ DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) *
+ (ctrl->max_uV - ctrl->min_uV),
+ out->max_uV - out->min_uV);
+}
+
+static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)
+{
+ struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+ struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+ if (ctrl_uV < 0) {
+ pr_err("vctrl: failed to get control voltage\n");
+ return ctrl_uV;
+ }
+
+ if (ctrl_uV < ctrl->min_uV)
+ return out->min_uV;
+
+ if (ctrl_uV > ctrl->max_uV)
+ return out->max_uV;
+
+ return out->min_uV +
+ DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) *
+ (out->max_uV - out->min_uV),
+ ctrl->max_uV - ctrl->min_uV);
+}
+
+static int vctrl_get_voltage(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+
+ return vctrl_calc_output_voltage(vctrl, ctrl_uV);
+}
+
+static int vctrl_set_voltage(struct regulator_dev *rdev,
+ int req_min_uV, int req_max_uV,
+ unsigned int *selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ int orig_ctrl_uV = regulator_get_voltage(ctrl_reg);
+ int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
+ int ret;
+
+ if (req_min_uV >= uV || !vctrl->ovp_threshold)
+ /* voltage rising or no OVP */
+ return regulator_set_voltage(
+ ctrl_reg,
+ vctrl_calc_ctrl_voltage(vctrl, req_min_uV),
+ vctrl_calc_ctrl_voltage(vctrl, req_max_uV));
+
+ while (uV > req_min_uV) {
+ int max_drop_uV = (uV * vctrl->ovp_threshold) / 100;
+ int next_uV;
+ int next_ctrl_uV;
+ int delay;
+
+ /* Make sure no infinite loop even in crazy cases */
+ if (max_drop_uV == 0)
+ max_drop_uV = 1;
+
+ next_uV = max_t(int, req_min_uV, uV - max_drop_uV);
+ next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);
+
+ ret = regulator_set_voltage(ctrl_reg,
+ next_ctrl_uV,
+ next_ctrl_uV);
+ if (ret)
+ goto err;
+
+ delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate);
+ usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+
+ uV = next_uV;
+ }
+
+ return 0;
+
+err:
+ /* Try to go back to original voltage */
+ regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV);
+
+ return ret;
+}
+
+static int vctrl_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ return vctrl->sel;
+}
+
+static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ unsigned int orig_sel = vctrl->sel;
+ int ret;
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ if (selector >= vctrl->sel || !vctrl->ovp_threshold) {
+ /* voltage rising or no OVP */
+ ret = regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[selector].ctrl,
+ vctrl->vtable[selector].ctrl);
+ if (!ret)
+ vctrl->sel = selector;
+
+ return ret;
+ }
+
+ while (vctrl->sel != selector) {
+ unsigned int next_sel;
+ int delay;
+
+ if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel)
+ next_sel = selector;
+ else
+ next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
+
+ ret = regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[next_sel].ctrl,
+ vctrl->vtable[next_sel].ctrl);
+ if (ret) {
+ dev_err(&rdev->dev,
+ "failed to set control voltage to %duV\n",
+ vctrl->vtable[next_sel].ctrl);
+ goto err;
+ }
+ vctrl->sel = next_sel;
+
+ delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out -
+ vctrl->vtable[next_sel].out,
+ vctrl->min_slew_down_rate);
+ usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+ }
+
+ return 0;
+
+err:
+ if (vctrl->sel != orig_sel) {
+ /* Try to go back to original voltage */
+ if (!regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[orig_sel].ctrl,
+ vctrl->vtable[orig_sel].ctrl))
+ vctrl->sel = orig_sel;
+ else
+ dev_warn(&rdev->dev,
+ "failed to restore original voltage\n");
+ }
+
+ return ret;
+}
+
+static int vctrl_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ return vctrl->vtable[selector].out;
+}
+
+static int vctrl_parse_dt(struct platform_device *pdev,
+ struct vctrl_data *vctrl)
+{
+ int ret;
+ struct device_node *np = pdev->dev.of_node;
+ u32 pval;
+ u32 vrange_ctrl[2];
+
+ vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");
+ if (IS_ERR(vctrl->ctrl_reg))
+ return PTR_ERR(vctrl->ctrl_reg);
+
+ ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);
+ if (!ret) {
+ vctrl->ovp_threshold = pval;
+ if (vctrl->ovp_threshold > 100) {
+ dev_err(&pdev->dev,
+ "ovp-threshold-percent (%u) > 100\n",
+ vctrl->ovp_threshold);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32(np, "min-slew-down-rate", &pval);
+ if (!ret) {
+ vctrl->min_slew_down_rate = pval;
+
+ /* We use the value as int and as divider; sanity check */
+ if (vctrl->min_slew_down_rate == 0) {
+ dev_err(&pdev->dev,
+ "min-slew-down-rate must not be 0\n");
+ return -EINVAL;
+ } else if (vctrl->min_slew_down_rate > INT_MAX) {
+ dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n",
+ vctrl->min_slew_down_rate);
+ return -EINVAL;
+ }
+ }
+
+ if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) {
+ dev_err(&pdev->dev,
+ "ovp-threshold-percent requires min-slew-down-rate\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "regulator-min-microvolt", &pval);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to read regulator-min-microvolt: %d\n", ret);
+ return ret;
+ }
+ vctrl->vrange.out.min_uV = pval;
+
+ ret = of_property_read_u32(np, "regulator-max-microvolt", &pval);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to read regulator-max-microvolt: %d\n", ret);
+ return ret;
+ }
+ vctrl->vrange.out.max_uV = pval;
+
+ ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl,
+ 2);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (vrange_ctrl[0] >= vrange_ctrl[1]) {
+ dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n",
+ vrange_ctrl[0], vrange_ctrl[1]);
+ return -EINVAL;
+ }
+
+ vctrl->vrange.ctrl.min_uV = vrange_ctrl[0];
+ vctrl->vrange.ctrl.max_uV = vrange_ctrl[1];
+
+ return 0;
+}
+
+static int vctrl_cmp_ctrl_uV(const void *a, const void *b)
+{
+ const struct vctrl_voltage_table *at = a;
+ const struct vctrl_voltage_table *bt = b;
+
+ return at->ctrl - bt->ctrl;
+}
+
+static int vctrl_init_vtable(struct platform_device *pdev)
+{
+ struct vctrl_data *vctrl = platform_get_drvdata(pdev);
+ struct regulator_desc *rdesc = &vctrl->desc;
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;
+ int n_voltages;
+ int ctrl_uV;
+ int i, idx_vt;
+
+ n_voltages = regulator_count_voltages(ctrl_reg);
+
+ rdesc->n_voltages = n_voltages;
+
+ /* determine number of steps within the range of the vctrl regulator */
+ for (i = 0; i < n_voltages; i++) {
+ ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+ if (ctrl_uV < vrange_ctrl->min_uV ||
+ ctrl_uV > vrange_ctrl->max_uV) {
+ rdesc->n_voltages--;
+ continue;
+ }
+ }
+
+ if (rdesc->n_voltages == 0) {
+ dev_err(&pdev->dev, "invalid configuration\n");
+ return -EINVAL;
+ }
+
+ vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages,
+ sizeof(struct vctrl_voltage_table),
+ GFP_KERNEL);
+ if (!vctrl->vtable)
+ return -ENOMEM;
+
+ /* create mapping control <=> output voltage */
+ for (i = 0, idx_vt = 0; i < n_voltages; i++) {
+ ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+ if (ctrl_uV < vrange_ctrl->min_uV ||
+ ctrl_uV > vrange_ctrl->max_uV)
+ continue;
+
+ vctrl->vtable[idx_vt].ctrl = ctrl_uV;
+ vctrl->vtable[idx_vt].out =
+ vctrl_calc_output_voltage(vctrl, ctrl_uV);
+ idx_vt++;
+ }
+
+ /* we rely on the table to be ordered by ascending voltage */
+ sort(vctrl->vtable, rdesc->n_voltages,
+ sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV,
+ NULL);
+
+ /* pre-calculate OVP-safe downward transitions */
+ for (i = rdesc->n_voltages - 1; i > 0; i--) {
+ int j;
+ int ovp_min_uV = (vctrl->vtable[i].out *
+ (100 - vctrl->ovp_threshold)) / 100;
+
+ for (j = 0; j < i; j++) {
+ if (vctrl->vtable[j].out >= ovp_min_uV) {
+ vctrl->vtable[i].ovp_min_sel = j;
+ break;
+ }
+ }
+
+ if (j == i) {
+ dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n",
+ vctrl->vtable[i].out);
+ /* use next lowest voltage */
+ vctrl->vtable[i].ovp_min_sel = i - 1;
+ }
+ }
+
+ return 0;
+}
+
+static int vctrl_enable(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ret = regulator_enable(vctrl->ctrl_reg);
+
+ if (!ret)
+ vctrl->enabled = true;
+
+ return ret;
+}
+
+static int vctrl_disable(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ret = regulator_disable(vctrl->ctrl_reg);
+
+ if (!ret)
+ vctrl->enabled = false;
+
+ return ret;
+}
+
+static int vctrl_is_enabled(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ return vctrl->enabled;
+}
+
+static const struct regulator_ops vctrl_ops_cont = {
+ .enable = vctrl_enable,
+ .disable = vctrl_disable,
+ .is_enabled = vctrl_is_enabled,
+ .get_voltage = vctrl_get_voltage,
+ .set_voltage = vctrl_set_voltage,
+};
+
+static const struct regulator_ops vctrl_ops_non_cont = {
+ .enable = vctrl_enable,
+ .disable = vctrl_disable,
+ .is_enabled = vctrl_is_enabled,
+ .set_voltage_sel = vctrl_set_voltage_sel,
+ .get_voltage_sel = vctrl_get_voltage_sel,
+ .list_voltage = vctrl_list_voltage,
+ .map_voltage = regulator_map_voltage_iterate,
+};
+
+static int vctrl_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct vctrl_data *vctrl;
+ const struct regulator_init_data *init_data;
+ struct regulator_desc *rdesc;
+ struct regulator_config cfg = { };
+ struct vctrl_voltage_range *vrange_ctrl;
+ int ctrl_uV;
+ int ret;
+
+ vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data),
+ GFP_KERNEL);
+ if (!vctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vctrl);
+
+ ret = vctrl_parse_dt(pdev, vctrl);
+ if (ret)
+ return ret;
+
+ vrange_ctrl = &vctrl->vrange.ctrl;
+
+ rdesc = &vctrl->desc;
+ rdesc->name = "vctrl";
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->owner = THIS_MODULE;
+
+ if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) ||
+ (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) {
+ rdesc->continuous_voltage_range = true;
+ rdesc->ops = &vctrl_ops_cont;
+ } else {
+ rdesc->ops = &vctrl_ops_non_cont;
+ }
+
+ init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc);
+ if (!init_data)
+ return -ENOMEM;
+
+ cfg.of_node = np;
+ cfg.dev = &pdev->dev;
+ cfg.driver_data = vctrl;
+ cfg.init_data = init_data;
+
+ if (!rdesc->continuous_voltage_range) {
+ ret = vctrl_init_vtable(pdev);
+ if (ret)
+ return ret;
+
+ ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+ if (ctrl_uV < 0) {
+ dev_err(&pdev->dev, "failed to get control voltage\n");
+ return ctrl_uV;
+ }
+
+ /* determine current voltage selector from control voltage */
+ if (ctrl_uV < vrange_ctrl->min_uV) {
+ vctrl->sel = 0;
+ } else if (ctrl_uV > vrange_ctrl->max_uV) {
+ vctrl->sel = rdesc->n_voltages - 1;
+ } else {
+ int i;
+
+ for (i = 0; i < rdesc->n_voltages; i++) {
+ if (ctrl_uV == vctrl->vtable[i].ctrl) {
+ vctrl->sel = i;
+ break;
+ }
+ }
+ }
+ }
+
+ vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);
+ if (IS_ERR(vctrl->rdev)) {
+ ret = PTR_ERR(vctrl->rdev);
+ dev_err(&pdev->dev, "failed to register regulator: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id vctrl_of_match[] = {
+ { .compatible = "vctrl-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vctrl_of_match);
+
+static struct platform_driver vctrl_driver = {
+ .probe = vctrl_probe,
+ .driver = {
+ .name = "vctrl-regulator",
+ .of_match_table = of_match_ptr(vctrl_of_match),
+ },
+};
+
+module_platform_driver(vctrl_driver);
+
+MODULE_DESCRIPTION("Voltage Controlled Regulator Driver");
+MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/vexpress-regulator.c b/drivers/regulator/vexpress-regulator.c
new file mode 100644
index 000000000..c810cbbd4
--- /dev/null
+++ b/drivers/regulator/vexpress-regulator.c
@@ -0,0 +1,121 @@
+/*
+ * 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 <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/vexpress.h>
+
+struct vexpress_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *regdev;
+ struct regmap *regmap;
+};
+
+static int vexpress_regulator_get_voltage(struct regulator_dev *regdev)
+{
+ struct vexpress_regulator *reg = rdev_get_drvdata(regdev);
+ u32 uV;
+ int err = regmap_read(reg->regmap, 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 regmap_write(reg->regmap, 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)
+{
+ 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)
+ return -ENOMEM;
+
+ reg->regmap = devm_regmap_init_vexpress_config(&pdev->dev);
+ if (IS_ERR(reg->regmap))
+ return PTR_ERR(reg->regmap);
+
+ 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,
+ &reg->desc);
+ if (!init_data)
+ return -EINVAL;
+
+ 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 = devm_regulator_register(&pdev->dev, &reg->desc, &config);
+ if (IS_ERR(reg->regdev))
+ return PTR_ERR(reg->regdev);
+
+ platform_set_drvdata(pdev, reg);
+
+ return 0;
+}
+
+static const struct of_device_id vexpress_regulator_of_match[] = {
+ { .compatible = "arm,vexpress-volt", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vexpress_regulator_of_match);
+
+static struct platform_driver vexpress_regulator_driver = {
+ .probe = vexpress_regulator_probe,
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = vexpress_regulator_of_match,
+ },
+};
+
+module_platform_driver(vexpress_regulator_driver);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
+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 000000000..a6f1c7a99
--- /dev/null
+++ b/drivers/regulator/virtual.c
@@ -0,0 +1,349 @@
+/*
+ * reg-virtual-consumer.c
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+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, 0664, show_min_uV, set_min_uV);
+static DEVICE_ATTR(max_microvolts, 0664, show_max_uV, set_max_uV);
+static DEVICE_ATTR(min_microamps, 0664, show_min_uA, set_min_uA);
+static DEVICE_ATTR(max_microamps, 0664, show_max_uA, set_max_uA);
+static DEVICE_ATTR(mode, 0664, 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 = dev_get_platdata(&pdev->dev);
+ 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,
+ &regulator_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, &regulator_virtual_attr_group);
+
+ if (drvdata->enabled)
+ regulator_disable(drvdata->regulator);
+
+ return 0;
+}
+
+static struct platform_driver regulator_virtual_consumer_driver = {
+ .probe = regulator_virtual_probe,
+ .remove = regulator_virtual_remove,
+ .driver = {
+ .name = "reg-virt-consumer",
+ },
+};
+
+module_platform_driver(regulator_virtual_consumer_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+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 000000000..df591435d
--- /dev/null
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -0,0 +1,913 @@
+/*
+ * wm831x-dcdc.c -- DC-DC buck convertor driver for the WM831x series
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/regulator.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+#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 const unsigned int wm831x_dcdc_ilim[] = {
+ 125000, 250000, 375000, 500000, 625000, 750000, 875000, 1000000
+};
+
+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 const 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 platform_device *pdev,
+ 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 = devm_gpio_request_one(&pdev->dev, 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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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(pdev, 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 = devm_regulator_register(&pdev->dev, &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 = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_dcdc_uv_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err;
+ }
+
+ irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_dcdc_oc_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
+ irq, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static struct platform_driver wm831x_buckv_driver = {
+ .probe = wm831x_buckv_probe,
+ .driver = {
+ .name = "wm831x-buckv",
+ },
+};
+
+/*
+ * 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 const 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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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 = devm_regulator_register(&pdev->dev, &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 = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_dcdc_uv_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static struct platform_driver wm831x_buckp_driver = {
+ .probe = wm831x_buckp_probe,
+ .driver = {
+ .name = "wm831x-buckp",
+ },
+};
+
+/*
+ * 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 const 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 = dev_get_platdata(wm831x->dev);
+ 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)
+ return -ENOMEM;
+
+ dcdc->wm831x = wm831x;
+
+ res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No REG resource\n");
+ return -EINVAL;
+ }
+ 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 = devm_regulator_register(&pdev->dev, &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);
+ return ret;
+ }
+
+ irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_dcdc_uv_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name,
+ dcdc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, dcdc);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_boostp_driver = {
+ .probe = wm831x_boostp_probe,
+ .driver = {
+ .name = "wm831x-boostp",
+ },
+};
+
+/*
+ * External Power Enable
+ *
+ * These aren't actually DCDCs but look like them in hardware so share
+ * code.
+ */
+
+#define WM831X_EPE_BASE 6
+
+static const 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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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 = devm_regulator_register(&pdev->dev, &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 struct platform_driver wm831x_epe_driver = {
+ .probe = wm831x_epe_probe,
+ .driver = {
+ .name = "wm831x-epe",
+ },
+};
+
+static struct platform_driver * const drivers[] = {
+ &wm831x_buckv_driver,
+ &wm831x_buckp_driver,
+ &wm831x_boostp_driver,
+ &wm831x_epe_driver,
+};
+
+static int __init wm831x_dcdc_init(void)
+{
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
+}
+subsys_initcall(wm831x_dcdc_init);
+
+static void __exit wm831x_dcdc_exit(void)
+{
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
+}
+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 000000000..6dd891d7e
--- /dev/null
+++ b/drivers/regulator/wm831x-isink.c
@@ -0,0 +1,252 @@
+/*
+ * wm831x-isink.c -- Current sink driver for the WM831x series
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/regulator.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+#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 const 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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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 = devm_regulator_register(&pdev->dev, &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 = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_isink_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ isink->name,
+ isink);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
+ irq, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, isink);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static struct platform_driver wm831x_isink_driver = {
+ .probe = wm831x_isink_probe,
+ .driver = {
+ .name = "wm831x-isink",
+ },
+};
+
+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 000000000..e4a6f8884
--- /dev/null
+++ b/drivers/regulator/wm831x-ldo.c
@@ -0,0 +1,680 @@
+/*
+ * wm831x-ldo.c -- LDO driver for the WM831x series
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/regulator.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+#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
+ */
+
+static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
+ REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
+};
+
+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 = regulator_map_voltage_linear_range(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 const struct regulator_ops wm831x_gp_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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 = 32;
+ 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;
+ ldo->desc.linear_ranges = wm831x_gp_ldo_ranges;
+ ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_gp_ldo_ranges);
+
+ config.dev = pdev->dev.parent;
+ if (pdata)
+ config.init_data = pdata->ldo[id];
+ config.driver_data = ldo;
+ config.regmap = wm831x->regmap;
+
+ ldo->regulator = devm_regulator_register(&pdev->dev, &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 = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_ldo_uv_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ldo->name,
+ ldo);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, ldo);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static struct platform_driver wm831x_gp_ldo_driver = {
+ .probe = wm831x_gp_ldo_probe,
+ .driver = {
+ .name = "wm831x-ldo",
+ },
+};
+
+/*
+ * Analogue LDOs
+ */
+
+static const struct regulator_linear_range wm831x_aldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0, 12, 50000),
+ REGULATOR_LINEAR_RANGE(1700000, 13, 31, 100000),
+};
+
+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 = regulator_map_voltage_linear_range(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 const struct regulator_ops wm831x_aldo_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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 = 32;
+ ldo->desc.linear_ranges = wm831x_aldo_ranges;
+ ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_aldo_ranges);
+ 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 = devm_regulator_register(&pdev->dev, &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 = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wm831x_ldo_uv_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ldo->name, ldo);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
+ irq, ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, ldo);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static struct platform_driver wm831x_aldo_driver = {
+ .probe = wm831x_aldo_probe,
+ .driver = {
+ .name = "wm831x-aldo",
+ },
+};
+
+/*
+ * 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 const 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 = dev_get_platdata(wm831x->dev);
+ 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)
+ 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 = devm_regulator_register(&pdev->dev, &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 struct platform_driver wm831x_alive_ldo_driver = {
+ .probe = wm831x_alive_ldo_probe,
+ .driver = {
+ .name = "wm831x-alive-ldo",
+ },
+};
+
+static struct platform_driver * const drivers[] = {
+ &wm831x_gp_ldo_driver,
+ &wm831x_aldo_driver,
+ &wm831x_alive_ldo_driver,
+};
+
+static int __init wm831x_ldo_init(void)
+{
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
+}
+subsys_initcall(wm831x_ldo_init);
+
+static void __exit wm831x_ldo_exit(void)
+{
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
+}
+module_exit(wm831x_ldo_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+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 000000000..8ad11b074
--- /dev/null
+++ b/drivers/regulator/wm8350-regulator.c
@@ -0,0 +1,1397 @@
+/*
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/* 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 sel;
+
+ /* 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 const struct regulator_linear_range wm8350_ldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 15, 50000),
+ REGULATOR_LINEAR_RANGE(1800000, 16, 31, 100000),
+};
+
+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 = regulator_map_voltage_linear_range(rdev, uV, uV);
+ if (sel < 0)
+ return sel;
+
+ /* 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;
+ i++;
+ }
+ 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 const 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 const 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 const struct regulator_ops wm8350_ldo_ops = {
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .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 const 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,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
+ .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,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
+ .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,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
+ .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,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
+ .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;
+
+ mutex_lock(&rdev->mutex);
+ if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_REGULATION_OUT,
+ NULL);
+ else
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ 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 = dev_get_platdata(&pdev->dev);
+ config.driver_data = dev_get_drvdata(&pdev->dev);
+ config.regmap = wm8350->regmap;
+
+ /* register regulator */
+ rdev = devm_regulator_register(&pdev->dev, &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) {
+ 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);
+
+ 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 000000000..fb1837657
--- /dev/null
+++ b/drivers/regulator/wm8400-regulator.c
@@ -0,0 +1,282 @@
+/*
+ * Regulator support for WM8400
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/bug.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/wm8400-private.h>
+
+static const struct regulator_linear_range wm8400_ldo_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
+ REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
+};
+
+static const struct regulator_ops wm8400_ldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+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 const 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,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
+ .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,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
+ .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,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
+ .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,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
+ .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 = dev_get_platdata(&pdev->dev);
+ config.driver_data = wm8400;
+ config.regmap = wm8400->regmap;
+
+ rdev = devm_regulator_register(&pdev->dev, &regulators[pdev->id],
+ &config);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static struct platform_driver wm8400_regulator_driver = {
+ .driver = {
+ .name = "wm8400-regulator",
+ },
+ .probe = wm8400_regulator_probe,
+};
+
+/**
+ * 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 <broonie@opensource.wolfsonmicro.com>");
+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 000000000..7a4ce6df4
--- /dev/null
+++ b/drivers/regulator/wm8994-regulator.c
@@ -0,0 +1,199 @@
+/*
+ * wm8994-regulator.c -- Regulator driver for the WM8994
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+
+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 const 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 const 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 = dev_get_platdata(wm8994->dev);
+ 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)
+ 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;
+ config.ena_gpio_initialized = true;
+ }
+
+ /* 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 = devm_regulator_register(&pdev->dev,
+ &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 struct platform_driver wm8994_ldo_driver = {
+ .probe = wm8994_ldo_probe,
+ .driver = {
+ .name = "wm8994-ldo",
+ },
+};
+
+module_platform_driver(wm8994_ldo_driver);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8994 LDO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8994-ldo");