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 /drivers | |
| 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
Diffstat (limited to 'drivers')
| -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 |
14 files changed, 854 insertions, 172 deletions
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: + 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, <c3676_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 = <c3676->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 struct i2c_driver ltc3676_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .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/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c index 81950bdb1cc4..954a20eeb26f 100644 --- a/drivers/regulator/pv88080-regulator.c +++ b/drivers/regulator/pv88080-regulator.c @@ -16,6 +16,7 @@ #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> @@ -26,7 +27,7 @@ #include <linux/regulator/of_regulator.h> #include "pv88080-regulator.h" -#define PV88080_MAX_REGULATORS 3 +#define PV88080_MAX_REGULATORS 4 /* PV88080 REGULATOR IDs */ enum { @@ -34,6 +35,12 @@ enum { PV88080_ID_BUCK1, PV88080_ID_BUCK2, PV88080_ID_BUCK3, + PV88080_ID_HVBUCK, +}; + +enum pv88080_types { + TYPE_PV88080_AA, + TYPE_PV88080_BA, }; struct pv88080_regulator { @@ -42,7 +49,8 @@ struct pv88080_regulator { unsigned int n_current_limits; const int *current_limits; unsigned int limit_mask; - unsigned int conf; + unsigned int mode_reg; + unsigned int limit_reg; unsigned int conf2; unsigned int conf5; }; @@ -51,6 +59,8 @@ 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 { @@ -59,6 +69,30 @@ struct pv88080_buck_voltage { 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, @@ -89,13 +123,111 @@ static const struct pv88080_buck_voltage pv88080_buck_vol[2] = { }, }; +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, |
