diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-26 01:38:31 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-26 01:38:31 +0300 |
commit | 79f20778fb228ae372cd7602745382fd4543ef31 (patch) | |
tree | 63a929e1ce44701f065d254a1502f2807c8b1cdc | |
parent | d923fd6dc133ee8d8fe800e4e4beb9175368b21b (diff) | |
parent | c3b5725965228014215de553eed3492dbd80a4bd (diff) | |
download | linux-79f20778fb228ae372cd7602745382fd4543ef31.tar.xz |
Merge tag 'regulator-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates from Mark Brown:
"This has been a very busy release for the core, some fixes, one large
new feature and a big bit of refactoring to update the GPIO API:
- Support for coupled regulators from Dmitry Osipenko based on a
prior attempt by Maciej Purski, allowing us to handle situations
where the voltages on two regulators can't be too far apart from
each other.
- Conversion of the GPIO support in both drivers and the core to use
GPIO descriptors rather than numbers, part of the overall project
to remove GPIO numbers.
- Support for standby mode suspend states from Andrei Stefanescu.
- New drivers for Allwinner AXP209, Cirrus Logic Lochnagar and
Microchip MPC16502"
* tag 'regulator-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (90 commits)
regulator: tps65910: fix a missing check of return value
regulator: mcp16502: Select REGMAP_I2C to fix build error
regulator: convert to DEFINE_SHOW_ATTRIBUTE
regulator: mcp16502: Fix missing n_voltages setting
regulator: mcp16502: Use #ifdef CONFIG_PM_SLEEP around mcp16502_suspend/resume_noirq
regulator: mcp16502: code cleanup
regulator: act8945a-regulator: make symbol act8945a_pm static
drivers/regulator: fix a missing check of return value
regulator: act8945a-regulator: fix 'defined but not used' compiler warning
regulator: axp20x: fix set_ramp_delay for AXP209/dcdc2
regulator: mcp16502: add support for suspend
mfd: axp20x: use explicit bit defines
mfd: axp20x: Clean up included headers
regulator: dts: enable soft-start and ramp delay for the OLinuXino Lime2
dt-bindings: mfd: axp20x: Add software based soft_start for AXP209 LDO3
regulator: axp20x: add software based soft_start for AXP209 LDO3
dt-bindings: mfd: axp20x: add support for regulator-ramp-delay for AXP209
regulator: axp20x: add support for set_ramp_delay for AXP209
mfd: axp20x: name voltage ramping define properly
regulator: mcp16502: add regulator driver for MCP16502
...
59 files changed, 3141 insertions, 635 deletions
diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 188f0373d441..2af4ff95d6bc 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -32,6 +32,15 @@ Required properties: - interrupt-controller: The PMIC has its own internal IRQs - #interrupt-cells: Should be set to 1 +Supported common regulator properties, see ../regulator/regulator.txt for +more information: +- regulator-ramp-delay: sets the ramp up delay in uV/us + AXP20x/DCDC2: 1600, 800 + AXP20x/LDO3: 1600, 800 +- regulator-soft-start: enable the output at the lowest possible voltage and + only then set the desired voltage + AXP20x/LDO3: software-based implementation + Optional properties: - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz AXP152/20X: range: 750-1875, Default: 1.5 MHz diff --git a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt b/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt index ac955dea00d1..4017527619ab 100644 --- a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt @@ -15,11 +15,17 @@ Optional input supply properties: - inl67-supply: The input supply for REG_LDO3 and REG_LDO4 Any standard regulator properties can be used to configure the single regulator. +regulator-initial-mode, regulator-allowed-modes and regulator-mode could be +specified using mode values from dt-bindings/regulator/active-semi,8945a-regulator.h +file. The valid names for regulators are: REG_DCDC1, REG_DCDC2, REG_DCDC3, REG_LDO1, REG_LDO2, REG_LDO3, REG_LDO4. Example: + +#include <dt-bindings/regulator/active-semi,8945a-regulator.h> + pmic@5b { compatible = "active-semi,act8945a"; reg = <0x5b>; @@ -32,6 +38,18 @@ Example: regulator-min-microvolt = <1350000>; regulator-max-microvolt = <1350000>; regulator-always-on; + + regulator-allowed-modes = <ACT8945A_REGULATOR_MODE_FIXED>, + <ACT8945A_REGULATOR_MODE_LOWPOWER>; + regulator-initial-mode = <ACT8945A_REGULATOR_MODE_FIXED>; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-min-microvolt=<1400000>; + regulator-suspend-max-microvolt=<1400000>; + regulator-changeable-in-suspend; + regulator-mode=<ACT8945A_REGULATOR_MODE_LOWPOWER>; + }; }; vdd_1v2_reg: REG_DCDC2 { @@ -39,6 +57,14 @@ Example: regulator-min-microvolt = <1100000>; regulator-max-microvolt = <1300000>; regulator-always-on; + + regulator-allowed-modes = <ACT8945A_REGULATOR_MODE_FIXED>, + <ACT8945A_REGULATOR_MODE_LOWPOWER>; + regulator-initial-mode = <ACT8945A_REGULATOR_MODE_FIXED>; + + regulator-state-mem { + regulator-off-in-suspend; + }; }; vdd_3v3_reg: REG_DCDC3 { @@ -53,6 +79,14 @@ Example: regulator-min-microvolt = <2500000>; regulator-max-microvolt = <2500000>; regulator-always-on; + + regulator-allowed-modes = <ACT8945A_REGULATOR_MODE_NORMAL>, + <ACT8945A_REGULATOR_MODE_LOWPOWER>; + regulator-initial-mode = <ACT8945A_REGULATOR_MODE_NORMAL>; + + regulator-state-mem { + regulator-off-in-suspend; + }; }; vdd_3v3_lp_reg: REG_LDO2 { diff --git a/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt new file mode 100644 index 000000000000..91974e6ee251 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt @@ -0,0 +1,82 @@ +Cirrus Logic Lochnagar Audio Development Board + +Lochnagar is an evaluation and development board for Cirrus Logic +Smart CODEC and Amp devices. It allows the connection of most Cirrus +Logic devices on mini-cards, as well as allowing connection of +various application processor systems to provide a full evaluation +platform. Audio system topology, clocking and power can all be +controlled through the Lochnagar, allowing the device under test +to be used in a variety of possible use cases. + +This binding document describes the binding for the regulator portion +of the driver. + +Also see these documents for generic binding information: + [1] Regulator: ../regulator/regulator.txt + +This binding must be part of the Lochnagar MFD binding: + [2] ../mfd/cirrus,lochnagar.txt + +Optional sub-nodes: + + - VDDCORE : Initialisation data for the VDDCORE regulator, which + supplies the CODECs digital core if it has no build regulator for that + purpose. + Required Properties: + - compatible : One of the following strings: + "cirrus,lochnagar2-vddcore" + - SYSVDD-supply: Primary power supply for the Lochnagar. + + - MICVDD : Initialisation data for the MICVDD regulator, which + supplies the CODECs MICVDD. + Required Properties: + - compatible : One of the following strings: + "cirrus,lochnagar2-micvdd" + - SYSVDD-supply: Primary power supply for the Lochnagar. + + - MIC1VDD, MIC2VDD : Initialisation data for the MICxVDD supplies. + Required Properties: + - compatible : One of the following strings: + "cirrus,lochnagar2-mic1vdd", "cirrus,lochnagar2-mic2vdd" + Optional Properties: + - cirrus,micbias-input : A property selecting which of the CODEC + minicard micbias outputs should be used, valid values are 1 - 4. + - MICBIAS1-supply, MICBIAS2-supply: Regulator supplies for the + MICxVDD outputs, supplying the digital microphones, normally + supplied from the attached CODEC. + + - VDD1V8 : Recommended fixed regulator for the VDD1V8 regulator, which supplies the + CODECs analog and 1.8V digital supplies. + Required Properties: + - compatible : Should be set to "regulator-fixed" + - regulator-min-microvolt : Should be set to 1.8V + - regulator-max-microvolt : Should be set to 1.8V + - regulator-boot-on + - regulator-always-on + - vin-supply : Should be set to same supply as SYSVDD + +Example: + +lochnagar { + lochnagar-micvdd: MICVDD { + compatible = "cirrus,lochnagar2-micvdd"; + + SYSVDD-supply = <&wallvdd>; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + lochnagar-vdd1v8: VDD1V8 { + compatible = "regulator-fixed"; + + regulator-name = "VDD1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + + vin-supply = <&wallvdd>; + }; +}; + diff --git a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt new file mode 100644 index 000000000000..b8f843fa6092 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt @@ -0,0 +1,143 @@ +MCP16502 PMIC + +Required properties: +- compatible: "microchip,mcp16502" +- reg: I2C slave address +- lpm-gpios: GPIO for LPM pin. Note that this GPIO *must* remain high during + suspend-to-ram, keeping the PMIC into HIBERNATE mode. +- regulators: A node that houses a sub-node for each regulator within + the device. Each sub-node is identified using the node's + name. The content of each sub-node is defined by the + standard binding for regulators; see regulator.txt. + +Regualtors of MCP16502 PMIC: +1) VDD_IO - Buck (1.2 - 3.7 V) +2) VDD_DDR - Buck (0.6 - 1.85 V) +3) VDD_CORE - Buck (0.6 - 1.85 V) +4) VDD_OTHER - BUCK (0.6 - 1.85 V) +5) LDO1 - LDO (1.2 - 3.7 V) +6) LDO2 - LDO (1.2 - 3.7 V) + +Regulator modes: +2 - FPWM: higher precision, higher consumption +4 - AutoPFM: lower precision, lower consumption + +Each regulator is defined using the standard binding for regulators. + +Example: + +mcp16502@5b { + compatible = "microchip,mcp16502"; + reg = <0x5b>; + status = "okay"; + lpm-gpios = <&pioBU 7 GPIO_ACTIVE_HIGH>; + + regulators { + VDD_IO { + regulator-name = "VDD_IO"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + VDD_DDR { + regulator-name = "VDD_DDR"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1850000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + }; + + VDD_CORE { + regulator-name = "VDD_CORE"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1850000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + VDD_OTHER { + regulator-name = "VDD_OTHER"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1850000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + }; +}; diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index a7cd36877bfe..0a3f087d5844 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -33,13 +33,16 @@ Optional properties: decreases of any level. This is useful for regulators with exponential voltage changes. - regulator-soft-start: Enable soft start so that voltage ramps slowly +- regulator-state-standby sub-root node for Standby mode + : equivalent with standby Linux sleep state, which provides energy savings + with a relatively quick transition back time. - regulator-state-mem sub-root node for Suspend-to-RAM mode : suspend to memory, the device goes to sleep, but all data stored in memory, only some external interrupt can wake the device. - regulator-state-disk sub-root node for Suspend-to-DISK mode : suspend to disk, this state operates similarly to Suspend-to-RAM, but includes a final step of writing memory contents to disk. -- regulator-state-[mem/disk] node has following common properties: +- regulator-state-[mem/disk/standby] node has following common properties: - regulator-on-in-suspend: regulator should be on in suspend state. - regulator-off-in-suspend: regulator should be off in suspend state. - regulator-suspend-min-microvolt: minimum voltage may be set in @@ -76,8 +79,11 @@ Optional properties: - regulator-coupled-with: Regulators with which the regulator is coupled. The linkage is 2-way - all coupled regulators should be linked with each other. A regulator should not be coupled with its supplier. -- regulator-coupled-max-spread: Max spread between voltages of coupled regulators - in microvolts. +- regulator-coupled-max-spread: Array of maximum spread between voltages of + coupled regulators in microvolts, each value in the array relates to the + corresponding couple specified by the regulator-coupled-with property. +- regulator-max-step-microvolt: Maximum difference between current and target + voltages that can be changed safely in a single step. Deprecated properties: - regulator-compatible: If a regulator chip contains multiple diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 43681ca0837f..fc4cc24dfb97 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -254,6 +254,7 @@ GPIO devm_gpiod_get_index_optional() devm_gpiod_get_optional() devm_gpiod_put() + devm_gpiod_unhinge() devm_gpiochip_add_data() devm_gpiochip_remove() devm_gpio_request() diff --git a/MAINTAINERS b/MAINTAINERS index 7808b166fdb9..f0f0c23fce54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9891,6 +9891,13 @@ M: Ludovic Desroches <ludovic.desroches@microchip.com> S: Maintained F: drivers/mmc/host/atmel-mci.c +MICROCHIP MCP16502 PMIC DRIVER +M: Andrei Stefanescu <andrei.stefanescu@microchip.com> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt +F: drivers/regulator/mcp16502.c + MICROCHIP MCP3911 ADC DRIVER M: Marcus Folkesson <marcus.folkesson@gmail.com> M: Kent Gustavsson <kent@minoris.se> diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index b828677f331d..ffafe9720b35 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -245,6 +245,8 @@ regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-name = "vddio-csi0"; + regulator-soft-start; + regulator-ramp-delay = <1600>; }; ®_ldo4 { diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c index 5aa472892465..76c4855a03bc 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -194,8 +194,8 @@ static struct wm8994_pdata wm8994_pdata = { 0x3, /* IRQ out, active high, CMOS */ }, .ldo = { - { .enable = S3C64XX_GPN(6), .init_data = &wm8994_ldo1, }, - { .enable = S3C64XX_GPN(4), .init_data = &wm8994_ldo2, }, + { .init_data = &wm8994_ldo1, }, + { .init_data = &wm8994_ldo2, }, }, }; @@ -203,6 +203,18 @@ static const struct i2c_board_info wm1277_devs[] = { { I2C_BOARD_INFO("wm8958", 0x1a), /* WM8958 is the superset */ .platform_data = &wm8994_pdata, .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + .dev_name = "wm8958", + }, +}; + +static struct gpiod_lookup_table wm8994_gpiod_table = { + .dev_id = "i2c-wm8958", /* I2C device name */ + .table = { + GPIO_LOOKUP("GPION", 6, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPION", 4, + "wlf,ldo2ena", GPIO_ACTIVE_HIGH), + { }, }, }; @@ -381,6 +393,7 @@ static int wlf_gf_module_probe(struct i2c_client *i2c, gpiod_add_lookup_table(&wm5102_reva_gpiod_table); gpiod_add_lookup_table(&wm5102_gpiod_table); + gpiod_add_lookup_table(&wm8994_gpiod_table); if (i < ARRAY_SIZE(gf_mods)) { dev_info(&i2c->dev, "%s revision %d\n", diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 01959369360b..0acc2cc6e868 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -98,15 +98,28 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, struct gpio_desc **dr; struct gpio_desc *desc; + desc = gpiod_get_index(dev, con_id, idx, flags); + if (IS_ERR(desc)) + return desc; + + /* + * For non-exclusive GPIO descriptors, check if this descriptor is + * already under resource management by this device. + */ + if (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { + struct devres *dres; + + dres = devres_find(dev, devm_gpiod_release, + devm_gpiod_match, &desc); + if (dres) + return desc; + } + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); - if (!dr) + if (!dr) { + gpiod_put(desc); return ERR_PTR(-ENOMEM); - - desc = gpiod_get_index(dev, con_id, idx, flags); - if (IS_ERR(desc)) { - devres_free(dr); - return desc; } *dr = desc; @@ -140,15 +153,28 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, struct gpio_desc **dr; struct gpio_desc *desc; + desc = gpiod_get_from_of_node(node, propname, index, dflags, label); + if (IS_ERR(desc)) + return desc; + + /* + * For non-exclusive GPIO descriptors, check if this descriptor is + * already under resource management by this device. + */ + if (dflags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { + struct devres *dres; + + dres = devres_find(dev, devm_gpiod_release, + devm_gpiod_match, &desc); + if (dres) + return desc; + } + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); - if (!dr) + if (!dr) { + gpiod_put(desc); return ERR_PTR(-ENOMEM); - - desc = gpiod_get_from_of_node(node, propname, index, dflags, label); - if (IS_ERR(desc)) { - devres_free(dr); - return desc; } *dr = desc; @@ -321,6 +347,36 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) EXPORT_SYMBOL(devm_gpiod_put); /** + * devm_gpiod_unhinge - Remove resource management from a gpio descriptor + * @dev: GPIO consumer + * @desc: GPIO descriptor to remove resource management from + * + * Remove resource management from a GPIO descriptor. This is needed when + * you want to hand over lifecycle management of a descriptor to another + * mechanism. + */ + +void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc) +{ + int ret; + + if (IS_ERR_OR_NULL(desc)) + return; + ret = devres_destroy(dev, devm_gpiod_release, + devm_gpiod_match, &desc); + /* + * If the GPIO descriptor is requested as nonexclusive, we + * may call this function several times on the same descriptor + * so it is OK if devres_destroy() returns -ENOENT. + */ + if (ret == -ENOENT) + return; + /* Anything else we should warn about */ + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_gpiod_unhinge); + +/** * devm_gpiod_put_array - Resource-managed gpiod_put_array() * @dev: GPIO consumer * @descs: GPIO descriptor array to dispose of diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a2cbb474901c..985c09ce80fb 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4205,6 +4205,8 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, transitory = flags & OF_GPIO_TRANSITORY; ret = gpiod_request(desc, label); + if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) + return desc; if (ret) return ERR_PTR(ret); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 087d865286a0..bc57f0dc5953 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -201,12 +201,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap); -/* This is just passed between gpiolib and devres */ -struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, - const char *propname, int index, - enum gpiod_flags dflags, - const char *label); - extern struct spinlock gpio_lock; extern struct list_head gpio_devices; diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 0be511dd93d0..e1450a56fc07 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -16,20 +16,21 @@ * published by the Free Software Foundation. */ -#include <linux/err.h> +#include <linux/acpi.h> +#include <linux/bitops.h> #include <linux/delay.h> +#include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> +#include <linux/mfd/axp20x.h> +#include <linux/mfd/core.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#include <linux/mfd/axp20x.h> -#include <linux/mfd/core.h> -#include <linux/of_device.h> -#include <linux/acpi.h> -#define AXP20X_OFF 0x80 +#define AXP20X_OFF BIT(7) #define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0 #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 22bd6525e09c..04a177efd245 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -21,7 +21,6 @@ #include <linux/mfd/core.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -306,14 +305,6 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) pdata->csnaddr_pd = of_property_read_bool(np, "wlf,csnaddr-pd"); - pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0); - if (pdata->ldo[0].enable < 0) - pdata->ldo[0].enable = 0; - - pdata->ldo[1].enable = of_get_named_gpio(np, "wlf,ldo2ena", 0); - if (pdata->ldo[1].enable < 0) - pdata->ldo[1].enable = 0; - return 0; } #else diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index fd86446e499b..28f55248eb90 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -328,7 +328,7 @@ static int pm8607_regulator_dt_init(struct platform_device *pdev, return -ENODEV; } for_each_child_of_node(nproot, np) { - if (!of_node_cmp(np->name, info->desc.name)) { + if (of_node_name_eq(np, info->desc.name)) { config->init_data = of_get_regulator_init_data(&pdev->dev, np, &info->desc); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 926cee0d0b5f..ee60a222f5eb 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -567,6 +567,16 @@ config REGULATOR_MC13892 Say y here to support the regulators found on the Freescale MC13892 PMIC. +config REGULATOR_MCP16502 + tristate "Microchip MCP16502 PMIC" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support the MCP16502 PMIC. This driver supports + basic operations (get/set voltage, get/set operating mode) + through the regulator interface. In addition it enables + suspend-to-ram/standby transition. + config REGULATOR_MT6311 tristate "MediaTek MT6311 PMIC" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 72488ef11b8a..b12e1c9b2118 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o +obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index 43fda8b4455a..603db77723b6 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -15,31 +15,41 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <dt-bindings/regulator/active-semi,8945a-regulator.h> /** * ACT8945A Global Register Map. */ #define ACT8945A_SYS_MODE 0x00 #define ACT8945A_SYS_CTRL 0x01 +#define ACT8945A_SYS_UNLK_REGS 0x0b #define ACT8945A_DCDC1_VSET1 0x20 #define ACT8945A_DCDC1_VSET2 0x21 #define ACT8945A_DCDC1_CTRL 0x22 +#define ACT8945A_DCDC1_SUS 0x24 #define ACT8945A_DCDC2_VSET1 0x30 #define ACT8945A_DCDC2_VSET2 0x31 #define ACT8945A_DCDC2_CTRL 0x32 +#define ACT8945A_DCDC2_SUS 0x34 #define ACT8945A_DCDC3_VSET1 0x40 #define ACT8945A_DCDC3_VSET2 0x41 #define ACT8945A_DCDC3_CTRL 0x42 +#define ACT8945A_DCDC3_SUS 0x44 #define ACT8945A_LDO1_VSET 0x50 #define ACT8945A_LDO1_CTRL 0x51 +#define ACT8945A_LDO1_SUS 0x52 #define ACT8945A_LDO2_VSET 0x54 #define ACT8945A_LDO2_CTRL 0x55 +#define ACT8945A_LDO2_SUS 0x56 #define ACT8945A_LDO3_VSET 0x60 #define ACT8945A_LDO3_CTRL 0x61 +#define ACT8945A_LDO3_SUS 0x62 #define ACT8945A_LDO4_VSET 0x64 #define ACT8945A_LDO4_CTRL 0x65 +#define ACT8945A_LDO4_SUS 0x66 /** * Field Definitions. @@ -60,7 +70,12 @@ enum { ACT8945A_ID_LDO2, ACT8945A_ID_LDO3, ACT8945A_ID_LDO4, - ACT8945A_REG_NUM, + ACT8945A_ID_MAX, +}; + +struct act8945a_pmic { + struct regmap *regmap; + u32 op_mode[ACT8945A_ID_MAX]; }; static const struct regulator_linear_range act8945a_voltage_ranges[] = { @@ -69,6 +84,143 @@ static const struct regulator_linear_range act8945a_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000), }; +static int act8945a_set_suspend_state(struct regulator_dev *rdev, bool enable) +{ + struct regmap *regmap = rdev->regmap; + int id = rdev->desc->id, reg, val; + + switch (id) { + case ACT8945A_ID_DCDC1: + reg = ACT8945A_DCDC1_SUS; + val = 0xa8; + break; + case ACT8945A_ID_DCDC2: + reg = ACT8945A_DCDC2_SUS; + val = 0xa8; + break; + case ACT8945A_ID_DCDC3: + reg = ACT8945A_DCDC3_SUS; + val = 0xa8; + break; + case ACT8945A_ID_LDO1: + reg = ACT8945A_LDO1_SUS; + val = 0xe8; + break; + case ACT8945A_ID_LDO2: + reg = ACT8945A_LDO2_SUS; + val = 0xe8; + break; + case ACT8945A_ID_LDO3: + reg = ACT8945A_LDO3_SUS; + val = 0xe8; + break; + case ACT8945A_ID_LDO4: + reg = ACT8945A_LDO4_SUS; + val = 0xe8; + break; + default: + return -EINVAL; + } + + if (enable) + val |= BIT(4); + + /* + * Ask the PMIC to enable/disable this output when entering hibernate + * mode. + */ + return regmap_write(regmap, reg, val); +} + +static int act8945a_set_suspend_enable(struct regulator_dev *rdev) +{ + return act8945a_set_suspend_state(rdev, true); +} + +static int act8945a_set_suspend_disable(struct regulator_dev *rdev) +{ + return act8945a_set_suspend_state(rdev, false); +} + +static unsigned int act8945a_of_map_mode(unsigned int mode) +{ + switch (mode) { + case ACT8945A_REGULATOR_MODE_FIXED: + case ACT8945A_REGULATOR_MODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case ACT8945A_REGULATOR_MODE_LOWPOWER: + return REGULATOR_MODE_STANDBY; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int act8945a_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev); + struct regmap *regmap = rdev->regmap; + int id = rdev->desc->id; + int reg, ret, val = 0; + + switch (id) { + case ACT8945A_ID_DCDC1: + reg = ACT8945A_DCDC1_CTRL; + break; + case ACT8945A_ID_DCDC2: + reg = ACT8945A_DCDC2_CTRL; + break; + case ACT8945A_ID_DCDC3: + reg = ACT8945A_DCDC3_CTRL; + break; + case ACT8945A_ID_LDO1: + reg = ACT8945A_LDO1_SUS; + break; + case ACT8945A_ID_LDO2: + reg = ACT8945A_LDO2_SUS; + break; + case ACT8945A_ID_LDO3: + reg = ACT8945A_LDO3_SUS; + break; + case ACT8945A_ID_LDO4: + reg = ACT8945A_LDO4_SUS; + break; + default: + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_STANDBY: + if (rdev->desc->id > ACT8945A_ID_DCDC3) + val = BIT(5); + break; + case REGULATOR_MODE_NORMAL: + if (rdev->desc->id <= ACT8945A_ID_DCDC3) + val = BIT(5); + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(regmap, reg, BIT(5), val); + if (ret) + return ret; + + act8945a->op_mode[id] = mode; + + return 0; +} + +static unsigned int act8945a_get_mode(struct regulator_dev *rdev) +{ + struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev); + int id = rdev->desc->id; + + if (id < ACT8945A_ID_DCDC1 || id >= ACT8945A_ID_MAX) + return -EINVAL; + + return act8945a->op_mode[id]; +} + static const struct regulator_ops act8945a_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, @@ -76,7 +228,11 @@ static const struct regulator_ops act8945a_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .set_mode = act8945a_set_mode, + .get_mode = act8945a_get_mode, .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = act8945a_set_suspend_enable, + .set_suspend_disable = act8945a_set_suspend_disable, }; #define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply) \ @@ -84,6 +240,7 @@ static const struct regulator_ops act8945a_ops = { .name = _name, \ .supply_name = _supply, \ .of_match = of_match_ptr("REG_"#_id), \ + .of_map_mode = act8945a_of_map_mode, \ .regulators_node = of_match_ptr("regulators"), \ .id = _family##_ID_##_id, \ .type = REGULATOR_VOLTAGE, \ @@ -122,10 +279,22 @@ static int act8945a_pmic_probe(struct platform_device *pdev) { struct regulator_config config = { }; const struct regulator_desc *regulators; + struct act8945a_pmic *act8945a; struct regulator_dev *rdev; int i, num_regulators; bool voltage_select; + act8945a = devm_kzalloc(&pdev->dev, sizeof(*act8945a), GFP_KERNEL); + if (!act8945a) + return -ENOMEM; + + act8945a->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!act8945a->regmap) { + dev_err(&pdev->dev, + "could not retrieve regmap from parent device\n"); + return -EINVAL; + } + voltage_select = of_property_read_bool(pdev->dev.parent->of_node, "active-semi,vsel-high"); @@ -139,8 +308,10 @@ static int act8945a_pmic_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.dev->of_node = pdev->dev.parent->of_node; + config.driver_data = act8945a; for (i = 0; i < num_regulators; i++) { - rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], + &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s regulator\n", @@ -149,14 +320,42 @@ static int act8945a_pmic_probe(struct platform_device *pdev) } } - return 0; + platform_set_drvdata(pdev, act8945a); + + /* Unlock expert registers. */ + return regmap_write(act8945a->regmap, ACT8945A_SYS_UNLK_REGS, 0xef); +} + +static int __maybe_unused act8945a_suspend(struct device *pdev) +{ + struct act8945a_pmic *act8945a = dev_get_drvdata(pdev); + + /* + * Ask the PMIC to enter the suspend mode on the next PWRHLD + * transition. + */ + return regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x42); +} + +static SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL); + +static void act8945a_pmic_shutdown(struct platform_device *pdev) +{ + struct act8945a_pmic *act8945a = platform_get_drvdata(pdev); + + /* + * Ask the PMIC to shutdown everything on the next PWRHLD transition. + */ + regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x0); } static struct platform_driver act8945a_pmic_driver = { .driver = { .name = "act8945a-regulator", + .pm = &act8945a_pm, }, .probe = act8945a_pmic_probe, + .shutdown = act8945a_pmic_shutdown, }; module_platform_driver(act8945a_pmic_driver); diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 9a72eae4926d..b9a93049e41e 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -283,9 +283,6 @@ static int arizona_ldo1_common_init(struct platform_device *pdev, of_node_put(config.of_node); if (IS_ERR(ldo1->regulator)) { - if (config.ena_gpiod) - gpiod_put(config.ena_gpiod); - ret = PTR_ERR(ldo1->regulator); dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n", ret); diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 565a71343a8e..f7fe218bb3e4 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies * * Copyright (C) 2012 Renesas Electronics Corporation * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the version 2 of the GNU General Public License as - * published by the Free Software Foundation */ #include <linux/err.h> diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index a3734039a86a..48af859fd053 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -13,31 +13,262 @@ * GNU General Public License for more details. */ +#include <linux/bitops.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> +#include <linux/mfd/axp20x.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/mfd/axp20x.h> #include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> +#define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0) +#define AXP20X_GPIO1_FUNC_MASK GENMASK(3, 0) + #define AXP20X_IO_ENABLED 0x03 #define AXP20X_IO_DISABLED 0x07 +#define AXP20X_WORKMODE_DCDC2_MASK BIT_MASK(2) +#define AXP20X_WORKMODE_DCDC3_MASK BIT_MASK(1) + +#define AXP20X_FREQ_DCDC_MASK GENMASK(3, 0) + +#define AXP20X_VBUS_IPSOUT_MGMT_MASK BIT_MASK(2) + +#define AXP20X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP20X_DCDC3_V_OUT_MASK GENMASK(7, 0) +#define AXP20X_LDO24_V_OUT_MASK GENMASK(7, 4) +#define AXP20X_LDO3_V_OUT_MASK GENMASK(6, 0) +#define AXP20X_LDO5_V_OUT_MASK GENMASK(7, 4) + +#define AXP20X_PWR_OUT_EXTEN_MASK BIT_MASK(0) +#define AXP20X_PWR_OUT_DCDC3_MASK BIT_MASK(1) +#define AXP20X_PWR_OUT_LDO2_MASK BIT_MASK(2) +#define AXP20X_PWR_OUT_LDO4_MASK BIT_MASK(3) +#define AXP20X_PWR_OUT_DCDC2_MASK BIT_MASK(4) +#define AXP20X_PWR_OUT_LDO3_MASK BIT_MASK(6) + +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK BIT_MASK(0) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE(x) \ + ((x) << 0) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK BIT_MASK(1) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(x) \ + ((x) << 1) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK BIT_MASK(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN BIT(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK BIT_MASK(3) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN BIT(3) + +#define AXP20X_LDO4_V_OUT_1250mV_START 0x0 +#define AXP20X_LDO4_V_OUT_1250mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_1250mV_END \ + (AXP20X_LDO4_V_OUT_1250mV_START + AXP20X_LDO4_V_OUT_1250mV_STEPS) +#define AXP20X_LDO4_V_OUT_1300mV_START 0x1 +#define AXP20X_LDO4_V_OUT_1300mV_STEPS 7 +#define AXP20X_LDO4_V_OUT_1300mV_END \ + (AXP20X_LDO4_V_OUT_1300mV_START + AXP20X_LDO4_V_OUT_1300mV_STEPS) +#define AXP20X_LDO4_V_OUT_2500mV_START 0x9 +#define AXP20X_LDO4_V_OUT_2500mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_2500mV_END \ + (AXP20X_LDO4_V_OUT_2500mV_START + AXP20X_LDO4_V_OUT_2500mV_STEPS) +#define AXP20X_LDO4_V_OUT_2700mV_START 0xa +#define AXP20X_LDO4_V_OUT_2700mV_STEPS 1 +#define AXP20X_LDO4_V_OUT_2700mV_END \ + (AXP20X_LDO4_V_OUT_2700mV_START + AXP20X_LDO4_V_OUT_2700mV_STEPS) +#define AXP20X_LDO4_V_OUT_3000mV_START 0xc +#define AXP20X_LDO4_V_OUT_3000mV_STEPS 3 +#define AXP20X_LDO4_V_OUT_3000mV_END \ + (AXP20X_LDO4_V_OUT_3000mV_START + AXP20X_LDO4_V_OUT_3000mV_STEPS) +#define AXP20X_LDO4_V_OUT_NUM_VOLTAGES 16 + #define AXP22X_IO_ENABLED 0x03 #define AXP22X_IO_DISABLED 0x04 -#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) -#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) -#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x) - -#define AXP20X_FREQ_DCDC_MASK 0x0f +#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT_MASK(x) #define AXP22X_MISC_N_VBUSEN_FUNC BIT(4) +#define AXP22X_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC3_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC4_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC5_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DC5LDO_V_OUT_MASK GENMASK(2, 0) +#define AXP22X_ALDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO4_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO0_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO1_V_OUT_MASK GENMASK(4, 0) + +#define AXP22X_PWR_OUT_DC5LDO_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_DCDC1_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_DCDC2_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DCDC3_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DCDC4_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DCDC5_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_ALDO1_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO2_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_SW_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_DC1SW_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_ELDO1_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_ELDO2_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_ELDO3_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DLDO1_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DLDO2_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DLDO3_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_DLDO4_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO3_MASK BIT_MASK(7) + +#define AXP803_PWR_OUT_DCDC1_MASK BIT_MASK(0) +#define AXP803_PWR_OUT_DCDC2_MASK BIT_MASK(1) +#define AXP803_PWR_OUT_DCDC3_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_DCDC4_MASK BIT_MASK(3) +#define AXP803_PWR_OUT_DCDC5_MASK BIT_MASK(4) +#define AXP803_PWR_OUT_DCDC6_MASK BIT_MASK(5) + +#define AXP803_PWR_OUT_FLDO1_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_FLDO2_MASK BIT_MASK(3) + +#define AXP803_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP803_DCDC2_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC3_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC4_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC5_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC6_V_OUT_MASK GENMASK(6, 0) + +#define AXP803_FLDO1_V_OUT_MASK GENMASK(3, 0) +#define AXP803_FLDO2_V_OUT_MASK GENMASK(3, 0) + +#define AXP803_DCDC23_POLYPHASE_DUAL BIT(6) +#define AXP803_DCDC56_POLYPHASE_DUAL BIT(5) + +#define AXP803_DCDC234_500mV_START 0x00 +#define AXP803_DCDC234_500mV_STEPS 70 +#define AXP803_DCDC234_500mV_END \ + (AXP803_DCDC234_500mV_START + AXP803_DCDC234_500mV_STEPS) +#define AXP803_DCDC234_1220mV_START 0x47 +#define AXP803_DCDC234_1220mV_STEPS 4 +#define AXP803_DCDC234_1220mV_END \ + (AXP803_DCDC234_1220mV_START + AXP803_DCDC234_1220mV_STEPS) +#define AXP803_DCDC234_NUM_VOLTAGES 76 + +#define AXP803_DCDC5_800mV_START 0x00 +#define AXP803_DCDC5_800mV_STEPS 32 +#define AXP803_DCDC5_800mV_END \ + (AXP803_DCDC5_800mV_START + AXP803_DCDC5_800mV_STEPS) +#define AXP803_DCDC5_1140mV_START 0x21 +#define AXP803_DCDC5_1140mV_STEPS 35 +#define AXP803_DCDC5_1140mV_END \ + (AXP803_DCDC5_1140mV_START + AXP803_DCDC5_1140mV_STEPS) +#define AXP803_DCDC5_NUM_VOLTAGES 68 + +#define AXP803_DCDC6_600mV_START 0x00 +#define AXP803_DCDC6_600mV_STEPS 50 +#define AXP803_DCDC6_600mV_END \ + (AXP803_DCDC6_600mV_START + AXP803_DCDC6_600mV_STEPS) +#define AXP803_DCDC6_1120mV_START 0x33 +#define AXP803_DCDC6_1120mV_STEPS 14 +#define AXP803_DCDC6_1120mV_END \ + (AXP803_DCDC6_1120mV_START + AXP803_DCDC6_1120mV_STEPS) +#define AXP803_DCDC6_NUM_VOLTAGES 72 + +#define AXP803_DLDO2_700mV_START 0x00 +#define AXP803_DLDO2_700mV_STEPS 26 +#define AXP803_DLDO2_700mV_END \ + (AXP803_DLDO2_700mV_START + AXP803_DLDO2_700mV_STEPS) +#define AXP803_DLDO2_3400mV_START 0x1b +#define AXP803_DLDO2_3400mV_STEPS 4 +#define AXP803_DLDO2_3400mV_END \ + (AXP803_DLDO2_3400mV_START + AXP803_DLDO2_3400mV_STEPS) +#define AXP803_DLDO2_NUM_VOLTAGES 32 + +#define AXP806_DCDCA_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCB_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_DCDCC_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCD_V_CTRL_MASK GENMASK(5, 0) +#define AXP806_DCDCE_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO3_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_BLDO1_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO2_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO3_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO4_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_CLDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO3_V_CTRL_MASK GENMASK(4, 0) + +#define AXP806_PWR_OUT_DCDCA_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_DCDCB_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_DCDCC_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_DCDCD_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_DCDCE_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_ALDO1_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_ALDO2_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_ALDO3_MASK BIT_MASK(7) +#define AXP806_PWR_OUT_BLDO1_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_BLDO2_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_BLDO3_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_BLDO4_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_CLDO1_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_CLDO2_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_CLDO3_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_SW_MASK BIT_MASK(7) + +#define AXP806_DCDCAB_POLYPHASE_DUAL 0x40 +#define AXP806_DCDCABC_POLYPHASE_TRI 0x80 +#define AXP806_DCDCABC_POLYPHASE_MASK GENMASK(7, 6) + +#define AXP806_DCDCDE_POLYPHASE_DUAL BIT(5) + +#define AXP806_DCDCA_600mV_START 0x00 +#define AXP806_DCDCA_600mV_STEPS 50 +#define AXP806_DCDCA_600mV_END \ + (AXP806_DCDCA_600mV_START + AXP806_DCDCA_600mV_STEPS) +#define AXP806_DCDCA_1120mV_START 0x33 +#define AXP806_DCDCA_1120mV_STEPS 14 +#define AXP806_DCDCA_1120mV_END \ + (AXP806_DCDCA_1120mV_START + AXP806_DCDCA_1120mV_STEPS) +#define AXP806_DCDCA_NUM_VOLTAGES 72 + +#define AXP806_DCDCD_600mV_START 0x00 +#define AXP806_DCDCD_600mV_STEPS 45 +#define AXP806_DCDCD_600mV_END \ + (AXP806_DCDCD_600mV_START + AXP806_DCDCD_600mV_STEPS) +#define AXP806_DCDCD_1600mV_START 0x2e +#define AXP806_DCDCD_1600mV_STEPS 17 +#define AXP806_DCDCD_1600mV_END \ + (AXP806_DCDCD_1600mV_START + AXP806_DCDCD_1600mV_STEPS) +#define AXP806_DCDCD_NUM_VOLTAGES 64 + +#define AXP809_DCDC4_600mV_START 0x00 +#define AXP809_DCDC4_600mV_STEPS 47 +#define AXP809_DCDC4_600mV_END \ + (AXP809_DCDC4_600mV_START + AXP809_DCDC4_600mV_STEPS) +#define AXP809_DCDC4_1800mV_START 0x30 +#define AXP809_DCDC4_1800mV_STEPS 8 +#define AXP809_DCDC4_1800mV_END \ + (AXP809_DCDC4_1800mV_START + AXP809_DCDC4_1800mV_STEPS) +#define AXP809_DCDC4_NUM_VOLTAGES 57 + +#define AXP813_DCDC7_V_OUT_MASK GENMASK(6, 0) + +#define AXP813_PWR_OUT_DCDC7_MASK BIT_MASK(6) + #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ _vmask, _ereg, _emask, _enable_val, _disable_val) \ [_family##_##_id] = { \ @@ -128,6 +359,133 @@ .ops = &axp20x_ops_range, \ } +static const int axp209_dcdc2_ldo3_slew_rates[] = { + 1600, + 800, +}; + +static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + u8 reg, mask, enable, cfg = 0xff; + const int *slew_rates; + int rate_count = 0; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if (desc->id == AXP20X_DCDC2) { + slew_rates = axp209_dcdc2_ldo3_slew_rates; + rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN; + break; + } + + if (desc->id == AXP20X_LDO3) { + slew_rates = axp209_dcdc2_ldo3_slew_rates; + rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN; + break; + } + + if (rate_count > 0) + break; + + /* fall through */ + default: + /* Not supported for this regulator */ + return -ENOTSUPP; + } + + if (ramp == 0) { + cfg = enable; + } else { + int i; + + for (i = 0; i < rate_count; i++) { + if (ramp <= slew_rates[i]) + cfg = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(i); + else + break; + } + + if (cfg == 0xff) { + dev_err(axp20x->dev, "unsupported ramp value %d", ramp); + return -EINVAL; + } + + cfg |= enable; + } + + return regmap_update_bits(axp20x->regmap, reg, mask, cfg); +} + +static int axp20x_regulator_enable_regmap(struct regulator_dev *rdev) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if ((desc->id == AXP20X_LDO3) && + rdev->constraints && rdev->constraints->soft_start) { + int v_out; + int ret; + + /* + * On some boards, the LDO3 can be overloaded when + * turning on, causing the entire PMIC to shutdown + * without warning. Turning it on at the minimal voltage + * and then setting the voltage to the requested value + * works reliably. + */ + if (regulator_is_enabled_regmap(rdev)) + break; + + v_out = regulator_get_voltage_sel_regmap(rdev); + if (v_out < 0) + return v_out; + + if (v_out == 0) + break; + + ret = regulator_set_voltage_sel_regmap(rdev, 0x00); + /* + * A small pause is needed between + * setting the voltage and enabling the LDO to give the + * internal state machine time to process the request. + */ + usleep_range(1000, 5000); + ret |= regulator_enable_regmap(rdev); + ret |= regulator_set_voltage_sel_regmap(rdev, v_out); + + return ret; + } + break; + default: + /* No quirks */ + break; + } + + return regulator_enable_regmap(rdev); +}; + static const struct regulator_ops axp20x_ops_fixed = { .list_voltage = regulator_list_voltage_linear, }; @@ -145,9 +503,10 @@ static const struct regulator_ops axp20x_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, - .enable = regulator_enable_regmap, + .enable = axp20x_regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_ramp_delay = axp20x_set_ramp_delay, }; static const struct regulator_ops axp20x_ops_sw = { @@ -157,77 +516,116 @@ static const struct regulator_ops axp20x_ops_sw = { }; static const struct regulator_linear_range axp20x_ldo4_ranges[] = { - REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0), - REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000), - REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0), - REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000), - REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000), + REGULATOR_LINEAR_RANGE(1250000, + AXP20X_LDO4_V_OUT_1250mV_START, + AXP20X_LDO4_V_OUT_1250mV_END, + 0), + REGULATOR_LINEAR_RANGE(1300000, + AXP20X_LDO4_V_OUT_1300mV_START, + AXP20X_LDO4_V_OUT_1300mV_END, + 100000), + REGULATOR_LINEAR_RANGE(2500000, + AXP20X_LDO4_V_OUT_2500mV_START, + AXP20X_LDO4_V_OUT_2500mV_END, + 0), + REGULATOR_LINEAR_RANGE(2700000, + AXP20X_LDO4_V_OUT_2700mV_START, + AXP20X_LDO4_V_OUT_2700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3000000, + AXP20X_LDO4_V_OUT_3000mV_START, + AXP20X_LDO4_V_OUT_3000mV_END, + 100000), }; static const struct regulator_desc axp20x_regulators[] = { AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25, - AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DCDC2_V_OUT, AXP20X_DCDC2_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25, - AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DCDC3_V_OUT, AXP20X_DCDC3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC3_MASK), AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300), AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100, - AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO2_MASK), AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25, - AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), - AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges, - 16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, - 0x08), + AXP20X_LDO3_V_OUT, AXP20X_LDO3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO3_MASK), + AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", + axp20x_ldo4_ranges, AXP20X_LDO4_V_OUT_NUM_VOLTAGES, + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO4_MASK), AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100, - AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, + AXP20X_LDO5_V_OUT, AXP20X_LDO5_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; static const struct regulator_desc axp22x_regulators[] = { AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20, - AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)), + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP22X_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000), }; @@ -240,240 +638,354 @@ static const struct regulator_desc axp22x_drivevbus_regulator = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = AXP20X_VBUS_IPSOUT_MGMT, - .enable_mask = BIT(2), + .enable_mask = AXP20X_VBUS_IPSOUT_MGMT_MASK, .ops = &axp20x_ops_sw, }; /* DCDC ranges shared with AXP813 */ static const struct regulator_linear_range axp803_dcdc234_ranges[] = { - REGULATOR_LINEAR_RANGE(500000, 0x0, 0x46, 10000), - REGULATOR_LINEAR_RANGE(1220000, 0x47, 0x4b, 20000), + REGULATOR_LINEAR_RANGE(500000, + AXP803_DCDC234_500mV_START, + AXP803_DCDC234_500mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1220000, + AXP803_DCDC234_1220mV_START, + AXP803_DCDC234_1220mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc5_ranges[] = { - REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 10000), - REGULATOR_LINEAR_RANGE(1140000, 0x21, 0x44, 20000), + REGULATOR_LINEAR_RANGE(800000, + AXP803_DCDC5_800mV_START, + AXP803_DCDC5_800mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1140000, + AXP803_DCDC5_1140mV_START, + AXP803_DCDC5_1140mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc6_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP803_DCDC6_600mV_START, + AXP803_DCDC6_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP803_DCDC6_1120mV_START, + AXP803_DCDC6_1120mV_END, + 20000), }; -/* AXP806's CLDO2 and AXP809's DLDO1 shares the same range */ +/* AXP806's CLDO2 and AXP809's DLDO1 share the same range */ static const struct regulator_linear_range axp803_dldo2_ranges[] = { - REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000), - REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000), + REGULATOR_LINEAR_RANGE(700000, + AXP803_DLDO2_700mV_START, + AXP803_DLDO2_700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3400000, + AXP803_DLDO2_3400mV_START, + AXP803_DLDO2_3400mV_END, + 200000), }; static const struct regulator_desc axp803_regulators[] = { AXP_DESC(AXP803, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), AXP_DESC(AXP803, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP803, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP803, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP803, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP803, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP803, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP803, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP803, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP803, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), AXP_DESC(AXP803, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP803, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), AXP_DESC_IO(AXP803, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP803, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000), }; static const struct regulator_linear_range axp806_dcdca_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCA_600mV_START, + AXP806_DCDCA_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP806_DCDCA_1120mV_START, + AXP806_DCDCA_1120mV_END, + 20000), }; static const struct regulator_linear_range axp806_dcdcd_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000), - REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 100000), }; static const struct regulator_desc axp806_regulators[] = { - AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges, - 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(0)), + AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCA_V_CTRL, AXP806_DCDCA_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCA_MASK), AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50, - AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)), - AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges, - 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges, - 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1, - BIT(3)), + AXP806_DCDCB_V_CTRL, AXP806_DCDCB_V_CTRL, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCB_MASK), + AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCC_V_CTRL, AXP806_DCDCC_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCC_MASK), + AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", + axp806_dcdcd_ranges, AXP806_DCDCD_NUM_VOLTAGES, + AXP806_DCDCD_V_CTRL, AXP806_DCDCD_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCD_MASK), AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100, - AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)), + AXP806_DCDCE_V_CTRL, AXP806_DCDCE_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCE_MASK), AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)), + AXP806_ALDO1_V_CTRL, AXP806_ALDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100, - AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)), + AXP806_ALDO2_V_CTRL, AXP806_ALDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)), + AXP806_ALDO3_V_CTRL, AXP806_ALDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100, - AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)), + AXP806_BLDO1_V_CTRL, AXP806_BLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO1_MASK), AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100, - AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)), + AXP806_BLDO2_V_CTRL, AXP806_BLDO2_V_CTRL, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO2_MASK), AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100, - AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)), + AXP806_BLDO3_V_CTRL, AXP806_BLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO3_MASK), AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100, - AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)), + AXP806_BLDO4_V_CTRL, AXP806_BLDO4_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO4_MASK), AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100, - AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)), - AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp803_dldo2_ranges, - 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, - BIT(5)), + AXP806_CLDO1_V_CTRL, AXP806_CLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO1_MASK), + AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP806_CLDO2_V_CTRL, AXP806_CLDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO2_MASK), AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100, - AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)), - AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)), + AXP806_CLDO3_V_CTRL, AXP806_CLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO3_MASK), + AXP_DESC_SW(AXP806, SW, "sw", "swin", + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_SW_MASK), }; static const struct regulator_linear_range axp809_dcdc4_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000), - REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP809_DCDC4_600mV_START, + AXP809_DCDC4_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1800000, + AXP809_DCDC4_1800mV_START, + AXP809_DCDC4_1800mV_END, + 100000), }; static const struct regulator_desc axp809_regulators[] = { AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP809, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP809, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), - AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", axp809_dcdc4_ranges, - 57, AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", + axp809_dcdc4_ranges, AXP809_DCDC4_NUM_VOLTAGES, + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP809, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP809, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP809, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP809, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), - AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(3)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ALDO3_MASK), + AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP809, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP809, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800), - AXP_DESC_SW(AXP809, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP_DESC_SW(AXP809, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_SW_MASK), }; static const struct regulator_desc axp813_regulators[] = { AXP_DESC(AXP813, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), - AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", axp803_dcdc6_ranges, - 72, AXP813_DCDC7_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(6)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), + AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP813_DCDC7_V_OUT, AXP813_DCDC7_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP813_PWR_OUT_DCDC7_MASK), AXP_DESC(AXP813, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP813, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP813, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP813, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP813, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP813, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP813, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP813, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP813, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* to do / check ... */ AXP_DESC(AXP813, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP813, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), /* * TODO: FLDO3 = {DCDC5, FLDOIN} / 2 * @@ -482,12 +994,15 @@ static const struct regulator_desc axp813_regulators[] = { */ AXP_DESC_FIXED(AXP813, RTC_LDO, "rtc-ldo", "ips", 1800), AXP_DESC_IO(AXP813, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP813, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), - AXP_DESC_SW(AXP813, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(7)), + AXP_DESC_SW(AXP813, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), }; static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) @@ -663,9 +1178,9 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP803_DCDC3: - return !!(reg & BIT(6)); + return !!(reg & AXP803_DCDC23_POLYPHASE_DUAL); case AXP803_DCDC6: - return !!(reg & BIT(5)); + return !!(reg & AXP803_DCDC56_POLYPHASE_DUAL); } break; @@ -674,12 +1189,15 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP806_DCDCB: - return (((reg & GENMASK(7, 6)) == BIT(6)) || - ((reg & GENMASK(7, 6)) == BIT(7))); + return (((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCAB_POLYPHASE_DUAL) || + ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI)); case AXP806_DCDCC: - return ((reg & GENMASK(7, 6)) == BIT(7)); + return ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI); case AXP806_DCDCE: - return !!(reg & BIT(5)); + return !!(reg & AXP806_DCDCDE_POLYPHASE_DUAL); } break; diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 3a47e0372e77..b8dcdc21dc22 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/mfd/rohm-bd718x7.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> @@ -130,6 +131,7 @@ static struct regulator_ops bd718xx_buck_regulator_nolinear_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, .set_voltage_sel = bd718xx_set_voltage_sel_restricted, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, @@ -1007,7 +1009,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { }; struct bd718xx_pmic_inits { - const struct bd718xx_regulator_data (*r_datas)[]; + const struct bd718xx_regulator_data *r_datas; unsigned int r_amount; }; @@ -1017,11 +1019,11 @@ static int bd718xx_probe(struct platform_device *pdev) struct regulator_config config = { 0 }; struct bd718xx_pmic_inits pmic_regulators[] = { [BD718XX_TYPE_BD71837] = { - .r_datas = &bd71837_regulators, + .r_datas = bd71837_regulators, .r_amount = ARRAY_SIZE(bd71837_regulators), }, [BD718XX_TYPE_BD71847] = { - .r_datas = &bd71847_regulators, + .r_datas = bd71847_regulators, .r_amount = ARRAY_SIZE(bd71847_regulators), }, }; @@ -1053,13 +1055,36 @@ static int bd718xx_probe(struct platform_device *pdev) BD718XX_REG_REGLOCK); } + /* At poweroff transition PMIC HW disables EN bit for regulators but + * leaves SEL bit untouched. So if state transition from POWEROFF + * is done to SNVS - then all power rails controlled by SW (having + * SEL bit set) stay disabled as EN is cleared. This may result boot + * failure if any crucial systems are powered by these rails. + * + * Change the next stage from poweroff to be READY instead of SNVS + * for all reset types because OTP loading at READY will clear SEL + * bit allowing HW defaults for power rails to be used + */ + err = regmap_update_bits(mfd->regmap, BD718XX_REG_TRANS_COND1, + BD718XX_ON_REQ_POWEROFF_MASK | + BD718XX_SWRESET_POWEROFF_MASK | + BD718XX_WDOG_POWEROFF_MASK | + BD718XX_KEY_L_POWEROFF_MASK, + BD718XX_POWOFF_TO_RDY); + if (err) { + dev_err(&pdev->dev, "Failed to change reset target\n"); + goto err; + } else { + dev_dbg(&pdev->dev, "Changed all resets from SVNS to READY\n"); + } + for (i = 0; i < pmic_regulators[mfd->chip_type].r_amount; i++) { const struct regulator_desc *desc; struct regulator_dev *rdev; const struct bd718xx_regulator_data *r; - r = &(*pmic_regulators[mfd->chip_type].r_datas)[i]; + r = &pmic_regulators[mfd->chip_type].r_datas[i]; desc = &r->desc; config.dev = pdev->dev.parent; diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index 274c5ed7cd73..e12dd1f750f3 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ROHM BD9571MWV-M regulator driver * * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65086 driver * * NOTE: VD09 is missing diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2c66b528aede..b9d7b45c7295 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -50,6 +50,8 @@ #define rdev_dbg(rdev, fmt, ...) \ pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) +static DEFINE_WW_CLASS(regulator_ww_class); +static DEFINE_MUTEX(regulator_nesting_mutex); static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); @@ -97,7 +99,7 @@ struct regulator_supply_alias { }; static int _regulator_is_enabled(struct regulator_dev *rdev); -static int _regulator_disable(struct regulator_dev *rdev); +static int _regulator_disable(struct regulator *regulator); static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev); @@ -105,6 +107,11 @@ static int _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); +static int regulator_balance_voltage(struct regulator_dev *rdev, + suspend_state_t state); +static int regulator_set_voltage_rdev(struct regulator_dev *rdev, + int min_uV, int max_uV, + suspend_state_t state); static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, const char *supply_name); @@ -149,7 +156,7 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) /** * regulator_lock_nested - lock a single regulator * @rdev: regulator source - * @subclass: mutex subclass used for lockdep + * @ww_ctx: w/w mutex acquire context * * This function can be called many times by one task on * a single regulator and its mutex will be locked only @@ -157,25 +164,54 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) * than the one, which initially locked the mutex, it will * wait on mutex. */ -static void regulator_lock_nested(struct regulator_dev *rdev, - unsigned int subclass) +static inline int regulator_lock_nested(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - if (!mutex_trylock(&rdev->mutex)) { - if (rdev->mutex_owner == current) { + bool lock = false; + int ret = 0; + + mutex_lock(®ulator_nesting_mutex); + + if (ww_ctx || !ww_mutex_trylock(&rdev->mutex)) { + if (rdev->mutex_owner == current) rdev->ref_cnt++; - return; + else + lock = true; + + if (lock) { + mutex_unlock(®ulator_nesting_mutex); + ret = ww_mutex_lock(&rdev->mutex, ww_ctx); + mutex_lock(®ulator_nesting_mutex); } - mutex_lock_nested(&rdev->mutex, subclass); + } else { + lock = true; + } + + if (lock && ret != -EDEADLK) { + rdev->ref_cnt++; + rdev->mutex_owner = current; } - rdev->ref_cnt = 1; - rdev->mutex_owner = current; + mutex_unlock(®ulator_nesting_mutex); + + return ret; } -static inline void regulator_lock(struct regulator_dev *rdev) +/** + * regulator_lock - lock a single regulator + * @rdev: regulator source + * + * This function can be called many times by one task on + * a single regulator and its mutex will be locked only + * once. If a task, which is calling this function is other + * than the one, which initially locked the mutex, it will + * wait on mutex. + */ +void regulator_lock(struct regulator_dev *rdev) { - regulator_lock_nested(rdev, 0); + regulator_lock_nested(rdev, NULL); } +EXPORT_SYMBOL_GPL(regulator_lock); /** * regulator_unlock - unlock a single regulator @@ -184,47 +220,191 @@ static inline void regulator_lock(struct regulator_dev *rdev) * This function unlocks the mutex when the * reference counter reaches 0. */ -static void regulator_unlock(struct regulator_dev *rdev) +void regulator_unlock(struct regulator_dev *rdev) +{ + mutex_lock(®ulator_nesting_mutex); + + if (--rdev->ref_cnt == 0) { + rdev->mutex_owner = NULL; + ww_mutex_unlock(&rdev->mutex); + } + + WARN_ON_ONCE(rdev->ref_cnt < 0); + + mutex_unlock(®ulator_nesting_mutex); +} +EXPORT_SYMBOL_GPL(regulator_unlock); + +static bool regulator_supply_is_couple(struct regulator_dev *rdev) +{ + struct regulator_dev *c_rdev; + int i; + + for (i = 1; i < rdev->coupling_desc.n_coupled; i++) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i]; + + if (rdev->supply->rdev == c_rdev) + return true; + } + + return false; +} + +static void regulator_unlock_recursive(struct regulator_dev *rdev, + unsigned int n_coupled) { - if (rdev->ref_cnt != 0) { - rdev->ref_cnt--; + struct regulator_dev *c_rdev; + int i; + + for (i = n_coupled; i > 0; i--) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i - 1]; + + if (!c_rdev) + continue; + + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) + regulator_unlock_recursive( + c_rdev->supply->rdev, + c_rdev->coupling_desc.n_coupled); - if (!rdev->ref_cnt) { - rdev->mutex_owner = NULL; - mutex_unlock(&rdev->mutex); + regulator_unlock(c_rdev); + } +} + +static int regulator_lock_recursive(struct regulator_dev *rdev, + struct regulator_dev **new_contended_rdev, + struct regulator_dev **old_contended_rdev, + struct ww_acquire_ctx *ww_ctx) +{ + struct regulator_dev *c_rdev; + int i, err; + + for (i = 0; i < rdev->coupling_desc.n_coupled; i++) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i]; + + if (!c_rdev) + continue; + + if (c_rdev != *old_contended_rdev) { + err = regulator_lock_nested(c_rdev, ww_ctx); + if (err) { + if (err == -EDEADLK) { + *new_contended_rdev = c_rdev; + goto err_unlock; + } + + /* shouldn't happen */ + WARN_ON_ONCE(err != -EALREADY); + } + } else { + *old_contended_rdev = NULL; + } + + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) { + err = regulator_lock_recursive(c_rdev->supply->rdev, + new_contended_rdev, + old_contended_rdev, + ww_ctx); + if (err) { + regulator_unlock(c_rdev); + goto err_unlock; + } } } + + return 0; + +err_unlock: + regulator_unlock_recursive(rdev, i); + + return err; } /** - * regulator_lock_supply - lock a regulator and its supplies - * @rdev: regulator source + * regulator_unlock_dependent - unlock regulator's suppliers and coupled + * regulators + * @rdev: regulator source + * @ww_ctx: w/w mutex acquire context + * + * Unlock all regulators related with rdev by coupling or suppling. */ -static void regulator_lock_supply(struct regulator_dev *rdev) +static void regulator_unlock_dependent(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - int i; - - for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) - regulator_lock_nested(rdev, i); + regulator_unlock_recursive(rdev, rdev->coupling_desc.n_coupled); + ww_acquire_fini(ww_ctx); } /** - * regulator_unlock_supply - unlock a regulator and its supplies - * @rdev: regulator source + * regulator_lock_dependent - lock regulator's suppliers and coupled regulators + * @rdev: regulator source + * @ww_ctx: w/w mutex acquire context + * + * This function as a wrapper on regulator_lock_recursive(), which locks + * all regulators related with rdev by coupling or suppling. */ -static void regulator_unlock_supply(struct regulator_dev *rdev) +static void regulator_lock_dependent(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - struct regulator *supply; + struct regulator_dev *new_contended_rdev = NULL; + struct regulator_dev *old_contended_rdev = NULL; + int err; + + mutex_lock(®ulator_list_mutex); + + ww_acquire_init(ww_ctx, ®ulator_ww_class); + + do { + if (new_contended_rdev) { + ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx); + old_contended_rdev = new_contended_rdev; + old_contended_rdev->ref_cnt++; + } + + err = regulator_lock_recursive(rdev, + &new_contended_rdev, + &old_contended_rdev, + ww_ctx); - while (1) { - regulator_unlock(rdev); - supply = rdev->supply; + if (old_contended_rdev) + regulator_unlock(old_contended_rdev); - if (!rdev->supply) - return; + } while (err == -EDEADLK); - rdev = supply->rdev; + ww_acquire_done(ww_ctx); + + mutex_unlock(®ulator_list_mutex); +} + +/** + * of_get_child_regulator - get a child regulator device node + * based on supply name + * @parent: Parent device node + * @prop_name: Combination regulator supply name and "-supply" + * + * Traverse all child nodes. + * Extract the child regulator device node corresponding to the supply name. + * returns the device node corresponding to the regulator if found, else + * returns NULL. + */ +static struct device_node *of_get_child_regulator(struct device_node *parent, + const char *prop_name) +{ + struct device_node *regnode = NULL; + struct device_node *child = NULL; + + for_each_child_of_node(parent, child) { + regnode = of_parse_phandle(child, prop_name, 0); + + if (!regnode) { + regnode = of_get_child_regulator(child, prop_name); + if (regnode) + return regnode; + } else { + return regnode; + } } + return NULL; } /** @@ -247,6 +427,10 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp regnode = of_parse_phandle(dev->of_node, prop_name, 0); if (!regnode) { + regnode = of_get_child_regulator(dev->of_node, prop_name); + if (regnode) + return regnode; + dev_dbg(dev, "Looking up %s property in node %pOF failed\n", prop_name, dev->of_node); return NULL; @@ -582,8 +766,10 @@ static ssize_t regulator_total_uA_show(struct device *dev, int uA = 0; regulator_lock(rdev); - list_for_each_entry(regulator, &rdev->consumer_list, list) - uA += regulator->uA_load; + list_for_each_entry(regulator, &rdev->consumer_list, list) { + if (regulator->enable_count) + uA += regulator->uA_load; + } regulator_unlock(rdev); return sprintf(buf, "%d\n", uA); } @@ -738,7 +924,7 @@ static int drms_uA_update(struct regulator_dev *rdev) int current_uA = 0, output_uV, input_uV, err; unsigned int mode; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); /* * first check to see if we can set modes at all, otherwise just @@ -756,8 +942,10 @@ static int drms_uA_update(struct regulator_dev *rdev) return -EINVAL; /* calc total requested load */ - list_for_each_entry(sibling, &rdev->consumer_list, list) - current_uA += sibling->uA_load; + list_for_each_entry(sibling, &rdev->consumer_list, list) { + if (sibling->enable_count) + current_uA += sibling->uA_load; + } current_uA += rdev->constraints->system_load; @@ -1156,17 +1344,12 @@ static int set_machine_constraints(struct regulator_dev *rdev, rdev_err(rdev, "failed to set initial mode: %d\n", ret); return ret; } - } - - /* If the constraints say the regulator should be on at this point - * and we have control then make sure it is enabled. - */ - if (rdev->constraints->always_on || rdev->constraints->boot_on) { - ret = _regulator_do_enable(rdev); - if (ret < 0 && ret != -EINVAL) { - rdev_err(rdev, "failed to enable\n"); - return ret; - } + } else if (rdev->constraints->system_load) { + /* + * We'll only apply the initial system load if an + * initial mode wasn't specified. + */ + drms_uA_update(rdev); } if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable) @@ -1214,6 +1397,27 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + /* If the constraints say the regulator should be on at this point + * and we have control then make sure it is enabled. + */ + if (rdev->constraints->always_on || rdev->constraints->boot_on) { + if (rdev->supply) { + ret = regulator_enable(rdev->supply); + if (ret < 0) { + _regulator_put(rdev->supply); + rdev->supply = NULL; + return ret; + } + } + + ret = _regulator_do_enable(rdev); + if (ret < 0 && ret != -EINVAL) { + rdev_err(rdev, "failed to enable\n"); + return ret; + } + rdev->use_count++; + } + print_constraints(rdev); return 0; } @@ -1628,8 +1832,12 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) return ret; } - /* Cascade always-on state to supply */ - if (_regulator_is_enabled(rdev)) { + /* + * In set_machine_constraints() we may have turned this regulator on + * but we couldn't propagate to the supply if it hadn't been resolved + * yet. Do it now. + */ + if (rdev->use_count) { ret = regulator_enable(rdev->supply); if (ret < 0) { _regulator_put(rdev->supply); @@ -1713,6 +1921,16 @@ struct regulator *_regulator_get(struct device *dev, const char *id, return regulator; } + mutex_lock(®ulator_list_mutex); + ret = (rdev->coupling_desc.n_resolved != rdev->coupling_desc.n_coupled); + mutex_unlock(®ulator_list_mutex); + + if (ret != 0) { + regulator = ERR_PTR(-EPROBE_DEFER); + put_device(&rdev->dev); + return regulator; + } + ret = regulator_resolve_supply(rdev); if (ret < 0) { regulator = ERR_PTR(ret); @@ -1832,6 +2050,9 @@ static void _regulator_put(struct regulator *regulator) lockdep_assert_held_once(®ulator_list_mutex); + /* Docs say you must disable before calling regulator_put() */ + WARN_ON(regulator->enable_count); + rdev = regulator->rdev; debugfs_remove_recursive(regulator->debugfs); @@ -2225,34 +2446,109 @@ static int _regulator_do_enable(struct regulator_dev *rdev) return 0; } +/** + * _regulator_handle_consumer_enable - handle that a consumer enabled + * @regulator: regulator source + * + * Some things on a regulator consumer (like the contribution towards total + * load on the regulator) only have an effect when the consumer wants the + * regulator enabled. Explained in example with two consumers of the same + * regulator: + * consumer A: set_load(100); => total load = 0 + * consumer A: regulator_enable(); => total load = 100 + * consumer B: set_load(1000); => total load = 100 + * consumer B: regulator_enable(); => total load = 1100 + * consumer A: regulator_disable(); => total_load = 1000 + * + * This function (together with _regulator_handle_consumer_disable) is + * responsible for keeping track of the refcount for a given regulator consumer + * and applying / unapplying these things. + * + * Returns 0 upon no error; -error upon error. + */ +static int _regulator_handle_consumer_enable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + lockdep_assert_held_once(&rdev->mutex.base); + + regulator->enable_count++; + if (regulator->uA_load && regulator->enable_count == 1) + return drms_uA_update(rdev); + + return 0; +} + +/** + * _regulator_handle_consumer_disable - handle that a consumer disabled + * @regulator: regulator source + * + * The opposite of _regulator_handle_consumer_enable(). + * + * Returns 0 upon no error; -error upon error. + */ +static int _regulator_handle_consumer_disable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + lockdep_assert_held_once(&rdev->mutex.base); + + if (!regulator->enable_count) { + rdev_err(rdev, "Underflow of regulator enable count\n"); + return -EINVAL; + } + + regulator->enable_count--; + if (regulator->uA_load && regulator->enable_count == 0) + return drms_uA_update(rdev); + + return 0; +} + /* locks held by regulator_enable() */ -static int _regulator_enable(struct regulator_dev *rdev) +static int _regulator_enable(struct regulator *regulator) { + struct regulator_dev *rdev = regulator->rdev; int ret; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); - /* check voltage and requested load before enabling */ - if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) - drms_uA_update(rdev); + if (rdev->use_count == 0 && rdev->supply) { + ret = _regulator_enable(rdev->supply); + if (ret < 0) + return ret; + } + + /* balance only if there are regulators coupled */ + if (rdev->coupling_desc.n_coupled > 1) { + ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); + if (ret < 0) + goto err_disable_supply; + } + + ret = _regulator_handle_consumer_enable(regulator); + if (ret < 0) + goto err_disable_supply; if (rdev->use_count == 0) { /* The regulator may on if it's not switchable or left on */ ret = _regulator_is_enabled(rdev); if (ret == -EINVAL || ret == 0) { if (!regulator_ops_is_valid(rdev, - REGULATOR_CHANGE_STATUS)) - return -EPERM; + REGULATOR_CHANGE_STATUS)) { + ret = -EPERM; + goto err_consumer_disable; + } ret = _regulator_do_enable(rdev); if (ret < 0) - return ret; + goto err_consumer_disable; _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE, NULL); } else if (ret < 0) { rdev_err(rdev, "is_enabled() failed: %d\n", ret); - return ret; + goto err_consumer_disable; } /* Fallthrough on positive return values - already enabled */ } @@ -2260,6 +2556,15 @@ static int _regulator_enable(struct regulator_dev *rdev) rdev->use_count++; return 0; + +err_consumer_disable: + _regulator_handle_consumer_disable(regulator); + +err_disable_supply: + if (rdev->use_count == 0 && rdev->supply) + _regulator_disable(rdev->supply); + + return ret; } /** @@ -2276,23 +2581,12 @@ static int _regulator_enable(struct regulator_dev *rdev) int regulator_enable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - int ret = 0; - - if (regulator->always_on) - return 0; - - if (rdev->supply) { - ret = regulator_enable(rdev->supply); - if (ret != 0) - return ret; - } - - mutex_lock(&rdev->mutex); - ret = _regulator_enable(rdev); - mutex_unlock(&rdev->mutex); + struct ww_acquire_ctx ww_ctx; + int ret; - if (ret != 0 && rdev->supply) - regulator_disable(rdev->supply); + regulator_lock_dependent(rdev, &ww_ctx); + ret = _regulator_enable(regulator); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2330,11 +2624,12 @@ static int _regulator_do_disable(struct regulator_dev *rdev) } /* locks held by regulator_disable() */ -static int _regulator_disable(struct regulator_dev *rdev) +static int _regulator_disable(struct regulator *regulator) { + struct regulator_dev *rdev = regulator->rdev; int ret = 0; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", rdev_get_name(rdev))) @@ -2366,12 +2661,18 @@ static int _regulator_disable(struct regulator_dev *rdev) rdev->use_count = 0; } else if (rdev->use_count > 1) { - if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) - drms_uA_update(rdev); - rdev->use_count--; } + if (ret == 0) + ret = _regulator_handle_consumer_disable(regulator); + + if (ret == 0 && rdev->coupling_desc.n_coupled > 1) + ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); + + if (ret == 0 && rdev->use_count == 0 && rdev->supply) + ret = _regulator_disable(rdev->supply); + return ret; } @@ -2390,17 +2691,12 @@ static int _regulator_disable(struct regulator_dev *rdev) int regulator_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - int ret = 0; - - if (regulator->always_on) - return 0; - - mutex_lock(&rdev->mutex); - ret = _regulator_disable(rdev); - mutex_unlock(&rdev->mutex); + struct ww_acquire_ctx ww_ctx; + int ret; - if (ret == 0 && rdev->supply) - regulator_disable(rdev->supply); + regulator_lock_dependent(rdev, &ww_ctx); + ret = _regulator_disable(regulator); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2411,7 +2707,7 @@ static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | REGULATOR_EVENT_PRE_DISABLE, NULL); @@ -2444,16 +2740,25 @@ static int _regulator_force_disable(struct regulator_dev *rdev) int regulator_force_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; + struct ww_acquire_ctx ww_ctx; int ret; - mutex_lock(&rdev->mutex); - regulator->uA_load = 0; + regulator_lock_dependent(rdev, &ww_ctx); + ret = _regulator_force_disable(regulator->rdev); - mutex_unlock(&rdev->mutex); - if (rdev->supply) - while (rdev->open_count--) - regulator_disable(rdev->supply); + if (rdev->coupling_desc.n_coupled > 1) + regulator_balance_voltage(rdev, PM_SUSPEND_ON); + + if (regulator->uA_load) { + regulator->uA_load = 0; + ret = drms_uA_update(rdev); + } + + if (rdev->use_count != 0 && rdev->supply) + _regulator_disable(rdev->supply); + + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2463,14 +2768,12 @@ static void regulator_disable_work(struct work_struct *work) { struct regulator_dev *rdev = container_of(work, struct regulator_dev, disable_work.work); + struct ww_acquire_ctx ww_ctx; int count, i, ret; + struct regulator *regulator; + int total_count = 0; - regulator_lock(rdev); - - BUG_ON(!rdev->deferred_disables); - - count = rdev->deferred_disables; - rdev->deferred_disables = 0; + regulator_lock_dependent(rdev, &ww_ctx); /* * Workqueue functions queue the new work instance while the previous @@ -2480,23 +2783,27 @@ static void regulator_disable_work(struct work_struct *work) */ cancel_delayed_work(&rdev->disable_work); - for (i = 0; i < count; i++) { - ret = _regulator_disable(rdev); - if (ret != 0) - rdev_err(rdev, "Deferred disable failed: %d\n", ret); - } + list_for_each_entry(regulator, &rdev->consumer_list, list) { + count = regulator->deferred_disables; - regulator_unlock(rdev); + if (!count) + continue; + + total_count += count; + regulator->deferred_disables = 0; - if (rdev->supply) { for (i = 0; i < count; i++) { - ret = regulator_disable(rdev->supply); - if (ret != 0) { - rdev_err(rdev, - "Supply disable failed: %d\n", ret); - } + ret = _regulator_disable(regulator); + if (ret != 0) + rdev_err(rdev, "Deferred disable failed: %d\n", ret); } } + WARN_ON(!total_count); + + if (rdev->coupling_desc.n_coupled > 1) + regulator_balance_voltage(rdev, PM_SUSPEND_ON); + + regulator_unlock_dependent(rdev, &ww_ctx); } /** @@ -2515,14 +2822,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) { struct regulator_dev *rdev = regulator->rdev; - if (regulator->always_on) - return 0; - if (!ms) return regulator_disable(regulator); regulator_lock(rdev); - rdev->deferred_disables++; + regulator->deferred_disables++; mod_delayed_work(system_power_efficient_wq, &rdev->disable_work, msecs_to_jiffies(ms)); regulator_unlock(rdev); @@ -2597,9 +2901,9 @@ int regulator_is_enabled(struct regulator *regulator) if (regulator->always_on) return 1; - mutex_lock(®ulator->rdev->mutex); + regulator_lock(regulator->rdev); ret = _regulator_is_enabled(regulator->rdev); - mutex_unlock(®ulator->rdev->mutex); + regulator_unlock(regulator->rdev); return ret; } @@ -3013,8 +3317,6 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, int ret = 0; int old_min_uV, old_max_uV; int current_uV; - int best_supply_uV = 0; - int supply_change_uV = 0; /* If we're setting the same range as last time the change * should be a noop (some cpufreq implementations use the same @@ -3054,10 +3356,27 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, voltage->min_uV = min_uV; voltage->max_uV = max_uV; - ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state); + /* for not coupled regulators this will just set the voltage */ + ret = regulator_balance_voltage(rdev, state); if (ret < 0) goto out2; +out: + return 0; +out2: + voltage->min_uV = old_min_uV; + voltage->max_uV = old_max_uV; + + return ret; +} + +static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, + int max_uV, suspend_state_t state) +{ + int best_supply_uV = 0; + int supply_change_uV = 0; + int ret; + if (rdev->supply && regulator_ops_is_valid(rdev->supply->rdev, REGULATOR_CHANGE_VOLTAGE) && @@ -3069,13 +3388,13 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, selector = regulator_map_voltage(rdev, min_uV, max_uV); if (selector < 0) { ret = selector; - goto out2; + goto out; } best_supply_uV = _regulator_list_voltage(rdev, selector, 0); if (best_supply_uV < 0) { ret = best_supply_uV; - goto out2; + goto out; } best_supply_uV += rdev->desc->min_dropout_uV; @@ -3083,7 +3402,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, current_supply_uV = _regulator_get_voltage(rdev->supply->rdev); if (current_supply_uV < 0) { ret = current_supply_uV; - goto out2; + goto out; } supply_change_uV = best_supply_uV - current_supply_uV; @@ -3095,7 +3414,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, if (ret) { dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n", ret); - goto out2; + goto out; } } @@ -3105,7 +3424,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, ret = _regulator_do_set_suspend_voltage(rdev, min_uV, max_uV, state); if (ret < 0) - goto out2; + goto out; if (supply_change_uV < 0) { ret = regulator_set_voltage_unlocked(rdev->supply, @@ -3119,10 +3438,273 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, out: return ret; -out2: - voltage->min_uV = old_min_uV; - voltage->max_uV = old_max_uV; +} + +static int regulator_limit_voltage_step(struct regulator_dev *rdev, + int *current_uV, int *min_uV) +{ + struct regulation_constraints *constraints = rdev->constraints; + + /* Limit voltage change only if necessary */ + if (!constraints->max_uV_step || !_regulator_is_enabled(rdev)) + return 1; + + if (*current_uV < 0) { + *current_uV = _regulator_get_voltage(rdev); + + if (*current_uV < 0) + return *current_uV; + } + + if (abs(*current_uV - *min_uV) <= constraints->max_uV_step) + return 1; + + /* Clamp target voltage within the given step */ + if (*current_uV < *min_uV) + *min_uV = min(*current_uV + constraints->max_uV_step, + *min_uV); + else + *min_uV = max(*current_uV - constraints->max_uV_step, + *min_uV); + + return 0; +} + +static int regulator_get_optimal_voltage(struct regulator_dev *rdev, + int *current_uV, + int *min_uV, int *max_uV, + suspend_state_t state, + int n_coupled) +{ + struct coupling_desc *c_desc = &rdev->coupling_desc; + struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; + struct regulation_constraints *constraints = rdev->constraints; + int max_spread = constraints->max_spread; + int desired_min_uV = 0, desired_max_uV = INT_MAX; + int max_current_uV = 0, min_current_uV = INT_MAX; + int highest_min_uV = 0, target_uV, possible_uV; + int i, ret; + bool done; + + *current_uV = -1; + + /* + * If there are no coupled regulators, simply set the voltage + * demanded by consumers. + */ + if (n_coupled == 1) { + /* + * If consumers don't provide any demands, set voltage + * to min_uV + */ + desired_min_uV = constraints->min_uV; + desired_max_uV = constraints->max_uV; + + ret = regulator_check_consumers(rdev, + &desired_min_uV, + &desired_max_uV, state); + if (ret < 0) + return ret; + + possible_uV = desired_min_uV; + done = true; + + goto finish; + } + + /* Find highest min desired voltage */ + for (i = 0; i < n_coupled; i++) { + int tmp_min = 0; + int tmp_max = INT_MAX; + + lockdep_assert_held_once(&c_rdevs[i]->mutex.base); + + ret = regulator_check_consumers(c_rdevs[i], + &tmp_min, + &tmp_max, state); + if (ret < 0) + return ret; + + ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max); + if (ret < 0) + return ret; + + highest_min_uV = max(highest_min_uV, tmp_min); + + if (i == 0) { + desired_min_uV = tmp_min; + desired_max_uV = tmp_max; + } + } + + /* + * Let target_uV be equal to the desired one if possible. + * If not, set it to minimum voltage, allowed by other coupled + * regulators. + */ + target_uV = max(desired_min_uV, highest_min_uV - max_spread); + + /* + * Find min and max voltages, which currently aren't violating + * max_spread. + */ + for (i = 1; i < n_coupled; i++) { + int tmp_act; + + if (!_regulator_is_enabled(c_rdevs[i])) + continue; + + tmp_act = _regulator_get_voltage(c_rdevs[i]); + if (tmp_act < 0) + return tmp_act; + + min_current_uV = min(tmp_act, min_current_uV); + max_current_uV = max(tmp_act, max_current_uV); + } + + /* There aren't any other regulators enabled */ + if (max_current_uV == 0) { + possible_uV = target_uV; + } else { + /* + * Correct target voltage, so as it currently isn't + * violating max_spread + */ + possible_uV = max(target_uV, max_current_uV - max_spread); + possible_uV = min(possible_uV, min_current_uV + max_spread); + } + + if (possible_uV > desired_max_uV) + return -EINVAL; + + done = (possible_uV == target_uV); + desired_min_uV = possible_uV; + +finish: + /* Apply max_uV_step constraint if necessary */ + if (state == PM_SUSPEND_ON) { + ret = regulator_limit_voltage_step(rdev, current_uV, + &desired_min_uV); + if (ret < 0) + return ret; + + if (ret == 0) + done = false; + } + /* Set current_uV if wasn't done earlier in the code and if necessary */ + if (n_coupled > 1 && *current_uV == -1) { + + if (_regulator_is_enabled(rdev)) { + ret = _regulator_get_voltage(rdev); + if (ret < 0) + return ret; + + *current_uV = ret; + } else { + *current_uV = desired_min_uV; + } + } + + *min_uV = desired_min_uV; + *max_uV = desired_max_uV; + + return done; +} + +static int regulator_balance_voltage(struct regulator_dev *rdev, + suspend_state_t state) +{ + struct regulator_dev **c_rdevs; + struct regulator_dev *best_rdev; + struct coupling_desc *c_desc = &rdev->coupling_desc; + int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev; + bool best_c_rdev_done, c_rdev_done[MAX_COUPLED]; + unsigned int delta, best_delta; + + c_rdevs = c_desc->coupled_rdevs; + n_coupled = c_desc->n_coupled; + + /* + * If system is in a state other than PM_SUSPEND_ON, don't check + * other coupled regulators. + */ + if (state != PM_SUSPEND_ON) + n_coupled = 1; + + if (c_desc->n_resolved < n_coupled) { + rdev_err(rdev, "Not all coupled regulators registered\n"); + return -EPERM; + } + + for (i = 0; i < n_coupled; i++) + c_rdev_done[i] = false; + + /* + * Find the best possible voltage change on each loop. Leave the loop + * if there isn't any possible change. + */ + do { + best_c_rdev_done = false; + best_delta = 0; + best_min_uV = 0; + best_max_uV = 0; + best_c_rdev = 0; + best_rdev = NULL; + + /* + * Find highest difference between optimal voltage + * and current voltage. + */ + for (i = 0; i < n_coupled; i++) { + /* + * optimal_uV is the best voltage that can be set for + * i-th regulator at the moment without violating + * max_spread constraint in order to balance + * the coupled voltages. + */ + int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; + + if (c_rdev_done[i]) + continue; + + ret = regulator_get_optimal_voltage(c_rdevs[i], + ¤t_uV, + &optimal_uV, + &optimal_max_uV, + state, n_coupled); + if (ret < 0) + goto out; + + delta = abs(optimal_uV - current_uV); + + if (delta && best_delta <= delta) { + best_c_rdev_done = ret; + best_delta = delta; + best_rdev = c_rdevs[i]; + best_min_uV = optimal_uV; + best_max_uV = optimal_max_uV; + best_c_rdev = i; + } + } + + /* Nothing to change, return successfully */ + if (!best_rdev) { + ret = 0; + goto out; + } + + ret = regulator_set_voltage_rdev(best_rdev, best_min_uV, + best_max_uV, state); + + if (ret < 0) + goto out; + + c_rdev_done[best_c_rdev] = best_c_rdev_done; + + } while (n_coupled > 1); + +out: return ret; } @@ -3146,14 +3728,15 @@ out2: */ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) { - int ret = 0; + struct ww_acquire_ctx ww_ctx; + int ret; - regulator_lock_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV, PM_SUSPEND_ON); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3225,18 +3808,19 @@ static int _regulator_set_suspend_voltage(struct regulator *regulator, int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV, int max_uV, suspend_state_t state) { - int ret = 0; + struct ww_acquire_ctx ww_ctx; + int ret; /* PM_SUSPEND_ON is handled by regulator_set_voltage() */ if (regulator_check_states(state) || state == PM_SUSPEND_ON) return -EINVAL; - regulator_lock_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = _regulator_set_suspend_voltage(regulator, min_uV, max_uV, state); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3426,13 +4010,12 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) */ int regulator_get_voltage(struct regulator *regulator) { + struct ww_acquire_ctx ww_ctx; int ret; - regulator_lock_supply(regulator->rdev); - + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = _regulator_get_voltage(regulator->rdev); - - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3650,16 +4233,30 @@ EXPORT_SYMBOL_GPL(regulator_get_error_flags); * DRMS will sum the total requested load on the regulator and change * to the most efficient operating mode if platform constraints allow. * + * NOTE: when a regulator consumer requests to have a regulator + * disabled then any load that consumer requested no longer counts + * toward the total requested load. If the regulator is re-enabled + * then the previously requested load will start counting again. + * + * If a regulator is an always-on regulator then an individual consumer's + * load will still be removed if that consumer is fully disabled. + * * On error a negative errno is returned. */ int regulator_set_load(struct regulator *regulator, int uA_load) { struct regulator_dev *rdev = regulator->rdev; - int ret; + int old_uA_load; + int ret = 0; regulator_lock(rdev); + old_uA_load = regulator->uA_load; regulator->uA_load = uA_load; - ret = drms_uA_update(rdev); + if (regulator->enable_count && old_uA_load != uA_load) { + ret = drms_uA_update(rdev); + if (ret < 0) + regulator->uA_load = old_uA_load; + } regulator_unlock(rdev); return ret; @@ -3830,11 +4427,8 @@ int regulator_bulk_enable(int num_consumers, int ret = 0; for (i = 0; i < num_consumers; i++) { - if (consumers[i].consumer->always_on) - consumers[i].ret = 0; - else - async_schedule_domain(regulator_bulk_enable_async, - &consumers[i], &async_domain); + async_schedule_domain(regulator_bulk_enable_async, + &consumers[i], &async_domain); } async_synchronize_full_domain(&async_domain); @@ -3968,7 +4562,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); _notifier_call_chain(rdev, event, data); return NOTIFY_DONE; @@ -4070,10 +4664,6 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_bypass.attr) return ops->get_bypass ? mode : 0; - /* some attributes are type-specific */ - if (attr == &dev_attr_requested_microamps.attr) - return rdev->desc->type == REGULATOR_CURRENT ? mode : 0; - /* constraints need specific supporting methods */ if (attr == &dev_attr_min_microvolts.attr || attr == &dev_attr_max_microvolts.attr) @@ -4157,7 +4747,7 @@ static int regulator_register_resolve_supply(struct device *dev, void *data) return 0; } -static int regulator_fill_coupling_array(struct regulator_dev *rdev) +static void regulator_resolve_coupling(struct regulator_dev *rdev) { struct coupling_desc *c_desc = &rdev->coupling_desc; int n_coupled = c_desc->n_coupled; @@ -4171,33 +4761,58 @@ static int regulator_fill_coupling_array(struct regulator_dev *rdev) c_rdev = of_parse_coupled_regulator(rdev, i - 1); - if (c_rdev) { - c_desc->coupled_rdevs[i] = c_rdev; - c_desc->n_resolved++; - } - } + if (!c_rdev) + continue; - if (rdev->coupling_desc.n_resolved < n_coupled) - return -1; - else - return 0; + regulator_lock(c_rdev); + + c_desc->coupled_rdevs[i] = c_rdev; + c_desc->n_resolved++; + + regulator_unlock(c_rdev); + + regulator_resolve_coupling(c_rdev); + } } -static int regulator_register_fill_coupling_array(struct device *dev, - void *data) +static void regulator_remove_coupling(struct regulator_dev *rdev) { - struct regulator_dev *rdev = dev_to_rdev(dev); + struct coupling_desc *__c_desc, *c_desc = &rdev->coupling_desc; + struct regulator_dev *__c_rdev, *c_rdev; + unsigned int __n_coupled, n_coupled; + int i, k; - if (!IS_ENABLED(CONFIG_OF)) - return 0; + n_coupled = c_desc->n_coupled; - if (regulator_fill_coupling_array(rdev)) - rdev_dbg(rdev, "unable to resolve coupling\n"); + for (i = 1; i < n_coupled; i++) { + c_rdev = c_desc->coupled_rdevs[i]; - return 0; + if (!c_rdev) + continue; + + regulator_lock(c_rdev); + + __c_desc = &c_rdev->coupling_desc; + __n_coupled = __c_desc->n_coupled; + + for (k = 1; k < __n_coupled; k++) { + __c_rdev = __c_desc->coupled_rdevs[k]; + + if (__c_rdev == rdev) { + __c_desc->coupled_rdevs[k] = NULL; + __c_desc->n_resolved--; + break; + } + } + + regulator_unlock(c_rdev); + + c_desc->coupled_rdevs[i] = NULL; + c_desc->n_resolved--; + } } -static int regulator_resolve_coupling(struct regulator_dev *rdev) +static int regulator_init_coupling(struct regulator_dev *rdev) { int n_phandles; @@ -4237,13 +4852,6 @@ static int regulator_resolve_coupling(struct regulator_dev *rdev) if (!of_check_coupling_data(rdev)) return -EPERM; - /* - * After everything has been checked, try to fill rdevs array - * with pointers to regulators parsed from device tree. If some - * regulators are not registered yet, retry in late init call - */ - regulator_fill_coupling_array(rdev); - return 0; } @@ -4265,21 +4873,33 @@ regulator_register(const struct regulator_desc *regulator_desc, struct regulator_config *config = NULL; static atomic_t regulator_no = ATOMIC_INIT(-1); struct regulator_dev *rdev; + bool dangling_cfg_gpiod = false; + bool dangling_of_gpiod = false; struct device *dev; int ret, i; - if (regulator_desc == NULL || cfg == NULL) + if (cfg == NULL) return ERR_PTR(-EINVAL); + if (cfg->ena_gpiod) + dangling_cfg_gpiod = true; + if (regulator_desc == NULL) { + ret = -EINVAL; + goto rinse; + } dev = cfg->dev; WARN_ON(!dev); - if (regulator_desc->name == NULL || regulator_desc->ops == NULL) - return ERR_PTR(-EINVAL); + if (regulator_desc->name == NULL || regulator_desc->ops == NULL) { + ret = -EINVAL; + goto rinse; + } if (regulator_desc->type != REGULATOR_VOLTAGE && - regulator_desc->type != REGULATOR_CURRENT) - return ERR_PTR(-EINVAL); + regulator_desc->type != REGULATOR_CURRENT) { + ret = -EINVAL; + goto rinse; + } /* Only one of each should be implemented */ WARN_ON(regulator_desc->ops->get_voltage && @@ -4290,16 +4910,20 @@ regulator_register(const struct regulator_desc *regulator_desc, /* If we're using selectors we must implement list_voltage. */ if (regulator_desc->ops->get_voltage_sel && !regulator_desc->ops->list_voltage) { - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto rinse; } if (regulator_desc->ops->set_voltage_sel && !regulator_desc->ops->list_voltage) { - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto rinse; } rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); - if (rdev == NULL) - return ERR_PTR(-ENOMEM); + if (rdev == NULL) { + ret = -ENOMEM; + goto rinse; + } /* * Duplicate the config so the driver could override it after @@ -4308,17 +4932,28 @@ regulator_register(const struct regulator_desc *regulator_desc, config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL); if (config == NULL) { kfree(rdev); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto rinse; } init_data = regulator_of_get_init_data(dev, regulator_desc, config, &rdev->dev.of_node); + /* + * We need to keep track of any GPIO descriptor coming from the + * device tree until we have handled it over to the core. If the + * config that was passed in to this function DOES NOT contain + * a descriptor, and the config after this call DOES contain + * a descriptor, we definately got one from parsing the device + * tree. + */ + if (!cfg->ena_gpiod && config->ena_gpiod) + dangling_of_gpiod = true; if (!init_data) { init_data = config->init_data; rdev->dev.of_node = of_node_get(config->of_node); } - mutex_init(&rdev->mutex); + ww_mutex_init(&rdev->mutex, ®ulator_ww_class); rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; @@ -4351,6 +4986,9 @@ regulator_register(const struct regulator_desc *regulator_desc, config->ena_gpio, ret); goto clean; } + /* The regulator core took over the GPIO descriptor */ + dangling_cfg_gpiod = false; + dangling_of_gpiod = false; } /* register with sysfs */ @@ -4380,11 +5018,8 @@ regulator_register(const struct regulator_desc *regulator_desc, if (ret < 0) goto wash; - mutex_lock(®ulator_list_mutex); - ret = regulator_resolve_coupling(rdev); - mutex_unlock(®ulator_list_mutex); - - if (ret != 0) + ret = regulator_init_coupling(rdev); + if (ret < 0) goto wash; /* add consumers devices */ @@ -4418,6 +5053,11 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev_init_debugfs(rdev); + /* try to resolve regulators coupling since a new one was registered */ + mutex_lock(®ulator_list_mutex); + regulator_resolve_coupling(rdev); + mutex_unlock(®ulator_list_mutex); + /* try to resolve regulators supply since a new one was registered */ class_for_each_device(®ulator_class, NULL, NULL, regulator_register_resolve_supply); @@ -4434,8 +5074,13 @@ wash: regulator_ena_gpio_free(rdev); mutex_unlock(®ulator_list_mutex); clean: + if (dangling_of_gpiod) + gpiod_put(config->ena_gpiod); kfree(rdev); kfree(config); +rinse: + if (dangling_cfg_gpiod) + gpiod_put(cfg->ena_gpiod); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(regulator_register); @@ -4456,15 +5101,19 @@ void regulator_unregister(struct regulator_dev *rdev) regulator_disable(rdev->supply); regulator_put(rdev->supply); } + mutex_lock(®ulator_list_mutex); + debugfs_remove_recursive(rdev->debugfs); flush_work(&rdev->disable_work.work); WARN_ON(rdev->open_count); + regulator_remove_coupling(rdev); unset_regulator_supplies(rdev); list_del(&rdev->list); regulator_ena_gpio_free(rdev); - mutex_unlock(®ulator_list_mutex); device_unregister(&rdev->dev); + + mutex_unlock(®ulator_list_mutex); } EXPORT_SYMBOL_GPL(regulator_unregister); @@ -4621,23 +5270,8 @@ static int supply_map_show(struct seq_file *sf, void *data) return 0; } +DEFINE_SHOW_ATTRIBUTE(supply_map); -static int supply_map_open(struct inode *inode, struct file *file) -{ - return single_open(file, supply_map_show, inode->i_private); -} -#endif - -static const struct file_operations supply_map_fops = { -#ifdef CONFIG_DEBUG_FS - .open = supply_map_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -#endif -}; - -#ifdef CONFIG_DEBUG_FS struct summary_data { struct seq_file *s; struct regulator_dev *parent; @@ -4672,8 +5306,6 @@ static void regulator_summary_show_subtree(struct seq_file *s, if (!rdev) return; - regulator_lock_nested(rdev, level); - opmode = _regulator_get_mode_unlocked(rdev); seq_printf(s, "%*s%-*s %3d %4d %6d %7s ", level * 3 + 1, "", @@ -4712,8 +5344,11 @@ static void regulator_summary_show_subtree(struct seq_file *s, switch (rdev->desc->type) { case REGULATOR_VOLTAGE: - seq_printf(s, "%37dmA %5dmV %5dmV", + seq_printf(s, "%3d %33dmA%c%5dmV %5dmV", + consumer->enable_count, consumer->uA_load / 1000, + consumer->uA_load && !consumer->enable_count ? + '*' : ' ', consumer->voltage[PM_SUSPEND_ON].min_uV / 1000, consumer->voltage[PM_SUSPEND_ON].max_uV / 1000); break; @@ -4730,8 +5365,105 @@ static void regulator_summary_show_subtree(struct seq_file *s, class_for_each_device(®ulator_class, NULL, &summary_data, regulator_summary_show_children); +} + +struct summary_lock_data { + struct ww_acquire_ctx *ww_ctx; + struct regulator_dev **new_contended_rdev; + struct regulator_dev **old_contended_rdev; +}; + +static int regulator_summary_lock_one(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + struct summary_lock_data *lock_data = data; + int ret = 0; + + if (rdev != *lock_data->old_contended_rdev) { + ret = regulator_lock_nested(rdev, lock_data->ww_ctx); + + if (ret == -EDEADLK) + *lock_data->new_contended_rdev = rdev; + else + WARN_ON_ONCE(ret); + } else { + *lock_data->old_contended_rdev = NULL; + } + + return ret; +} + +static int regulator_summary_unlock_one(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + struct summary_lock_data *lock_data = data; + + if (lock_data) { + if (rdev == *lock_data->new_contended_rdev) + return -EDEADLK; + } regulator_unlock(rdev); + + return 0; +} + +static int regulator_summary_lock_all(struct ww_acquire_ctx *ww_ctx, + struct regulator_dev **new_contended_rdev, + struct regulator_dev **old_contended_rdev) +{ + struct summary_lock_data lock_data; + int ret; + + lock_data.ww_ctx = ww_ctx; + lock_data.new_contended_rdev = new_contended_rdev; + lock_data.old_contended_rdev = old_contended_rdev; + + ret = class_for_each_device(®ulator_class, NULL, &lock_data, + regulator_summary_lock_one); + if (ret) + class_for_each_device(®ulator_class, NULL, &lock_data, + regulator_summary_unlock_one); + + return ret; +} + +static void regulator_summary_lock(struct ww_acquire_ctx *ww_ctx) +{ + struct regulator_dev *new_contended_rdev = NULL; + struct regulator_dev *old_contended_rdev = NULL; + int err; + + mutex_lock(®ulator_list_mutex); + + ww_acquire_init(ww_ctx, ®ulator_ww_class); + + do { + if (new_contended_rdev) { + ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx); + old_contended_rdev = new_contended_rdev; + old_contended_rdev->ref_cnt++; + } + + err = regulator_summary_lock_all(ww_ctx, + &new_contended_rdev, + &old_contended_rdev); + + if (old_contended_rdev) + regulator_unlock(old_contended_rdev); + + } while (err == -EDEADLK); + + ww_acquire_done(ww_ctx); +} + +static void regulator_summary_unlock(struct ww_acquire_ctx *ww_ctx) +{ + class_for_each_device(®ulator_class, NULL, NULL, + regulator_summary_unlock_one); + ww_acquire_fini(ww_ctx); + + mutex_unlock(®ulator_list_mutex); } static int regulator_summary_show_roots(struct device *dev, void *data) @@ -4747,29 +5479,22 @@ static int regulator_summary_show_roots(struct device *dev, void *data) static int regulator_summary_show(struct seq_file *s, void *data) { + struct ww_acquire_ctx ww_ctx; + seq_puts(s, " regulator use open bypass opmode voltage current min max\n"); seq_puts(s, "---------------------------------------------------------------------------------------\n"); + regulator_summary_lock(&ww_ctx); + class_for_each_device(®ulator_class, NULL, s, regulator_summary_show_roots); - return 0; -} + regulator_summary_unlock(&ww_ctx); -static int regulator_summary_open(struct inode *inode, struct file *file) -{ - return single_open(file, regulator_summary_show, inode->i_private); + return 0; } -#endif - -static const struct file_operations regulator_summary_fops = { -#ifdef CONFIG_DEBUG_FS - .open = regulator_summary_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -#endif -}; +DEFINE_SHOW_ATTRIBUTE(regulator_summary); +#endif /* CONFIG_DEBUG_FS */ static int __init regulator_init(void) { @@ -4781,12 +5506,13 @@ static int __init regulator_init(void) if (!debugfs_root) pr_warn("regulator: Failed to create debugfs directory\n"); +#ifdef CONFIG_DEBUG_FS debugfs_create_file("supply_map", 0444, debugfs_root, NULL, &supply_map_fops); debugfs_create_file("regulator_summary", 0444, debugfs_root, NULL, ®ulator_summary_fops); - +#endif regulator_dummy_init(); return ret; @@ -4873,9 +5599,6 @@ static int __init regulator_init_complete(void) class_for_each_device(®ulator_class, NULL, NULL, regulator_late_cleanup); - class_for_each_device(®ulator_class, NULL, NULL, - regulator_register_fill_coupling_array); - return 0; } late_initcall_sync(regulator_init_complete); diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 37e4025203e3..207cb3859dcc 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -435,7 +435,7 @@ static int da9052_regulator_probe(struct platform_device *pdev) return -ENODEV; for_each_child_of_node(nproot, np) { - if (!of_node_cmp(np->name, + if (of_node_name_eq(np, regulator->info->reg_desc.name)) { config.init_data = of_get_regulator_init_data( &pdev->dev, np, diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index d0496d6b0934..84dba64ed11e 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -131,7 +131,7 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) if (error < 0) goto error_i2c; - mutex_lock(&chip->rdev->mutex); + regulator_lock(chip->rdev); if (val & DA9210_E_OVCURR) { regulator_notifier_call_chain(chip->rdev, @@ -157,7 +157,7 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) handled |= DA9210_E_VMAX; } - mutex_unlock(&chip->rdev->mutex); + regulator_unlock(chip->rdev); if (handled) { /* Clear handled events */ diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 8f68c7a05d27..109ee12d4362 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -389,6 +389,12 @@ static int da9211_regulator_init(struct da9211 *chip) else config.ena_gpiod = NULL; + /* + * Hand the GPIO descriptor management over to the regulator + * core, remove it from GPIO devres management. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(chip->dev, config.ena_gpiod); chip->rdev[i] = devm_regulator_register(chip->dev, &da9211_regulators[i], &config); if (IS_ERR(chip->rdev[i])) { diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index 8976141c1438..308e3ff0a1bd 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -75,7 +75,7 @@ static struct ux500_regulator_debug { u8 *state_after_suspend; } rdebug; -static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) +static int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p) { /* print power state count */ seq_printf(s, "ux500-regulator power state count: %i\n", @@ -83,23 +83,9 @@ static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) return 0; } +DEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt); -static int ux500_regulator_power_state_cnt_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ux500_regulator_power_state_cnt_print, - inode->i_private); -} - -static const struct file_operations ux500_regulator_power_state_cnt_fops = { - .open = ux500_regulator_power_state_cnt_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ux500_regulator_status_print(struct seq_file *s, void *p) +static int ux500_regulator_status_show(struct seq_file *s, void *p) { int i; @@ -122,20 +108,7 @@ static int ux500_regulator_status_print(struct seq_file *s, void *p) return 0; } - -static int ux500_regulator_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, ux500_regulator_status_print, - inode->i_private); -} - -static const struct file_operations ux500_regulator_status_fops = { - .open = ux500_regulator_status_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ux500_regulator_status); int __attribute__((weak)) dbx500_regulator_testcase( struct dbx500_regulator_info *regulator_info, diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index ccc29038f19a..9abdb9130766 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -183,7 +183,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) */ gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; - cfg.ena_gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, gflags); + /* + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. + */ + cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags); if (IS_ERR(cfg.ena_gpiod)) return PTR_ERR(cfg.ena_gpiod); diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 943926a156f2..6017f15c5d75 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -42,6 +42,8 @@ struct regulator { unsigned int always_on:1; unsigned int bypass:1; int uA_load; + unsigned int enable_count; + unsigned int deferred_disables; struct regulator_voltage voltage[REGULATOR_STATES_NUM]; const char *supply_name; struct device_attribute dev_attr; diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c index bbedb08d257b..8c0e8419c43f 100644 --- a/drivers/regulator/lm363x-regulator.c +++ b/drivers/regulator/lm363x-regulator.c @@ -224,13 +224,15 @@ static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev, /* * Check LCM_EN1/2_GPIO is configured. * Those pins are used for enabling VPOS/VNEG LDOs. + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. */ switch (id) { case LM3632_LDO_POS: - return devm_gpiod_get_index_optional(dev, "enable", 0, + return gpiod_get_index_optional(dev, "enable", 0, GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); case LM3632_LDO_NEG: - return devm_gpiod_get_index_optional(dev, "enable", 1, + return gpiod_get_index_optional(dev, "enable", 1, GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); default: return NULL; @@ -263,6 +265,8 @@ static int lm363x_regulator_probe(struct platform_device *pdev) LM3632_EXT_EN_MASK, LM3632_EXT_EN_MASK); if (ret) { + if (gpiod) + gpiod_put(gpiod); dev_err(dev, "External pin err: %d\n", ret); return ret; } diff --git a/drivers/regulator/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c index 2b5073b9ff86..5a89e6d4b9a6 100644 --- a/drivers/regulator/lochnagar-regulator.c +++ b/drivers/regulator/lochnagar-regulator.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> @@ -20,6 +21,8 @@ #include <linux/regulator/of_regulator.h> #include <linux/mfd/lochnagar.h> +#include <linux/mfd/lochnagar1_regs.h> +#include <linux/mfd/lochnagar2_regs.h> static const struct regulator_ops lochnagar_micvdd_ops = { .enable = regulator_enable_regmap, @@ -212,28 +215,52 @@ static const struct regulator_desc lochnagar_regulators[] = { }, }; +static const struct of_device_id lochnagar_of_match[] = { + { + .compatible = "cirrus,lochnagar2-micvdd", + .data = &lochnagar_regulators[LOCHNAGAR_MICVDD], + }, + { + .compatible = "cirrus,lochnagar2-mic1vdd", + .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD], + }, + { + .compatible = "cirrus,lochnagar2-mic2vdd", + .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD], + }, + { + .compatible = "cirrus,lochnagar2-vddcore", + .data = &lochnagar_regulators[LOCHNAGAR_VDDCORE], + }, + {}, +}; + static int lochnagar_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct lochnagar *lochnagar = dev_get_drvdata(dev->parent); struct regulator_config config = { }; + const struct of_device_id *of_id; + const struct regulator_desc *desc; struct regulator_dev *rdev; - int ret, i; + int ret; - config.dev = lochnagar->dev; + config.dev = dev; config.regmap = lochnagar->regmap; config.driver_data = lochnagar; - for (i = 0; i < ARRAY_SIZE(lochnagar_regulators); i++) { - const struct regulator_desc *desc = &lochnagar_regulators[i]; + of_id = of_match_device(lochnagar_of_match, dev); + if (!of_id) + return -EINVAL; - rdev = devm_regulator_register(dev, desc, &config); - if (IS_ERR(rdev)) { - ret = PTR_ERR(rdev); - dev_err(dev, "Failed to register %s regulator: %d\n", - desc->name, ret); - return ret; - } + desc = of_id->data; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "Failed to register %s regulator: %d\n", + desc->name, ret); + return ret; } return 0; @@ -242,6 +269,7 @@ static int lochnagar_regulator_probe(struct platform_device *pdev) static struct platform_driver lochnagar_regulator_driver = { .driver = { .name = "lochnagar-regulator", + .of_match_table = of_match_ptr(lochnagar_of_match), }, .probe = lochnagar_regulator_probe, diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 553b4790050f..2ee22e7ea675 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -501,8 +501,12 @@ static int lp8788_config_ldo_enable_mode(struct platform_device *pdev, return 0; } - /* FIXME: check default mode for GPIO here: high or low? */ - ldo->ena_gpiod = devm_gpiod_get_index_optional(&pdev->dev, + /* + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. + * FIXME: check default mode for GPIO here: high or low? + */ + ldo->ena_gpiod = gpiod_get_index_optional(&pdev->dev, "enable", enable_id, GPIOD_OUT_HIGH | diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index bee060937f56..8020eb57374a 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -11,8 +11,7 @@ #include <linux/kernel.h> #include <linux/bug.h> #include <linux/err.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> @@ -76,6 +75,7 @@ enum max77686_ramp_rate { }; struct max77686_data { + struct device *dev; DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS); /* Array indexed by regulator id */ @@ -250,26 +250,34 @@ static int max77686_of_parse_cb(struct device_node *np, struct regulator_config *config) { struct max77686_data *max77686 = config->driver_data; + int ret; switch (desc->id) { case MAX77686_BUCK8: case MAX77686_BUCK9: case MAX77686_LDO20 ... MAX77686_LDO22: - config->ena_gpio = of_get_named_gpio(np, - "maxim,ena-gpios", 0); - config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; - config->ena_gpio_initialized = true; + config->ena_gpiod = gpiod_get_from_of_node(np, + "maxim,ena", + 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "max77686-regulator"); + if (IS_ERR(config->ena_gpiod)) + config->ena_gpiod = NULL; break; default: return 0; } - if (gpio_is_valid(config->ena_gpio)) { + if (config->ena_gpiod) { set_bit(desc->id, max77686->gpio_enabled); - return regmap_update_bits(config->regmap, desc->enable_reg, - desc->enable_mask, - MAX77686_GPIO_CONTROL); + ret = regmap_update_bits(config->regmap, desc->enable_reg, + desc->enable_mask, + MAX77686_GPIO_CONTROL); + if (ret) { + gpiod_put(config->ena_gpiod); + config->ena_gpiod = NULL; + } } return 0; @@ -507,6 +515,7 @@ static int max77686_pmic_probe(struct platform_device *pdev) if (!max77686) return -ENOMEM; + max77686->dev = &pdev->dev; config.dev = iodev->dev; config.regmap = iodev->regmap; config.driver_data = max77686; diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 6c39fff73b8a..cf2a2912cb1b 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -231,9 +231,13 @@ static int max8952_pmic_probe(struct i2c_client *client, else gflags = GPIOD_OUT_LOW; gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; - gpiod = devm_gpiod_get_optional(&client->dev, - "max8952,en", - gflags); + /* + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. + */ + gpiod = gpiod_get_optional(&client->dev, + "max8952,en", + gflags); if (IS_ERR(gpiod)) return PTR_ERR(gpiod); if (gpiod) diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index e7a58b509032..9aee1444181d 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -808,7 +808,13 @@ static int max8973_probe(struct i2c_client *client, config.of_node = client->dev.of_node; config.regmap = max->regmap; - /* Register the regulators */ + /* + * Register the regulators + * Turn the GPIO descriptor over to the regulator core for + * lifecycle management if we pass an ena_gpiod. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(&client->dev, config.ena_gpiod); rdev = devm_regulator_register(&client->dev, &max->desc, &config); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c index 3bf5ddfaaea8..4d2487279a0a 100644 --- a/drivers/regulator/max8997-regulator.c +++ b/drivers/regulator/max8997-regulator.c @@ -925,7 +925,7 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, pdata->regulators = rdata; for_each_child_of_node(regulators_np, reg_np) { for (i = 0; i < ARRAY_SIZE(regulators); i++) - if (!of_node_cmp(reg_np->name, regulators[i].name)) + if (of_node_name_eq(reg_np, regulators[i].name)) break; if (i == ARRAY_SIZE(regulators)) { diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 65eb1e0350cf..2243138d8a58 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -186,7 +186,7 @@ struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( for (i = 0; i < num_regulators; i++) { if (!regulators[i].desc.name) continue; - if (!of_node_cmp(child->name, + if (of_node_name_eq(child, regulators[i].desc.name)) { p->id = i; p->init_data = of_get_regulator_init_data( diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c new file mode 100644 index 000000000000..3479ae009b0b --- /dev/null +++ b/drivers/regulator/mcp16502.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MCP16502 PMIC driver +// +// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries +// +// Author: Andrei Stefanescu <andrei.stefanescu@microchip.com> +// +// Inspired from tps65086-regulator.c + +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/suspend.h> + +#define VDD_LOW_SEL 0x0D +#define VDD_HIGH_SEL 0x3F + +#define MCP16502_FLT BIT(7) +#define MCP16502_ENS BIT(0) + +/* + * The PMIC has four sets of registers corresponding to four power modes: + * Performance, Active, Low-power, Hibernate. + * + * Registers: + * Each regulator has a register for each power mode. To access a register + * for a specific regulator and mode BASE_* and OFFSET_* need to be added. + * + * Operating modes: + * In order for the PMIC to transition to operating modes it has to be + * controlled via GPIO lines called LPM and HPM. + * + * The registers are fully configurable such that you can put all regulators in + * a low-power state while the PMIC is in Active mode. They are supposed to be + * configured at startup and then simply transition to/from a global low-power + * state by setting the GPIO lpm pin high/low. + * + * This driver keeps the PMIC in Active mode, Low-power state is set for the + * regulators by enabling/disabling operating mode (FPWM or Auto PFM). + * + * The PMIC's Low-power and Hibernate modes are used during standby/suspend. + * To enter standby/suspend the PMIC will go to Low-power mode. From there, it + * will transition to Hibernate when the PWRHLD line is set to low by the MPU. + */ + +/* + * This function is useful for iterating over all regulators and accessing their + * registers in a generic way or accessing a regulator device by its id. + */ +#define MCP16502_BASE(i) (((i) + 1) << 4) +#define MCP16502_STAT_BASE(i) ((i) + 5) + +#define MCP16502_OFFSET_MODE_A 0 +#define MCP16502_OFFSET_MODE_LPM 1 +#define MCP16502_OFFSET_MODE_HIB 2 + +#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL +#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE +#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY + +#define MCP16502_MODE_AUTO_PFM 0 +#define MCP16502_MODE_FPWM BIT(6) + +#define MCP16502_VSEL 0x3F +#define MCP16502_EN BIT(7) +#define MCP16502_MODE BIT(6) + +#define MCP16502_MIN_REG 0x0 +#define MCP16502_MAX_REG 0x65 + +static unsigned int mcp16502_of_map_mode(unsigned int mode) +{ + if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) + return mode; + + return REGULATOR_MODE_INVALID; +} + +#define MCP16502_REGULATOR(_name, _id, _ranges, _ops) \ + [_id] = { \ + .name = _name, \ + .regulators_node = of_match_ptr("regulators"), \ + .id = _id, \ + .ops = &(_ops), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .n_voltages = MCP16502_VSEL + 1, \ + .linear_ranges = _ranges, \ + .n_linear_ranges = ARRAY_SIZE(_ranges), \ + .of_match = of_match_ptr(_name), \ + .of_map_mode = mcp16502_of_map_mode, \ + .vsel_reg = (((_id) + 1) << 4), \ + .vsel_mask = MCP16502_VSEL, \ + .enable_reg = (((_id) + 1) << 4), \ + .enable_mask = MCP16502_EN, \ + } + +enum { + BUCK1 = 0, + BUCK2, + BUCK3, + BUCK4, + LDO1, + LDO2, + NUM_REGULATORS +}; + +/* + * struct mcp16502 - PMIC representation + * @rdev: the regulators belonging to this chip + * @rmap: regmap to be used for I2C communication + * @lpm: LPM GPIO descriptor + */ +struct mcp16502 { + struct regulator_dev *rdev[NUM_REGULATORS]; + struct regmap *rmap; + struct gpio_desc *lpm; +}; + +/* + * mcp16502_gpio_set_mode() - set the GPIO corresponding value + * + * Used to prepare transitioning into hibernate or resuming from it. + */ +static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) +{ + switch (mode) { + case MCP16502_OPMODE_ACTIVE: + gpiod_set_value(mcp->lpm, 0); + break; + case MCP16502_OPMODE_LPM: + case MCP16502_OPMODE_HIB: + gpiod_set_value(mcp->lpm, 1); + break; + default: + pr_err("%s: %d invalid\n", __func__, mode); + } +} + +/* + * mcp16502_get_reg() - get the PMIC's configuration register for opmode + * + * @rdev: the regulator whose register we are searching + * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate + */ +static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) +{ + int reg = MCP16502_BASE(rdev_get_id(rdev)); + + switch (opmode) { + case MCP16502_OPMODE_ACTIVE: + return reg + MCP16502_OFFSET_MODE_A; + case MCP16502_OPMODE_LPM: + return reg + MCP16502_OFFSET_MODE_LPM; + case MCP16502_OPMODE_HIB: + return reg + MCP16502_OFFSET_MODE_HIB; + default: + return -EINVAL; + } +} + +/* + * mcp16502_get_mode() - return the current operating mode of a regulator + * + * Note: all functions that are not part of entering/exiting standby/suspend + * use the Active mode registers. + * + * Note: this is different from the PMIC's operatig mode, it is the + * MODE bit from the regulator's register. + */ +static unsigned int mcp16502_get_mode(struct regulator_dev *rdev) +{ + unsigned int val; + int ret, reg; + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + + reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); + if (reg < 0) + return reg; + + ret = regmap_read(mcp->rmap, reg, &val); + if (ret) + return ret; + + switch (val & MCP16502_MODE) { + case MCP16502_MODE_FPWM: + return REGULATOR_MODE_NORMAL; + case MCP16502_MODE_AUTO_PFM: + return REGULATOR_MODE_IDLE; + default: + return REGULATOR_MODE_INVALID; + } +} + +/* + * _mcp16502_set_mode() - helper for set_mode and set_suspend_mode + * + * @rdev: the regulator for which we are setting the mode + * @mode: the regulator's mode (the one from MODE bit) + * @opmode: the PMIC's operating mode: Active/Low-power/Hibernate + */ +static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, + unsigned int op_mode) +{ + int val; + int reg; + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + + reg = mcp16502_get_reg(rdev, op_mode); + if (reg < 0) + return reg; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = MCP16502_MODE_FPWM; + break; + case REGULATOR_MODE_IDLE: + val = MCP16502_MODE_AUTO_PFM; + break; + default: + return -EINVAL; + } + + reg = regmap_update_bits(mcp->rmap, reg, MCP16502_MODE, val); + return reg; +} + +/* + * mcp16502_set_mode() - regulator_ops set_mode + */ +static int mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_ACTIVE); +} + +/* + * mcp16502_get_status() - regulator_ops get_status + */ +static int mcp16502_get_status(struct regulator_dev *rdev) +{ + int ret; + unsigned int val; + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + + ret = regmap_read(mcp->rmap, MCP16502_STAT_BASE(rdev_get_id(rdev)), + &val); + if (ret) + return ret; + + if (val & MCP16502_FLT) + return REGULATOR_STATUS_ERROR; + else if (val & MCP16502_ENS) + return REGULATOR_STATUS_ON; + else if (!(val & MCP16502_ENS)) + return REGULATOR_STATUS_OFF; + + return REGULATOR_STATUS_UNDEFINED; +} + +#ifdef CONFIG_SUSPEND +/* + * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC + * mode + */ +static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) +{ + switch (pm_suspend_target_state) { + case PM_SUSPEND_STANDBY: + return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); + case PM_SUSPEND_ON: + case PM_SUSPEND_MEM: + return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); + default: + dev_err(&rdev->dev, "invalid suspend target: %d\n", + pm_suspend_target_state); + } + + return -EINVAL; +} + +/* + * mcp16502_set_suspend_voltage() - regulator_ops set_suspend_voltage + */ +static int mcp16502_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + int sel = regulator_map_voltage_linear_range(rdev, uV, uV); + int reg = mcp16502_suspend_get_target_reg(rdev); + + if (sel < 0) + return sel; + + if (reg < 0) + return reg; + + return regmap_update_bits(mcp->rmap, reg, MCP16502_VSEL, sel); +} + +/* + * mcp16502_set_suspend_mode() - regulator_ops set_suspend_mode + */ +static int mcp16502_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + switch (pm_suspend_target_state) { + case PM_SUSPEND_STANDBY: + return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_LPM); + case PM_SUSPEND_ON: + case PM_SUSPEND_MEM: + return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_HIB); + default: + dev_err(&rdev->dev, "invalid suspend target: %d\n", + pm_suspend_target_state); + } + + return -EINVAL; +} + +/* + * mcp16502_set_suspend_enable() - regulator_ops set_suspend_enable + */ +static int mcp16502_set_suspend_enable(struct regulator_dev *rdev) +{ + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + int reg = mcp16502_suspend_get_target_reg(rdev); + + if (reg < 0) + return reg; + + return regmap_update_bits(mcp->rmap, reg, MCP16502_EN, MCP16502_EN); +} + +/* + * mcp16502_set_suspend_disable() - regulator_ops set_suspend_disable + */ +static int mcp16502_set_suspend_disable(struct regulator_dev *rdev) +{ + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + int reg = mcp16502_suspend_get_target_reg(rdev); + + if (reg < 0) + return reg; + + return regmap_update_bits(mcp->rmap, reg, MCP16502_EN, 0); +} +#endif /* CONFIG_SUSPEND */ + +static const struct regulator_ops mcp16502_buck_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mcp16502_get_status, + + .set_mode = mcp16502_set_mode, + .get_mode = mcp16502_get_mode, + +#ifdef CONFIG_SUSPEND + .set_suspend_voltage = mcp16502_set_suspend_voltage, + .set_suspend_mode = mcp16502_set_suspend_mode, + .set_suspend_enable = mcp16502_set_suspend_enable, + .set_suspend_disable = mcp16502_set_suspend_disable, +#endif /* CONFIG_SUSPEND */ +}; + +/* + * LDOs cannot change operating modes. + */ +static const struct regulator_ops mcp16502_ldo_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mcp16502_get_status, + +#ifdef CONFIG_SUSPEND + .set_suspend_voltage = mcp16502_set_suspend_voltage, + .set_suspend_enable = mcp16502_set_suspend_enable, + .set_suspend_disable = mcp16502_set_suspend_disable, +#endif /* CONFIG_SUSPEND */ +}; + +static const struct of_device_id mcp16502_ids[] = { + { .compatible = "microchip,mcp16502", }, + {} +}; +MODULE_DEVICE_TABLE(of, mcp16502_ids); + +static const struct regulator_linear_range b1l12_ranges[] = { + REGULATOR_LINEAR_RANGE(1200000, VDD_LOW_SEL, VDD_HIGH_SEL, 50000), +}; + +static const struct regulator_linear_range b234_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, VDD_LOW_SEL, VDD_HIGH_SEL, 25000), +}; + +static const struct regulator_desc mcp16502_desc[] = { + /* MCP16502_REGULATOR(_name, _id, ranges, regulator_ops) */ + MCP16502_REGULATOR("VDD_IO", BUCK1, b1l12_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("VDD_DDR", BUCK2, b234_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("VDD_CORE", BUCK3, b234_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("VDD_OTHER", BUCK4, b234_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("LDO1", LDO1, b1l12_ranges, mcp16502_ldo_ops), + MCP16502_REGULATOR("LDO2", LDO2, b1l12_ranges, mcp16502_ldo_ops) +}; + +static const struct regmap_range mcp16502_ranges[] = { + regmap_reg_range(MCP16502_MIN_REG, MCP16502_MAX_REG) +}; + +static const struct regmap_access_table mcp16502_yes_reg_table = { + .yes_ranges = mcp16502_ranges, + .n_yes_ranges = ARRAY_SIZE(mcp16502_ranges), +}; + +static const struct regmap_config mcp16502_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MCP16502_MAX_REG, + .cache_type = REGCACHE_NONE, + .rd_table = &mcp16502_yes_reg_table, + .wr_table = &mcp16502_yes_reg_table, +}; + +/* + * set_up_regulators() - initialize all regulators + */ +static int setup_regulators(struct mcp16502 *mcp, struct device *dev, + struct regulator_config config) +{ + int i; + + for (i = 0; i < NUM_REGULATORS; i++) { + mcp->rdev[i] = devm_regulator_register(dev, + &mcp16502_desc[i], + &config); + if (IS_ERR(mcp->rdev[i])) { + dev_err(dev, + "failed to register %s regulator %ld\n", + mcp16502_desc[i].name, PTR_ERR(mcp->rdev[i])); + return PTR_ERR(mcp->rdev[i]); + } + } + + return 0; +} + +static int mcp16502_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regulator_config config = { }; + struct device *dev; + struct mcp16502 *mcp; + int ret = 0; + + dev = &client->dev; + config.dev = dev; + + mcp = devm_kzalloc(dev, sizeof(*mcp), GFP_KERNEL); + if (!mcp) + return -ENOMEM; + + mcp->rmap = devm_regmap_init_i2c(client, &mcp16502_regmap_config); + if (IS_ERR(mcp->rmap)) { + ret = PTR_ERR(mcp->rmap); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, mcp); + config.regmap = mcp->rmap; + config.driver_data = mcp; + + mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW); + if (IS_ERR(mcp->lpm)) { + dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm)); + return PTR_ERR(mcp->lpm); + } + + ret = setup_regulators(mcp, dev, config); + if (ret != 0) + return ret; + + mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mcp16502_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp16502 *mcp = i2c_get_clientdata(client); + + mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_LPM); + + return 0; +} + +static int mcp16502_resume_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp16502 *mcp = i2c_get_clientdata(client); + + mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static const struct dev_pm_ops mcp16502_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq, + mcp16502_resume_noirq) +}; +#endif +static const struct i2c_device_id mcp16502_i2c_id[] = { + { "mcp16502", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp16502_i2c_id); + +static struct i2c_driver mcp16502_drv = { + .probe = mcp16502_probe, + .driver = { + .name = "mcp16502-regulator", + .of_match_table = of_match_ptr(mcp16502_ids), +#ifdef CONFIG_PM + .pm = &mcp16502_pm_ops, +#endif + }, + .id_table = mcp16502_i2c_id, +}; + +module_i2c_driver(mcp16502_drv); + +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MCP16502 PMIC driver"); +MODULE_AUTHOR("Andrei Stefanescu andrei.stefanescu@microchip.com"); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index c4223b3e0dff..ffa5fc3724e4 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -20,6 +20,7 @@ #include "internal.h" static const char *const regulator_states[PM_SUSPEND_MAX + 1] = { + [PM_SUSPEND_STANDBY] = "regulator-state-standby", [PM_SUSPEND_MEM] = "regulator-state-mem", [PM_SUSPEND_MAX] = "regulator-state-disk", }; @@ -170,6 +171,10 @@ static void of_get_regulation_constraints(struct device_node *np, &pval)) constraints->max_spread = pval; + if (!of_property_read_u32(np, "regulator-max-step-microvolt", + &pval)) + constraints->max_uV_step = pval; + constraints->over_current_protection = of_property_read_bool(np, "regulator-over-current-protection"); @@ -181,9 +186,11 @@ static void of_get_regulation_constraints(struct device_node *np, case PM_SUSPEND_MAX: suspend_state = &constraints->state_disk; break; + case PM_SUSPEND_STANDBY: + suspend_state = &constraints->state_standby; + break; case PM_SUSPEND_ON: case PM_SUSPEND_TO_IDLE: - case PM_SUSPEND_STANDBY: default: continue; } @@ -364,24 +371,25 @@ int of_regulator_match(struct device *dev, struct device_node *node, } EXPORT_SYMBOL_GPL(of_regulator_match); -struct regulator_init_data *regulator_of_get_init_data(struct device *dev, - const struct regulator_desc *desc, - struct regulator_config *config, - struct device_node **node) +struct device_node *regulator_of_get_init_node(struct device *dev, + const struct regulator_desc *desc) { struct device_node *search, *child; - struct regulator_init_data *init_data = NULL; const char *name; if (!dev->of_node || !desc->of_match) return NULL; - if (desc->regulators_node) + if (desc->regulators_node) { search = of_get_child_by_name(dev->of_node, desc->regulators_node); - else + } else { search = of_node_get(dev->of_node); + if (!strcmp(desc->of_match, search->name)) + return search; + } + if (!search) { dev_dbg(dev, "Failed to find regulator container node '%s'\n", desc->regulators_node); @@ -393,35 +401,48 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev, if (!name) name = child->name; - if (strcmp(desc->of_match, name)) - continue; + if (!strcmp(desc->of_match, name)) + return of_node_get(child); + } - init_data = of_get_regulator_init_data(dev, child, desc); - if (!init_data) { - dev_err(dev, - "failed to parse DT for regulator %pOFn\n", - child); - break; - } + of_node_put(search); - if (desc->of_parse_cb) { - if (desc->of_parse_cb(child, desc, config)) { - dev_err(dev, - "driver callback failed to parse DT for regulator %pOFn\n", - child); - init_data = NULL; - break; - } - } + return NULL; +} + +struct regulator_init_data *regulator_of_get_init_data(struct device *dev, + const struct regulator_desc *desc, + struct regulator_config *config, + struct device_node **node) +{ + struct device_node *child; + struct regulator_init_data *init_data = NULL; - of_node_get(child); - *node = child; - break; + child = regulator_of_get_init_node(dev, desc); + if (!child) + return NULL; + + init_data = of_get_regulator_init_data(dev, child, desc); + if (!init_data) { + dev_err(dev, "failed to parse DT for regulator %pOFn\n", child); + goto error; } - of_node_put(search); + if (desc->of_parse_cb && desc->of_parse_cb(child, desc, config)) { + dev_err(dev, + "driver callback failed to parse DT for regulator %pOFn\n", + child); + goto error; + } + + *node = child; return init_data; + +error: + of_node_put(child); + + return NULL; } static int of_node_match(struct device *dev, const void *data) diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index bb5ab7d78895..c2cc392a27d4 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -443,13 +443,16 @@ static int palmas_ldo_write(struct palmas *palmas, unsigned int reg, static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode) { int id = rdev_get_id(dev); + int ret; struct palmas_pmic *pmic = rdev_get_drvdata(dev); struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata; struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id]; unsigned int reg; bool rail_enable = true; - palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, ®); + ret = palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, ®); + if (ret) + return ret; reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index dd41a9bb3f5c..df5df1c495ad 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -370,6 +370,7 @@ static struct pfuze_regulator pfuze100_regulators[] = { PFUZE100_VGEN_REG(PFUZE100, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), PFUZE100_VGEN_REG(PFUZE100, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), PFUZE100_VGEN_REG(PFUZE100, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), + PFUZE100_COIN_REG(PFUZE100, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin), }; static struct pfuze_regulator pfuze200_regulators[] = { @@ -436,6 +437,7 @@ static struct of_regulator_match pfuze100_matches[] = { { .name = "vgen4", }, { .name = "vgen5", }, { .name = "vgen6", }, + { .name = "coin", }, }; /* PFUZE200 */ diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 39ccf53fdeb3..b2c2d01d1637 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -410,7 +410,7 @@ static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev, vreg->dev = dev; for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++) - if (!strcmp(rpmh_data->name, node->name)) + if (of_node_name_eq(node, rpmh_data->name)) break; if (!rpmh_data->name) { diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 5bb6f4ca48db..ee4a23ab0663 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -5,7 +5,7 @@ #include <linux/bug.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/of.h> @@ -14,7 +14,6 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> -#include <linux/of_gpio.h> #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/s2mps11.h> #include <linux/mfd/samsung/s2mps13.h> @@ -44,7 +43,7 @@ struct s2mps11_info { * Array (size: number of regulators) with GPIO-s for external * sleep control. */ - int *ext_control_gpio; + struct gpio_desc **ext_control_gpiod; }; static int get_ramp_delay(int ramp_delay) @@ -511,7 +510,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev) case S2MPS14X: if (test_bit(rdev_get_id(rdev), s2mps11->suspend_state)) val = S2MPS14_ENABLE_SUSPEND; - else if (gpio_is_valid(s2mps11->ext_control_gpio[rdev_get_id(rdev)])) + else if (s2mps11->ext_control_gpiod[rdev_get_id(rdev)]) val = S2MPS14_ENABLE_EXT_CONTROL; else val = rdev->desc->enable_mask; @@ -805,7 +804,7 @@ static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) { - int *gpio = s2mps11->ext_control_gpio; + struct gpio_desc **gpio = s2mps11->ext_control_gpiod; unsigned int i; unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11, S2MPS14_LDO12 }; @@ -816,11 +815,20 @@ static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, if (!rdata[reg].init_data || !rdata[reg].of_node) continue; - gpio[reg] = of_get_named_gpio(rdata[reg].of_node, - "samsung,ext-control-gpios", 0); - if (gpio_is_valid(gpio[reg])) - dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n", - gpio[reg], reg, rdata[reg].name); + gpio[reg] = devm_gpiod_get_from_of_node(&pdev->dev, + rdata[reg].of_node, + "samsung,ext-control-gpios", + 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "s2mps11-regulator"); + if (IS_ERR(gpio[reg])) { + dev_err(&pdev->dev, "Failed to get control GPIO for %d/%s\n", + reg, rdata[reg].name); + continue; + } + if (gpio[reg]) + dev_dbg(&pdev->dev, "Using GPIO for ext-control over %d/%s\n", + reg, rdata[reg].name); } } @@ -1126,17 +1134,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; } - s2mps11->ext_control_gpio = devm_kmalloc_array(&pdev->dev, - rdev_num, sizeof(*s2mps11->ext_control_gpio), - GFP_KERNEL); - if (!s2mps11->ext_control_gpio) + s2mps11->ext_control_gpiod = devm_kcalloc(&pdev->dev, rdev_num, + sizeof(*s2mps11->ext_control_gpiod), GFP_KERNEL); + if (!s2mps11->ext_control_gpiod) return -ENOMEM; - /* - * 0 is a valid GPIO so initialize all GPIO-s to negative value - * to indicate that external control won't be used for this regulator. - */ - for (i = 0; i < rdev_num; i++) - s2mps11->ext_control_gpio[i] = -EINVAL; if (!iodev->dev->of_node) { if (iodev->pdata) { @@ -1166,8 +1167,6 @@ common_reg: config.dev = &pdev->dev; config.regmap = iodev->regmap_pmic; config.driver_data = s2mps11; - config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; - config.ena_gpio_initialized = true; for (i = 0; i < rdev_num; i++) { struct regulator_dev *regulator; @@ -1178,8 +1177,13 @@ common_reg: config.init_data = rdata[i].init_data; config.of_node = rdata[i].of_node; } - config.ena_gpio = s2mps11->ext_control_gpio[i]; - + config.ena_gpiod = s2mps11->ext_control_gpiod[i]; + /* + * Hand the GPIO descriptor management over to the regulator + * core, remove it from devres management. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(&pdev->dev, config.ena_gpiod); regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(regulator)) { @@ -1189,7 +1193,7 @@ common_reg: goto out; } - if (gpio_is_valid(s2mps11->ext_control_gpio[i])) { + if (s2mps11->ext_control_gpiod[i]) { ret = s2mps14_pmic_enable_ext_control(s2mps11, regulator); if (ret < 0) { diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 219b9afda0cb..b581f01f3395 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -561,7 +561,7 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, pdata->opmode = rmode; for_each_child_of_node(regulators_np, reg_np) { for (i = 0; i < ARRAY_SIZE(regulators); i++) - if (!of_node_cmp(reg_np->name, regulators[i].name)) + if (of_node_name_eq(reg_np, regulators[i].name)) break; if (i == ARRAY_SIZE(regulators)) { @@ -956,10 +956,17 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.regmap = iodev->regmap_pmic; config.of_node = pdata->regulators[i].reg_node; config.ena_gpiod = NULL; - if (pdata->regulators[i].ext_control_gpiod) + if (pdata->regulators[i].ext_control_gpiod) { + /* Assigns config.ena_gpiod */ s5m8767_regulator_config_ext_control(s5m8767, &pdata->regulators[i], &config); + /* + * Hand the GPIO descriptor management over to the + * regulator core, remove it from devres management. + */ + devm_gpiod_unhinge(s5m8767->dev, config.ena_gpiod); + } rdev = devm_regulator_register(&pdev->dev, ®ulators[id], &config); if (IS_ERR(rdev)) { diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c index e15634edb8ce..16ba0297f709 100644 --- a/drivers/regulator/stpmic1_regulator.c +++ b/drivers/regulator/stpmic1_regulator.c @@ -489,14 +489,14 @@ static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - mutex_lock(&rdev->mutex); + regulator_lock(rdev); /* Send an overcurrent notification */ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - mutex_unlock(&rdev->mutex); + regulator_unlock(rdev); return IRQ_HANDLED; } diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index db714d5edafc..0614551796a1 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -480,6 +480,12 @@ static int tps65090_regulator_probe(struct platform_device *pdev) else config.of_node = NULL; + /* + * Hand the GPIO descriptor management over to the regulator + * core, remove it from devres management. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(&pdev->dev, config.ena_gpiod); rdev = devm_regulator_register(&pdev->dev, ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 02ccdaa226a7..5ebb6ee73f07 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1102,8 +1102,10 @@ static int tps65910_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); /* Give control of all register to control port */ - tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, + err = tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); + if (err < 0) + return err; switch (tps65910_chip_id(tps65910)) { case TPS65910: diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 8ad11b074b49..a1c7dfee5c37 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1153,7 +1153,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - mutex_lock(&rdev->mutex); + regulator_lock(rdev); if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, @@ -1162,7 +1162,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - mutex_unlock(&rdev->mutex); + regulator_unlock(rdev); return IRQ_HANDLED; } diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 7a4ce6df4f22..38928cdcb6e6 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -19,7 +19,7 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/mfd/wm8994/core.h> @@ -129,6 +129,7 @@ static int wm8994_ldo_probe(struct platform_device *pdev) int id = pdev->id % ARRAY_SIZE(pdata->ldo); struct regulator_config config = { }; struct wm8994_ldo *ldo; + struct gpio_desc *gpiod; int ret; dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); @@ -145,12 +146,18 @@ static int wm8994_ldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm8994->regmap; config.init_data = &ldo->init_data; - if (pdata) { - config.ena_gpio = pdata->ldo[id].enable; - } else if (wm8994->dev->of_node) { - config.ena_gpio = wm8994->pdata.ldo[id].enable; - config.ena_gpio_initialized = true; - } + + /* + * Look up LDO enable GPIO from the parent device node, we don't + * use devm because the regulator core will free the GPIO + */ + gpiod = gpiod_get_optional(pdev->dev.parent, + id ? "wlf,ldo2ena" : "wlf,ldo1ena", + GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + config.ena_gpiod = gpiod; /* Use default constraints if none set up */ if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) { @@ -159,12 +166,17 @@ static int wm8994_ldo_probe(struct platform_device *pdev) ldo->init_data = wm8994_ldo_default[id]; ldo->init_data.consumer_supplies = &ldo->supply; - if (!config.ena_gpio) + if (!gpiod) ldo->init_data.constraints.valid_ops_mask = 0; } else { ldo->init_data = *pdata->ldo[id].init_data; } + /* + * At this point the GPIO descriptor is handled over to the + * regulator core and we need not worry about it on the + * error path. + */ ldo->regulator = devm_regulator_register(&pdev->dev, &wm8994_ldo_desc[id], &config); @@ -172,15 +184,12 @@ static int wm8994_ldo_probe(struct platform_device *pdev) ret = PTR_ERR(ldo->regulator); dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", id + 1, ret); - goto err; + return ret; } platform_set_drvdata(pdev, ldo); return 0; - -err: - return ret; } static struct platform_driver wm8994_ldo_driver = { diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index b8163b40bb92..e0f061139c8f 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -90,6 +90,9 @@ #define PIO_DATAOUT_1B 0x0020 #define PIO_DATAOUT_4B 0x0024 +#define RD_FIFO_CFG 0x0028 +#define CONTINUOUS_MODE BIT(0) + #define RD_FIFO_STATUS 0x002c #define FIFO_EMPTY BIT(11) #define WR_CNTS_MSK 0x7f0 @@ -99,9 +102,6 @@ #define RDY_16BYTE BIT(1) #define FIFO_RDY BIT(0) -#define RD_FIFO_CFG 0x0028 -#define CONTINUOUS_MODE BIT(0) - #define RD_FIFO_RESET 0x0030 #define RESET_FIFO BIT(0) @@ -139,7 +139,7 @@ struct qcom_qspi { struct device *dev; struct clk_bulk_data clks[QSPI_NUM_CLKS]; struct qspi_xfer xfer; - /* Lock to protect data accessed by IRQs */ + /* Lock to protect xfer and IRQ accessed registers */ spinlock_t lock; }; diff --git a/include/dt-bindings/regulator/active-semi,8945a-regulator.h b/include/dt-bindings/regulator/active-semi,8945a-regulator.h new file mode 100644 index 000000000000..9bdba5e3141a --- /dev/null +++ b/include/dt-bindings/regulator/active-semi,8945a-regulator.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Microchip Technology, Inc. All rights reserved. + * + * Device Tree binding constants for the ACT8945A PMIC regulators + */ + +#ifndef _DT_BINDINGS_REGULATOR_ACT8945A_H +#define _DT_BINDINGS_REGULATOR_ACT8945A_H + +/* + * These constants should be used to specify regulator modes in device tree for + * ACT8945A regulators as follows: + * ACT8945A_REGULATOR_MODE_FIXED: It is specific to DCDC regulators and it + * specifies the usage of fixed-frequency + * PWM. + * + * ACT8945A_REGULATOR_MODE_NORMAL: It is specific to LDO regulators and it + * specifies the usage of normal mode. + * + * ACT8945A_REGULATOR_MODE_LOWPOWER: For DCDC and LDO regulators; it specify + * the usage of proprietary power-saving + * mode. + */ + +#define ACT8945A_REGULATOR_MODE_FIXED 1 +#define ACT8945A_REGULATOR_MODE_NORMAL 2 +#define ACT8945A_REGULATOR_MODE_LOWPOWER 3 + +#endif diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index f2f887795d43..8aebcf822082 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -104,6 +104,7 @@ struct gpio_descs *__must_check devm_gpiod_get_array_optional(struct device *dev, const char *con_id, enum gpiod_flags flags); void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); +void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc); void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); int gpiod_get_direction(struct gpio_desc *desc); @@ -172,6 +173,10 @@ int desc_to_gpio(const struct gpio_desc *desc); struct device_node; struct fwnode_handle; +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label); struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, struct device_node *node, const char *propname, int index, @@ -245,6 +250,15 @@ static inline void gpiod_put(struct gpio_desc *desc) WARN_ON(1); } +static inline void devm_gpiod_unhinge(struct device *dev, + struct gpio_desc *desc) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + static inline void gpiod_put_array(struct gpio_descs *descs) { might_sleep(); @@ -518,6 +532,15 @@ struct device_node; struct fwnode_handle; static inline +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, struct device_node *node, const char *propname, int index, diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 517e60eecbcb..1293695245df 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -35,7 +35,7 @@ enum axp20x_variants { #define AXP152_ALDO_OP_MODE 0x13 #define AXP152_LDO0_CTRL 0x15 #define AXP152_DCDC2_V_OUT 0x23 -#define AXP152_DCDC2_V_SCAL 0x25 +#define AXP152_DCDC2_V_RAMP 0x25 #define AXP152_DCDC1_V_OUT 0x26 #define AXP152_DCDC3_V_OUT 0x27 #define AXP152_ALDO12_V_OUT 0x28 @@ -53,7 +53,7 @@ enum axp20x_variants { #define AXP20X_USB_OTG_STATUS 0x02 #define AXP20X_PWR_OUT_CTRL 0x12 #define AXP20X_DCDC2_V_OUT 0x23 -#define AXP20X_DCDC2_LDO3_V_SCAL 0x25 +#define AXP20X_DCDC2_LDO3_V_RAMP 0x25 #define AXP20X_DCDC3_V_OUT 0x27 #define AXP20X_LDO24_V_OUT 0x28 #define AXP20X_LDO3_V_OUT 0x29 diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index b19c370fe81a..f346167c0e00 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -20,9 +20,6 @@ #define WM8994_NUM_AIF 3 struct wm8994_ldo_pdata { - /** GPIOs to enable regulator, 0 or less if not available */ - int enable; - const struct regulator_init_data *init_data; }; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 25602afd4844..f3f76051e8b0 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -508,7 +508,7 @@ static inline int regulator_get_error_flags(struct regulator *regulator, static inline int regulator_set_load(struct regulator *regulator, int load_uA) { - return REGULATOR_MODE_NORMAL; + return 0; } static inline int regulator_allow_bypass(struct regulator *regulator, diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index a9c030192147..389bcaf7900f 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -15,11 +15,12 @@ #ifndef __LINUX_REGULATOR_DRIVER_H_ #define __LINUX_REGULATOR_DRIVER_H_ -#define MAX_COUPLED 4 +#define MAX_COUPLED 2 #include <linux/device.h> #include <linux/notifier.h> #include <linux/regulator/consumer.h> +#include <linux/ww_mutex.h> struct gpio_desc; struct regmap; @@ -462,7 +463,7 @@ struct regulator_dev { struct coupling_desc coupling_desc; struct blocking_notifier_head notifier; - struct mutex mutex; /* consumer lock */ + struct ww_mutex mutex; /* consumer lock */ struct task_struct *mutex_owner; int ref_cnt; struct module *owner; @@ -473,7 +474,6 @@ struct regulator_dev { struct regmap *regmap; struct delayed_work disable_work; - int deferred_disables; void *reg_data; /* regulator_dev data */ @@ -545,4 +545,7 @@ int regulator_set_active_discharge_regmap(struct regulator_dev *rdev, bool enable); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); +void regulator_lock(struct regulator_dev *rdev); +void regulator_unlock(struct regulator_dev *rdev); + #endif diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index a459a5e973a7..1d34a70ffda2 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -158,6 +158,9 @@ struct regulation_constraints { /* used for coupled regulators */ int max_spread; + /* used for changing voltage in steps */ + int max_uV_step; + /* valid regulator operating modes for this machine */ unsigned int valid_modes_mask; diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index cb5aecd40f07..331d7d940c7a 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h @@ -33,7 +33,8 @@ #define PFUZE100_VGEN4 12 #define PFUZE100_VGEN5 13 #define PFUZE100_VGEN6 14 -#define PFUZE100_MAX_REGULATOR 15 +#define PFUZE100_COIN 15 +#define PFUZE100_MAX_REGULATOR 16 #define PFUZE200_SW1AB 0 #define PFUZE200_SW2 1 |