diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-04 11:16:38 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-04 11:16:38 -0700 |
| commit | 5a9f228a183bc18bbc64a12a962adc2c7305782c (patch) | |
| tree | dd24c20eeb668a36c7b88a7d405d20ef303b9480 | |
| parent | 808c2b0583f010d3993ae534980af55c43c1adba (diff) | |
| parent | 2ed89d577c172506b5bada8c5810f607a4eae771 (diff) | |
| download | linux-5a9f228a183bc18bbc64a12a962adc2c7305782c.tar.gz linux-5a9f228a183bc18bbc64a12a962adc2c7305782c.tar.bz2 linux-5a9f228a183bc18bbc64a12a962adc2c7305782c.zip | |
Merge tag 'regulator-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates from Mark Brown:
"This is an extremely quiet release for the regulator API, we've got a
small set of bug fixes and minor feature enhancements for drivers plus
a couple of more visible changes:
- add support for ramp times in regulators that don't use selectors.
- new driver for LTC3676"
* tag 'regulator-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator:
regulator: dbx500: remove unused functions in dbx500-prcmu.c
regulator: pv88080: Update regulator for PV88080 BB silicon support
regulator: core: don't return error with inadequate reason
regulator: tps65910: Work around silicon erratum SWCZ010
regulator: core: Add set_voltage_time op
regulator: core: Don't skip set_voltage_time when ramp delay disabled
regulator: core: Simplify error flow in _regulator_do_set_voltage()
regulator: core: Use local ops variable in _regulator_do_set_voltage()
regulator: hi6421: mark hi6421_regulator_ldo_get_optimum_mode() static
regulator: Kconfig: Fix typo
regulator: bindings: Use the correct symbol for second
regulator: Remove support for optional supplies in the bulk API
regulator: Add LTC3676 support
regulator: rk808: Delete owner assignment
regulator: tps65218: do not disable DCDC3 during poweroff on broken PMICs
mfd: tps65218: add version check to the PMIC probe
| -rw-r--r-- | Documentation/devicetree/bindings/regulator/ltc3676.txt | 94 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/regulator/pv88080.txt | 23 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/regulator/regulator.txt | 2 | ||||
| -rw-r--r-- | drivers/mfd/tps65218.c | 9 | ||||
| -rw-r--r-- | drivers/regulator/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
| -rw-r--r-- | drivers/regulator/core.c | 154 | ||||
| -rw-r--r-- | drivers/regulator/dbx500-prcmu.c | 18 | ||||
| -rw-r--r-- | drivers/regulator/devres.c | 7 | ||||
| -rw-r--r-- | drivers/regulator/hi6421-regulator.c | 3 | ||||
| -rw-r--r-- | drivers/regulator/ltc3676.c | 420 | ||||
| -rw-r--r-- | drivers/regulator/pv88080-regulator.c | 263 | ||||
| -rw-r--r-- | drivers/regulator/pv88080-regulator.h | 114 | ||||
| -rw-r--r-- | drivers/regulator/pwm-regulator.c | 10 | ||||
| -rw-r--r-- | drivers/regulator/rk808-regulator.c | 3 | ||||
| -rw-r--r-- | drivers/regulator/tps65218-regulator.c | 8 | ||||
| -rw-r--r-- | drivers/regulator/tps65910-regulator.c | 6 | ||||
| -rw-r--r-- | include/linux/mfd/tps65218.h | 6 | ||||
| -rw-r--r-- | include/linux/regulator/consumer.h | 3 | ||||
| -rw-r--r-- | include/linux/regulator/driver.h | 10 |
20 files changed, 981 insertions, 183 deletions
diff --git a/Documentation/devicetree/bindings/regulator/ltc3676.txt b/Documentation/devicetree/bindings/regulator/ltc3676.txt new file mode 100644 index 000000000000..d4eb366ce18c --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/ltc3676.txt @@ -0,0 +1,94 @@ +Linear Technology LTC3676 8-output regulators + +Required properties: +- compatible: "lltc,ltc3676" +- reg: I2C slave address + +Required child node: +- regulators: Contains eight regulator child nodes sw1, sw2, sw3, sw4, + ldo1, ldo2, ldo3, and ldo4, specifying the initialization data as + documented in Documentation/devicetree/bindings/regulator/regulator.txt. + +Each regulator is defined using the standard binding for regulators. The +nodes for sw1, sw2, sw3, sw4, ldo1, ldo2 and ldo4 additionally need to specify +the resistor values of their external feedback voltage dividers: + +Required properties (not on ldo3): +- lltc,fb-voltage-divider: An array of two integers containing the resistor + values R1 and R2 of the feedback voltage divider in ohms. + +Regulators sw1, sw2, sw3, sw4 can regulate the feedback reference from: +412.5mV to 800mV in 12.5 mV steps. The output voltage thus ranges between +0.4125 * (1 + R1/R2) V and 0.8 * (1 + R1/R2) V. + +Regulators ldo1, ldo2, and ldo4 have a fixed 0.725 V reference and thus output +0.725 * (1 + R1/R2) V. The ldo3 regulator is fixed to 1.8 V. The ldo1 standby +regulator can not be disabled and thus should have the regulator-always-on +property set. + +Example: + + ltc3676: pmic@3c { + compatible = "lltc,ltc3676"; + reg = <0x3c>; + + regulators { + sw1_reg: sw1 { + regulator-min-microvolt = <674400>; + regulator-max-microvolt = <1308000>; + lltc,fb-voltage-divider = <127000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <1033310>; + regulator-max-microvolt = <200400>; + lltc,fb-voltage-divider = <301000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3_reg: sw3 { + regulator-min-microvolt = <674400>; + regulator-max-microvolt = <130800>; + lltc,fb-voltage-divider = <127000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <868310>; + regulator-max-microvolt = <168400>; + lltc,fb-voltage-divider = <221000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo2_reg: ldo2 { + regulator-min-microvolt = <2490375>; + regulator-max-microvolt = <2490375>; + lltc,fb-voltage-divider = <487000 200000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo3_reg: ldo3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + + ldo4_reg: ldo4 { + regulator-min-microvolt = <3023250>; + regulator-max-microvolt = <3023250>; + lltc,fb-voltage-divider = <634000 200000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/regulator/pv88080.txt b/Documentation/devicetree/bindings/regulator/pv88080.txt index 38a614210dcb..e6e4b9c82d89 100644 --- a/Documentation/devicetree/bindings/regulator/pv88080.txt +++ b/Documentation/devicetree/bindings/regulator/pv88080.txt @@ -1,22 +1,28 @@ * Powerventure Semiconductor PV88080 Voltage Regulator Required properties: -- compatible: "pvs,pv88080". -- reg: I2C slave address, usually 0x49. +- compatible: Must be one of the following, depending on the + silicon version: + - "pvs,pv88080" (DEPRECATED) + + - "pvs,pv88080-aa" for PV88080 AA or AB silicon + - "pvs,pv88080-ba" for PV88080 BA or BB silicon + NOTE: The use of the compatibles with no silicon version is deprecated. +- reg: I2C slave address, usually 0x49 - interrupts: the interrupt outputs of the controller - regulators: A node that houses a sub-node for each regulator within the device. Each sub-node is identified using the node's name, with valid values listed below. The content of each sub-node is defined by the standard binding for regulators; see regulator.txt. - BUCK1, BUCK2, and BUCK3. + BUCK1, BUCK2, BUCK3 and HVBUCK. Optional properties: - Any optional property defined in regulator.txt -Example +Example: pmic: pv88080@49 { - compatible = "pvs,pv88080"; + compatible = "pvs,pv88080-ba"; reg = <0x49>; interrupt-parent = <&gpio>; interrupts = <24 24>; @@ -45,5 +51,12 @@ Example regulator-min-microamp = <1496000>; regulator-max-microamp = <4189000>; }; + + HVBUCK { + regulator-name = "hvbuck"; + regulator-min-microvolt = < 5000>; + regulator-max-microvolt = <1275000>; + }; }; }; + diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index ecfc593cac15..6ab5aef619d9 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -13,7 +13,7 @@ Optional properties: - regulator-allow-bypass: allow the regulator to go into bypass mode - regulator-allow-set-load: allow the regulator performance level to be configured - <name>-supply: phandle to the parent supply/regulator node -- regulator-ramp-delay: ramp delay for regulator(in uV/uS) +- regulator-ramp-delay: ramp delay for regulator(in uV/us) For hardware which supports disabling ramp rate, it should be explicitly initialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay. - regulator-enable-ramp-delay: The time taken, in microseconds, for the supply diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 80b9dc363cd8..ba610adbdbff 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -219,6 +219,7 @@ static int tps65218_probe(struct i2c_client *client, struct tps65218 *tps; const struct of_device_id *match; int ret; + unsigned int chipid; match = of_match_device(of_tps65218_match_table, &client->dev); if (!match) { @@ -250,6 +251,14 @@ static int tps65218_probe(struct i2c_client *client, if (ret < 0) return ret; + ret = tps65218_reg_read(tps, TPS65218_REG_CHIPID, &chipid); + if (ret) { + dev_err(tps->dev, "Failed to read chipid: %d\n", ret); + return ret; + } + + tps->rev = chipid & TPS65218_CHIPID_REV_MASK; + ret = of_platform_populate(client->dev.of_node, NULL, NULL, &client->dev); if (ret < 0) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6c88e31c01f7..4c2631a2bb3c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -353,6 +353,14 @@ config REGULATOR_LTC3589 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 @@ -820,7 +828,7 @@ config REGULATOR_TPS65912 This driver supports TPS65912 voltage regulator chip. config REGULATOR_TPS80031 - tristate "TI TPS80031/TPS80032 power regualtor driver" + tristate "TI TPS80031/TPS80032 power regulator driver" depends on MFD_TPS80031 help TPS80031/ TPS80032 Fully Integrated Power Management with Power diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index f3da9eea9ab6..2142a5d3fc08 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -47,6 +47,7 @@ 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 diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index db320e8fa865..67426c0477d3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -679,24 +679,6 @@ static int drms_uA_update(struct regulator_dev *rdev) !rdev->desc->ops->set_load) return -EINVAL; - /* 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; - } - /* calc total requested load */ list_for_each_entry(sibling, &rdev->consumer_list, list) current_uA += sibling->uA_load; @@ -709,6 +691,24 @@ static int drms_uA_update(struct regulator_dev *rdev) 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); @@ -2743,6 +2743,24 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev, 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; + + if (ramp_delay == 0) { + rdev_warn(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) { @@ -2751,6 +2769,8 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, 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); @@ -2762,29 +2782,28 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, * info to call set_voltage_time_sel(). */ if (_regulator_is_enabled(rdev) && - rdev->desc->ops->set_voltage_time_sel && - rdev->desc->ops->get_voltage_sel) { - old_selector = rdev->desc->ops->get_voltage_sel(rdev); + ops->set_voltage_time_sel && ops->get_voltage_sel) { + old_selector = ops->get_voltage_sel(rdev); if (old_selector < 0) return old_selector; } - if (rdev->desc->ops->set_voltage) { + if (ops->set_voltage) { ret = _regulator_call_set_voltage(rdev, min_uV, max_uV, &selector); if (ret >= 0) { - if (rdev->desc->ops->list_voltage) - best_val = rdev->desc->ops->list_voltage(rdev, - selector); + if (ops->list_voltage) + best_val = ops->list_voltage(rdev, + selector); else best_val = _regulator_get_voltage(rdev); } - } else if (rdev->desc->ops->set_voltage_sel) { + } else if (ops->set_voltage_sel) { ret = regulator_map_voltage(rdev, min_uV, max_uV); if (ret >= 0) { - best_val = rdev->desc->ops->list_voltage(rdev, ret); + best_val = ops->list_voltage(rdev, ret); if (min_uV <= best_val && max_uV >= best_val) { selector = ret; if (old_selector == selector) @@ -2800,34 +2819,50 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, ret = -EINVAL; } - /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0 - && old_selector != selector) { + if (ret) + goto out; - delay = rdev->desc->ops->set_voltage_time_sel(rdev, - old_selector, selector); - if (delay < 0) { - rdev_warn(rdev, "set_voltage_time_sel() failed: %d\n", - delay); - delay = 0; + 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); } + } - /* Insert any necessary delays */ - if (delay >= 1000) { - mdelay(delay / 1000); - udelay(delay % 1000); - } else if (delay) { - udelay(delay); - } + if (delay < 0) { + rdev_warn(rdev, "failed to get delay: %d\n", delay); + delay = 0; } - if (ret == 0 && best_val >= 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; @@ -2998,9 +3033,13 @@ int regulator_set_voltage_time(struct regulator *regulator, 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 || !ops->set_voltage_time_sel - || !rdev->desc->n_voltages) + if (!ops->list_voltage || !rdev->desc->n_voltages) return -EINVAL; for (i = 0; i < rdev->desc->n_voltages; i++) { @@ -3039,19 +3078,8 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { - unsigned int ramp_delay = 0; int old_volt, new_volt; - if (rdev->constraints->ramp_delay) - ramp_delay = rdev->constraints->ramp_delay; - else if (rdev->desc->ramp_delay) - ramp_delay = rdev->desc->ramp_delay; - - if (ramp_delay == 0) { - rdev_warn(rdev, "ramp_delay not set\n"); - return 0; - } - /* sanity check */ if (!rdev->desc->ops->list_voltage) return -EINVAL; @@ -3059,7 +3087,11 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, old_volt = rdev->desc->ops->list_voltage(rdev, old_selector); new_volt = rdev->desc->ops->list_voltage(rdev, new_selector); - return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); + 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); @@ -3483,10 +3515,8 @@ int regulator_bulk_get(struct device *dev, int num_consumers, consumers[i].consumer = NULL; for (i = 0; i < num_consumers; i++) { - consumers[i].consumer = _regulator_get(dev, - consumers[i].supply, - false, - !consumers[i].optional); + 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", diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index 3963dfad766c..8976141c1438 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -75,24 +75,6 @@ static struct ux500_regulator_debug { u8 *state_after_suspend; } rdebug; -void ux500_regulator_suspend_debug(void) -{ - int i; - - for (i = 0; i < rdebug.num_regulators; i++) - rdebug.state_before_suspend[i] = - rdebug.regulator_array[i].is_enabled; -} - -void ux500_regulator_resume_debug(void) -{ - int i; - - for (i = 0; i < rdebug.num_regulators; i++) - rdebug.state_after_suspend[i] = - rdebug.regulator_array[i].is_enabled; -} - static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) { /* print power state count */ diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 6ad8ab4c578d..6ec1d400adae 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -164,11 +164,8 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, consumers[i].consumer = NULL; for (i = 0; i < num_consumers; i++) { - consumers[i].consumer = _devm_regulator_get(dev, - consumers[i].supply, - consumers[i].optional ? - OPTIONAL_GET : - NORMAL_GET); + consumers[i].consumer = devm_regulator_get(dev, + consumers[i].supply); if (IS_ERR(consumers[i].consumer)) { ret = PTR_ERR(consumers[i].consumer); dev_err(dev, "Failed to get supply '%s': %d\n", diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c index 42dc5fb8c899..62c5f5445d44 100644 --- a/drivers/regulator/hi6421-regulator.c +++ b/drivers/regulator/hi6421-regulator.c @@ -477,7 +477,8 @@ static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev, return 0; } -unsigned int hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev, +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); diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c new file mode 100644 index 000000000000..e2b476ca2b4d --- /dev/null +++ b/drivers/regulator/ltc3676.c @@ -0,0 +1,420 @@ +/* + * 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_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 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 = <c3676->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 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 = regulator_set_voltage_sel_regmap, + .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 struct regulator_ops ltc3676_fixed_standby_regulator_ops = { +}; + +/* LDO2, LDO3 fixed (LDO2 has external scalar via R1/R2 feedback res) */ +static 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 = <c3676_ ## _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:< |
