From 23a34f9d03a5d40a6234855bc069da370708cc9e Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 10 Aug 2016 17:53:55 +0530 Subject: regulator: tps65218: do not disable DCDC3 during poweroff on broken PMICs Some versions of tps65218 do not seem to support poweroff modes properly if DCDC3 regulator is shut-down. Thus, keep it enabled even during poweroff if the version info matches the broken silicon revision. Signed-off-by: Tero Kristo Signed-off-by: Dave Gerlach Signed-off-by: Keerthy Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/tps65218-regulator.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index d1e631d64a20..eb0f5b13841a 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -180,6 +180,14 @@ static int tps65218_pmic_set_suspend_disable(struct regulator_dev *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->info[rid]->strobe) { if (rid == TPS65218_DCDC_3) tps->info[rid]->strobe = 3; -- cgit v1.2.3 From 556ae220ac64b6564be8d76d855e26b65fcf75bf Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 15 Aug 2016 10:30:31 +0200 Subject: regulator: rk808: Delete owner assignment The field "owner" is set by core. Thus delete an extra initialisation. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Markus Elfring Signed-off-by: Mark Brown --- drivers/regulator/rk808-regulator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 40d07ba036e7..a6767494d39b 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -533,8 +533,7 @@ static int rk808_regulator_probe(struct platform_device *pdev) static struct platform_driver rk808_regulator_driver = { .probe = rk808_regulator_probe, .driver = { - .name = "rk808-regulator", - .owner = THIS_MODULE, + .name = "rk808-regulator" }, }; -- cgit v1.2.3 From 57995a4860542ea2089558c84f739e12d5ca1059 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 14 Sep 2016 09:52:05 -0700 Subject: regulator: core: Use local ops variable in _regulator_do_set_voltage() Signed-off-by: Matthias Kaehlcke Signed-off-by: Mark Brown --- drivers/regulator/core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index db320e8fa865..b059e8334567 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2751,6 +2751,7 @@ 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; trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); @@ -2762,29 +2763,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) @@ -2804,7 +2804,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0 && old_selector != selector) { - delay = rdev->desc->ops->set_voltage_time_sel(rdev, + delay = ops->set_voltage_time_sel(rdev, old_selector, selector); if (delay < 0) { rdev_warn(rdev, "set_voltage_time_sel() failed: %d\n", -- cgit v1.2.3 From 31dfe686ed0ba5a796bcfc5a6745e77ddb5daa4e Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 14 Sep 2016 09:52:06 -0700 Subject: regulator: core: Simplify error flow in _regulator_do_set_voltage() If the voltage can not be set jump to the end of the function. This avoids having to check for an error multiple times and eliminates one level of nesting in a follow-up change. Signed-off-by: Matthias Kaehlcke Signed-off-by: Mark Brown --- drivers/regulator/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b059e8334567..b0076ccf896b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2800,8 +2800,11 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, ret = -EINVAL; } + if (ret) + goto out; + /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0 + if (!rdev->constraints->ramp_disable && old_selector >= 0 && old_selector != selector) { delay = ops->set_voltage_time_sel(rdev, @@ -2821,13 +2824,14 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, } } - if (ret == 0 && best_val >= 0) { + 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; -- cgit v1.2.3 From d89564efe79419a093e966a959bf5ba2c94e693f Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 14 Sep 2016 09:52:07 -0700 Subject: regulator: core: Don't skip set_voltage_time when ramp delay disabled The current code assumes that only the ramp_delay is used to determine the time needed for the voltage to stabilize. This may be true for the calculation done by regulator_set_voltage_time_sel(), however regulators can implement their own set_voltage_time_sel() op which would be skipped if no ramp delay is specified. Remove the check in _regulator_do_set_voltage(), the functions calculating the ramp delay return 0 anyway when the ramp delay is not configured. Signed-off-by: Matthias Kaehlcke Signed-off-by: Mark Brown --- drivers/regulator/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b0076ccf896b..df3028b2a8e9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2804,9 +2804,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, goto out; /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (!rdev->constraints->ramp_disable && old_selector >= 0 - && old_selector != selector) { - + if (!old_selector >= 0 && old_selector != selector) { delay = ops->set_voltage_time_sel(rdev, old_selector, selector); if (delay < 0) { @@ -3051,10 +3049,8 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, else if (rdev->desc->ramp_delay) ramp_delay = rdev->desc->ramp_delay; - if (ramp_delay == 0) { - rdev_warn(rdev, "ramp_delay not set\n"); + if (ramp_delay == 0) return 0; - } /* sanity check */ if (!rdev->desc->ops->list_voltage) -- cgit v1.2.3 From 73e705bf81ceb84b39ef9cf6ffb8d12ca0c58a23 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 14 Sep 2016 09:52:08 -0700 Subject: regulator: core: Add set_voltage_time op The new op is analogous to set_voltage_time_sel. It can be used by regulators which don't have a table of discrete voltages. The function returns the time for the regulator output voltage to stabilize after being set to a new value, in microseconds. If the op is not set a default implementation is used to calculate the delay. This change also removes the ramp_delay calculation in the PWM regulator, since the driver now uses the core code for the calculation of the delay. Signed-off-by: Matthias Kaehlcke Signed-off-by: Mark Brown --- drivers/regulator/core.c | 86 +++++++++++++++++++++++++++------------ drivers/regulator/pwm-regulator.c | 10 ----- include/linux/regulator/driver.h | 10 ++++- 3 files changed, 67 insertions(+), 39 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index df3028b2a8e9..c52fc0ce2693 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -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) { @@ -2752,6 +2770,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, 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); @@ -2803,23 +2822,37 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, if (ret) goto out; - /* 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); - 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; + } + + /* Insert any necessary delays */ + if (delay >= 1000) { + mdelay(delay / 1000); + udelay(delay % 1000); + } else if (delay) { + udelay(delay); } if (best_val >= 0) { @@ -3000,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++) { @@ -3041,17 +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) - 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); diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index c24524242da2..1b88e0e15a70 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -194,12 +193,10 @@ static int pwm_regulator_set_voltage(struct regulator_dev *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; - unsigned int ramp_delay = rdev->constraints->ramp_delay; 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; - int old_uV = pwm_regulator_get_voltage(rdev); unsigned int diff_duty; unsigned int dutycycle; int ret; @@ -233,13 +230,6 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, return ret; } - if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev)) - return 0; - - /* Ramp delay is in uV/uS. Adjust to uS and delay */ - ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay); - usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10)); - return 0; } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index fcfa40a6692c..37b532410528 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -113,10 +113,14 @@ struct regulator_linear_range { * stabilise after being enabled, in microseconds. * @set_ramp_delay: Set the ramp delay for the regulator. The driver should * select ramp delay equal to or less than(closest) ramp_delay. + * @set_voltage_time: Time taken for the regulator voltage output voltage + * to stabilise after being set to a new value, in microseconds. + * The function receives the from and to voltage as input, it + * should return the worst case. * @set_voltage_time_sel: Time taken for the regulator voltage output voltage * to stabilise after being set to a new value, in microseconds. - * The function provides the from and to voltage selector, the - * function should return the worst case. + * The function receives the from and to voltage selector as + * input, it should return the worst case. * @set_soft_start: Enable soft start for the regulator. * * @set_suspend_voltage: Set the voltage for the regulator when the system @@ -168,6 +172,8 @@ struct regulator_ops { /* Time taken to enable or set voltage on the regulator */ int (*enable_time) (struct regulator_dev *); int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay); + int (*set_voltage_time) (struct regulator_dev *, int old_uV, + int new_uV); int (*set_voltage_time_sel) (struct regulator_dev *, unsigned int old_selector, unsigned int new_selector); -- cgit v1.2.3 From 5ff00f6aa84b7f4203f525fc6c4a4291e32c314d Mon Sep 17 00:00:00 2001 From: Eric Jeong Date: Mon, 26 Sep 2016 16:33:27 +0900 Subject: regulator: pv88080: Update regulator for PV88080 BB silicon support Three files are modified, the driver, header file and the binding document. Updates for the regulator source file include and .of_match_table entry and node match checking in the probe() function for a compatible pv88080 silicon type. A new "HVBUCK" is added in source file and added regsiter definition in header file for pv88080 bb silicion. The binding documentation changes have been made to reflect these updates. Signed-off-by: Eric Jeong Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/pv88080.txt | 23 +- drivers/regulator/pv88080-regulator.c | 263 ++++++++++++++++++--- drivers/regulator/pv88080-regulator.h | 114 +++++---- 3 files changed, 322 insertions(+), 78 deletions(-) (limited to 'drivers/regulator') 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/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 #include #include +#include #include #include #include @@ -26,7 +27,7 @@ #include #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, + }, + /* 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->conf, &data); + ret = regmap_read(rdev->regmap, info->mode_reg, &data); if (ret < 0) return ret; @@ -136,7 +268,7 @@ static int pv88080_buck_set_mode(struct regulator_dev *rdev, return -EINVAL; } - return regmap_update_bits(rdev->regmap, info->conf, + return regmap_update_bits(rdev->regmap, info->mode_reg, PV88080_BUCK1_MODE_MASK, val); } @@ -151,7 +283,7 @@ static int pv88080_set_current_limit(struct regulator_dev *rdev, int min, if (min <= info->current_limits[i] && max >= info->current_limits[i]) { return regmap_update_bits(rdev->regmap, - info->conf, + info->limit_reg, info->limit_mask, i << PV88080_BUCK1_ILIM_SHIFT); } @@ -166,7 +298,7 @@ static int pv88080_get_current_limit(struct regulator_dev *rdev) unsigned int data; int ret; - ret = regmap_read(rdev->regmap, info->conf, &data); + ret = regmap_read(rdev->regmap, info->limit_reg, &data); if (ret < 0) return ret; @@ -187,6 +319,15 @@ static struct regulator_ops pv88080_buck_ops = { .get_current_limit = pv88080_get_current_limit, }; +static 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 = {\ @@ -200,17 +341,25 @@ static struct regulator_ops pv88080_buck_ops = { .min_uV = min, \ .uV_step = step, \ .n_voltages = ((max) - (min))/(step) + 1, \ - .enable_reg = PV88080_REG_##regl_name##_CONF0, \ - .enable_mask = PV88080_##regl_name##_EN, \ - .vsel_reg = PV88080_REG_##regl_name##_CONF0, \ - .vsel_mask = PV88080_V##regl_name##_MASK, \ },\ .current_limits = limits_array, \ .n_current_limits = ARRAY_SIZE(limits_array), \ - .limit_mask = PV88080_##regl_name##_ILIM_MASK, \ - .conf = PV88080_REG_##regl_name##_CONF1, \ - .conf2 = PV88080_REG_##regl_name##_CONF2, \ - .conf5 = PV88080_REG_##regl_name##_CONF5, \ +} + +#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[] = { @@ -220,6 +369,7 @@ static struct pv88080_regulator pv88080_regulator_info[] = { 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) @@ -280,6 +430,8 @@ static int pv88080_i2c_probe(struct i2c_client *i2c, { 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; @@ -297,6 +449,17 @@ static int pv88080_i2c_probe(struct i2c_client *i2c, 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) { @@ -336,31 +499,58 @@ static int pv88080_i2c_probe(struct i2c_client *i2c, "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; - for (i = 0; i < PV88080_MAX_REGULATORS; i++) { + /* 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); + 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); + pv88080_regulator_info[i].conf5, &conf5); if (ret < 0) return ret; - conf5 = ((conf5 >> PV88080_BUCK_VRANGE_GAIN_SHIFT) & PV88080_BUCK_VRANGE_GAIN_MASK); @@ -383,23 +573,38 @@ static int pv88080_i2c_probe(struct i2c_client *i2c, } } + 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", 0}, + { "pv88080", TYPE_PV88080_AA }, + { "pv88080-aa", TYPE_PV88080_AA }, + { "pv88080-ba", TYPE_PV88080_BA }, {}, }; MODULE_DEVICE_TABLE(i2c, pv88080_i2c_id); -#ifdef CONFIG_OF -static const struct of_device_id pv88080_dt_ids[] = { - { .compatible = "pvs,pv88080", .data = &pv88080_i2c_id[0] }, - {}, -}; -MODULE_DEVICE_TABLE(of, pv88080_dt_ids); -#endif - static struct i2c_driver pv88080_regulator_driver = { .driver = { .name = "pv88080", diff --git a/drivers/regulator/pv88080-regulator.h b/drivers/regulator/pv88080-regulator.h index 5e9afde606f4..ae25ff360e3d 100644 --- a/drivers/regulator/pv88080-regulator.h +++ b/drivers/regulator/pv88080-regulator.h @@ -17,55 +17,75 @@ #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 */ -#define PV88080_REG_BUCK1_CONF0 0x27 -#define PV88080_REG_BUCK1_CONF1 0x28 -#define PV88080_REG_BUCK1_CONF2 0x59 -#define PV88080_REG_BUCK1_CONF5 0x5c -#define PV88080_REG_BUCK2_CONF0 0x29 -#define PV88080_REG_BUCK2_CONF1 0x2a -#define PV88080_REG_BUCK2_CONF2 0x61 -#define PV88080_REG_BUCK2_CONF5 0x64 -#define PV88080_REG_BUCK3_CONF0 0x2b -#define PV88080_REG_BUCK3_CONF1 0x2c -#define PV88080_REG_BUCK3_CONF2 0x69 -#define PV88080_REG_BUCK3_CONF5 0x6c +#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 +#define PV88080_E_OVER_TEMP 0x02 /* PV88080_REG_MASK_A (addr=0x09) */ #define PV88080_M_VDD_FLT 0x01 -#define PV88080_M_OVER_TEMP 0x02 +#define PV88080_M_OVER_TEMP 0x02 -/* PV88080_REG_BUCK1_CONF0 (addr=0x27) */ +/* PV88080_REG_BUCK1_CONF0 (addr=0x27|0x2A) */ #define PV88080_BUCK1_EN 0x80 -#define PV88080_VBUCK1_MASK 0x7F -/* PV88080_REG_BUCK2_CONF0 (addr=0x29) */ +#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) */ +#define PV88080_VBUCK2_MASK 0x7F + +/* PV88080_REG_BUCK3_CONF0 (addr=0x2B|0x30) */ #define PV88080_BUCK3_EN 0x80 -#define PV88080_VBUCK3_MASK 0x7F +#define PV88080_VBUCK3_MASK 0x7F -/* PV88080_REG_BUCK1_CONF1 (addr=0x28) */ -#define PV88080_BUCK1_ILIM_SHIFT 2 +/* 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) */ -#define PV88080_BUCK2_ILIM_SHIFT 2 +/* 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) */ -#define PV88080_BUCK3_ILIM_SHIFT 2 +/* 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 @@ -73,20 +93,26 @@ #define PV88080_BUCK_MODE_AUTO 0x01 #define PV88080_BUCK_MODE_SYNC 0x02 -/* PV88080_REG_BUCK2_CONF2 (addr=0x61) */ -/* PV88080_REG_BUCK3_CONF2 (addr=0x69) */ -#define PV88080_BUCK_VDAC_RANGE_SHIFT 7 -#define PV88080_BUCK_VDAC_RANGE_MASK 0x01 +/* 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 +#define PV88080_BUCK_VDAC_RANGE_1 0x00 +#define PV88080_BUCK_VDAC_RANGE_2 0x01 -/* PV88080_REG_BUCK2_CONF5 (addr=0x64) */ -/* PV88080_REG_BUCK3_CONF5 (addr=0x6c) */ -#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0 -#define PV88080_BUCK_VRANGE_GAIN_MASK 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 +#define PV88080_BUCK_VRANGE_GAIN_1 0x00 +#define PV88080_BUCK_VRANGE_GAIN_2 0x01 #endif /* __PV88080_REGISTERS_H__ */ -- cgit v1.2.3