summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml4
-rw-r--r--Documentation/devicetree/bindings/mfd/samsung,s2mpg10-pmic.yaml120
-rw-r--r--Documentation/devicetree/bindings/mfd/samsung,s2mpg11-pmic.yaml88
-rw-r--r--Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml29
-rw-r--r--Documentation/devicetree/bindings/regulator/adi,max77675.yaml184
-rw-r--r--Documentation/devicetree/bindings/regulator/mediatek,mt6331-regulator.yaml4
-rw-r--r--Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml5
-rw-r--r--Documentation/devicetree/bindings/regulator/regulator.yaml1
-rw-r--r--Documentation/devicetree/bindings/regulator/richtek,rt5739.yaml5
-rw-r--r--Documentation/devicetree/bindings/regulator/samsung,s2mpg10-regulator.yaml158
-rw-r--r--Documentation/devicetree/bindings/regulator/samsung,s2mpg11-regulator.yaml136
-rw-r--r--Documentation/devicetree/bindings/regulator/ti,tps65185.yaml96
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/mfd/sec-acpm.c171
-rw-r--r--drivers/mfd/sec-common.c62
-rw-r--r--drivers/mfd/sec-core.h2
-rw-r--r--drivers/mfd/sec-irq.c150
-rw-r--r--drivers/regulator/Kconfig29
-rw-r--r--drivers/regulator/Makefile3
-rw-r--r--drivers/regulator/core.c351
-rw-r--r--drivers/regulator/dummy.c2
-rw-r--r--drivers/regulator/max77675-regulator.c1056
-rw-r--r--drivers/regulator/rt8092.c313
-rw-r--r--drivers/regulator/s2mps11.c1186
-rw-r--r--drivers/regulator/tps65185.c454
-rw-r--r--drivers/rtc/rtc-s5m.c21
-rw-r--r--include/dt-bindings/regulator/samsung,s2mpg10-regulator.h53
-rw-r--r--include/linux/mfd/samsung/core.h2
-rw-r--r--include/linux/mfd/samsung/irq.h105
-rw-r--r--include/linux/mfd/samsung/s2mpg10.h44
-rw-r--r--include/linux/mfd/samsung/s2mpg11.h434
-rw-r--r--include/linux/regulator/driver.h7
-rw-r--r--rust/helpers/regulator.c24
-rw-r--r--rust/kernel/regulator.rs9
34 files changed, 4970 insertions, 339 deletions
diff --git a/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml b/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml
index d3bca6088d12..4a1e3e3c0505 100644
--- a/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml
+++ b/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml
@@ -75,7 +75,7 @@ examples:
interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>;
regulators {
- LDO1 {
+ ldo1m {
regulator-name = "vdd_ldo1";
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1300000>;
@@ -84,7 +84,7 @@ examples:
// ...
- BUCK1 {
+ buck8m {
regulator-name = "vdd_mif";
regulator-min-microvolt = <450000>;
regulator-max-microvolt = <1300000>;
diff --git a/Documentation/devicetree/bindings/mfd/samsung,s2mpg10-pmic.yaml b/Documentation/devicetree/bindings/mfd/samsung,s2mpg10-pmic.yaml
new file mode 100644
index 000000000000..0ea1a440b983
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/samsung,s2mpg10-pmic.yaml
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/samsung,s2mpg10-pmic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung S2MPG10 Power Management IC
+
+maintainers:
+ - André Draszik <andre.draszik@linaro.org>
+
+description: |
+ This is part of the device tree bindings for the S2MPG10 Power Management IC
+ (PMIC).
+
+ The Samsung S2MPG10 is a Power Management IC for mobile applications with buck
+ converters, various LDOs, power meters, RTC, clock outputs, and additional
+ GPIO interfaces and is typically complemented by S2MPG10 PMIC in a main/sub
+ configuration as the main PMIC.
+
+properties:
+ compatible:
+ const: samsung,s2mpg10-pmic
+
+ clocks:
+ $ref: /schemas/clock/samsung,s2mps11.yaml
+ description:
+ Child node describing clock provider.
+
+ interrupts:
+ maxItems: 1
+
+ regulators:
+ type: object
+ $ref: /schemas/regulator/samsung,s2mpg10-regulator.yaml
+ description:
+ List of child nodes that specify the regulators.
+
+ system-power-controller: true
+
+ wakeup-source: true
+
+patternProperties:
+ "^vinb([1-9]|10)m-supply$":
+ description:
+ Phandle to the power supply for each buck rail of this PMIC. There is a
+ 1:1 mapping of supply to rail, e.g. vinb1m-supply supplies buck1m.
+
+ "^vinl([1-9]|1[0-5])m-supply$":
+ description: |
+ Phandle to the power supply for one or multiple LDO rails of this PMIC.
+ The mapping of supply to rail(s) is as follows:
+ vinl1m - ldo13m
+ vinl2m - ldo15m
+ vinl3m - ldo1m, ldo5m, ldo7m
+ vinl4m - ldo3m, ldo8m
+ vinl5m - ldo16m
+ vinl6m - ldo17m
+ vinl7m - ldo6m, ldo11m, ldo24m, ldo28m
+ vinl8m - ldo12m
+ vinl9m - ldo2m, ldo4m
+ vinl10m - ldo9m, ldo14m, ldo18m, 19m, ldo20m, ldo25m
+ vinl11m - ldo23m, ldo31m
+ vinl12m - ldo29m
+ vinl13m - ldo30m
+ vinl14m - ldo21m
+ vinl15m - ldo10m, ldo22m, ldo26m, ldo27m
+
+required:
+ - compatible
+ - interrupts
+ - regulators
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/regulator/samsung,s2mpg10-regulator.h>
+
+ pmic {
+ compatible = "samsung,s2mpg10-pmic";
+ interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int>;
+ system-power-controller;
+ wakeup-source;
+
+ vinl3m-supply = <&buck8m>;
+
+ clocks {
+ compatible = "samsung,s2mpg10-clk";
+ #clock-cells = <1>;
+ clock-output-names = "rtc32k_ap", "peri32k1", "peri32k2";
+ };
+
+ regulators {
+ buck8m {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <450000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-ramp-delay = <6250>;
+ };
+
+ ldo1m {
+ regulator-name = "vdd_ldo1";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1300000>;
+ };
+
+ ldo20m {
+ regulator-name = "vdd_dmics";
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <1300000>;
+ regulator-always-on;
+ samsung,ext-control = <S2MPG10_EXTCTRL_LDO20M_EN2>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/samsung,s2mpg11-pmic.yaml b/Documentation/devicetree/bindings/mfd/samsung,s2mpg11-pmic.yaml
new file mode 100644
index 000000000000..62cedbbd9d8c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/samsung,s2mpg11-pmic.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/samsung,s2mpg11-pmic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung S2MPG11 Power Management IC
+
+maintainers:
+ - André Draszik <andre.draszik@linaro.org>
+
+description: |
+ This is part of the device tree bindings for the S2MPG11 Power Management IC
+ (PMIC).
+
+ The Samsung S2MPG11 is a Power Management IC for mobile applications with buck
+ converters, various LDOs, power meters, NTC thermistor inputs, and additional
+ GPIO interfaces and typically complements an S2MPG10 PMIC in a main/sub
+ configuration as the sub-PMIC.
+
+properties:
+ compatible:
+ const: samsung,s2mpg11-pmic
+
+ interrupts:
+ maxItems: 1
+
+ regulators:
+ type: object
+ $ref: /schemas/regulator/samsung,s2mpg11-regulator.yaml
+ description:
+ List of child nodes that specify the regulators.
+
+ wakeup-source: true
+
+patternProperties:
+ "^vinb(([1-9]|10)s|[abd])-supply$":
+ description:
+ Phandle to the power supply for each buck rail of this PMIC. There is a
+ 1:1 mapping of numbered supply to rail, e.g. vinb1s-supply supplies
+ buck1s. The remaining mapping is as follows
+ vinba - bucka
+ vinbb - buck boost
+ vinbd - buckd
+
+ "^vinl[1-6]s-supply$":
+ description: |
+ Phandle to the power supply for one or multiple LDO rails of this PMIC.
+ The mapping of supply to rail(s) is as follows
+ vinl1s - ldo1s, ldo2s
+ vinl2s - ldo8s, ldo9s
+ vinl3s - ldo3s, ldo5s, ldo7s, ldo15s
+ vinl4s - ldo10s, ldo11s, ldo12s, ldo14s
+ vinl5s - ldo4s, ldo6s
+ vinl6s - ldo13s
+
+required:
+ - compatible
+ - interrupts
+ - regulators
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/regulator/samsung,s2mpg10-regulator.h>
+
+ pmic {
+ compatible = "samsung,s2mpg11-pmic";
+ interrupts-extended = <&gpa0 7 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int>;
+ wakeup-source;
+
+ vinl1s-supply = <&buck8m>;
+ vinl2s-supply = <&buck6s>;
+
+ regulators {
+ buckd {
+ regulator-name = "vcc_ufs";
+ regulator-ramp-delay = <6250>;
+ enable-gpios = <&gpp0 1 GPIO_ACTIVE_HIGH>;
+ samsung,ext-control = <S2MPG11_EXTCTRL_UFS_EN>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml b/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml
index 31d544a9c05c..ac5d0c149796 100644
--- a/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml
+++ b/Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml
@@ -20,7 +20,6 @@ description: |
properties:
compatible:
enum:
- - samsung,s2mpg10-pmic
- samsung,s2mps11-pmic
- samsung,s2mps13-pmic
- samsung,s2mps14-pmic
@@ -59,12 +58,11 @@ properties:
reset (setting buck voltages to default values).
type: boolean
- system-power-controller: true
-
wakeup-source: true
required:
- compatible
+ - reg
- regulators
additionalProperties: false
@@ -74,31 +72,6 @@ allOf:
properties:
compatible:
contains:
- const: samsung,s2mpg10-pmic
- then:
- properties:
- reg: false
- samsung,s2mps11-acokb-ground: false
- samsung,s2mps11-wrstbi-ground: false
-
- # oneOf is required, because dtschema's fixups.py doesn't handle this
- # nesting here. Its special treatment to allow either interrupt property
- # when only one is specified in the binding works at the top level only.
- oneOf:
- - required: [interrupts]
- - required: [interrupts-extended]
-
- else:
- properties:
- system-power-controller: false
-
- required:
- - reg
-
- - if:
- properties:
- compatible:
- contains:
const: samsung,s2mps11-pmic
then:
properties:
diff --git a/Documentation/devicetree/bindings/regulator/adi,max77675.yaml b/Documentation/devicetree/bindings/regulator/adi,max77675.yaml
new file mode 100644
index 000000000000..c138e61380a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/adi,max77675.yaml
@@ -0,0 +1,184 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/adi,max77675.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim MAX77675 PMIC Regulator
+
+maintainers:
+ - Joan Na <joan.na@analog.com>
+
+description:
+ The MAX77675 is a Power Management IC providing four switching buck
+ regulators (SBB0–SBB3) accessible via I2C. It supports configuration
+ of output voltages and enable controls for each regulator.
+
+allOf:
+ - $ref: /schemas/input/input.yaml
+ - $ref: /schemas/pinctrl/pincfg-node.yaml
+
+properties:
+ compatible:
+ const: adi,max77675
+
+ reg:
+ maxItems: 1
+
+ reset-time-sec:
+ description: Manual reset time in seconds
+ enum: [4, 8, 12, 16]
+ default: 4
+
+ bias-disable:
+ type: boolean
+ description: Disable internal pull-up for EN pin
+
+ input-debounce:
+ description: Debounce time for the enable pin, in microseconds
+ items:
+ - enum: [100, 30000]
+ default: 100
+
+ adi,en-mode:
+ description: |
+ Enable mode configuration.
+ The debounce time set by 'input-debounce' applies to
+ both push-button and slide-switch modes.
+ "push-button" - A long press triggers power-on or power-down
+ "slide-switch" - Low : powers on, High : powers down
+ "logic" - Low : powers on, High : powers down (no debounce time)
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [push-button, slide-switch, logic]
+ default: slide-switch
+
+ adi,voltage-change-latency-us:
+ description:
+ Specifies the delay (in microseconds) between an output voltage change
+ request and the start of the SBB voltage ramp.
+ enum: [10, 100]
+ default: 100
+
+ adi,drv-sbb-strength:
+ description: |
+ SIMO Buck-Boost Drive Strength Trim.
+ Controls the drive strength of the SIMO regulator's power MOSFETs.
+ This setting affects switching speed, impacting power efficiency and EMI.
+ "max" – Maximum drive strength (~0.6 ns transition time)
+ "high" – High drive strength (~1.2 ns transition time)
+ "low" – Low drive strength (~1.8 ns transition time)
+ "min" – Minimum drive strength (~8 ns transition time)
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [max, high, low, min]
+ default: max
+
+ adi,dvs-slew-rate-mv-per-us:
+ description:
+ Dynamic rising slew rate for output voltage transitions, in mV/μs.
+ This setting is only used when 'adi,fixed-slew-rate' is not present.
+ enum: [5, 10]
+ default: 5
+
+ adi,bias-low-power-request:
+ type: boolean
+ description: Request low-power bias mode
+
+ adi,simo-ldo-always-on:
+ type: boolean
+ description: Set internal LDO to always supply 1.8V
+
+ regulators:
+ type: object
+ description: Regulator child nodes
+ patternProperties:
+ "^sbb[0-3]$":
+ type: object
+ $ref: regulator.yaml#
+ properties:
+ adi,fps-slot:
+ description: |
+ FPS (Flexible Power Sequencer) slot selection.
+ The Flexible Power Sequencer allows resources to power up under
+ hardware or software control. Additionally, each resource can
+ power up independently or among a group of other regulators with
+ adjustable power-up and power-down slots.
+ "slot0" - Assign to FPS Slot 0
+ "slot1" - Assign to FPS Slot 1
+ "slot2" - Assign to FPS Slot 2
+ "slot3" - Assign to FPS Slot 3
+ "default" - Use the default FPS slot value stored in register
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [slot0, slot1, slot2, slot3, default]
+ default: default
+
+ adi,fixed-slew-rate:
+ type: boolean
+ description:
+ When this property is present, the device uses a constant 2 mV/μs
+ slew rate and ignores any dynamic slew rate configuration.
+ When absent, the device uses the dynamic slew rate specified
+ by 'adi,dvs-slew-rate-mv-per-us'
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - regulators
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ max77675: pmic@44 {
+ compatible = "adi,max77675";
+ reg = <0x44>;
+
+ reset-time-sec = <4>;
+ input-debounce = <100>;
+
+ adi,en-mode = "slide-switch";
+ adi,voltage-change-latency-us = <100>;
+ adi,drv-sbb-strength = "max";
+ adi,dvs-slew-rate-mv-per-us = <5>;
+
+ regulators {
+ sbb0: sbb0 {
+ regulator-name = "sbb0";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <5500000>;
+ adi,fps-slot = "default";
+ adi,fixed-slew-rate;
+ };
+
+ sbb1: sbb1 {
+ regulator-name = "sbb1";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <5500000>;
+ adi,fps-slot = "default";
+ adi,fixed-slew-rate;
+ };
+
+ sbb2: sbb2 {
+ regulator-name = "sbb2";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <5500000>;
+ adi,fps-slot = "default";
+ adi,fixed-slew-rate;
+ };
+
+ sbb3: sbb3 {
+ regulator-name = "sbb3";
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <5500000>;
+ adi,fps-slot = "default";
+ adi,fixed-slew-rate;
+ };
+ };
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6331-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6331-regulator.yaml
index c654acf13768..eb16e53cb5bf 100644
--- a/Documentation/devicetree/bindings/regulator/mediatek,mt6331-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6331-regulator.yaml
@@ -40,13 +40,13 @@ patternProperties:
unevaluatedProperties: false
- "^ldo-v(dig18|emc33|ibr|mc|mch|mipi|rtc|sim1|sim2|sram|usb10)$":
+ "^ldo-v(dig18|emc33|ibr|io28|mc|mch|mipi|rtc|sim1|sim2|sram|usb10)$":
type: object
$ref: regulator.yaml#
properties:
regulator-name:
- pattern: "^v(dig18|emc33|ibr|mc|mch|mipi|rtc|sim1|sim2|sram|usb)$"
+ pattern: "^v(dig18|emc33|ibr|io28|mc|mch|mipi|rtc|sim1|sim2|sram|usb)$"
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml
index 41678400e63f..6c23f18a32c6 100644
--- a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml
@@ -24,6 +24,11 @@ properties:
reg:
maxItems: 1
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
additionalProperties: false
required:
diff --git a/Documentation/devicetree/bindings/regulator/regulator.yaml b/Documentation/devicetree/bindings/regulator/regulator.yaml
index 77573bcb6b79..042e56396399 100644
--- a/Documentation/devicetree/bindings/regulator/regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/regulator.yaml
@@ -274,6 +274,7 @@ patternProperties:
suspend. This property is now deprecated, instead setting voltage
for suspend mode via the API which regulator driver provides is
recommended.
+ deprecated: true
regulator-changeable-in-suspend:
description: whether the default voltage and the regulator on/off
diff --git a/Documentation/devicetree/bindings/regulator/richtek,rt5739.yaml b/Documentation/devicetree/bindings/regulator/richtek,rt5739.yaml
index e95e046e9ed6..983f4c1ce380 100644
--- a/Documentation/devicetree/bindings/regulator/richtek,rt5739.yaml
+++ b/Documentation/devicetree/bindings/regulator/richtek,rt5739.yaml
@@ -15,6 +15,10 @@ description: |
supply of 2.5V to 5.5V. It can provide up to 3.5A continuous current
capability at over 80% high efficiency.
+ The RT8092 is similar type buck converter. Compared to RT5739, it can offer
+ up to 4A output current and more output voltage range to meet the application
+ on most mobile products.
+
allOf:
- $ref: regulator.yaml#
@@ -23,6 +27,7 @@ properties:
enum:
- richtek,rt5733
- richtek,rt5739
+ - richtek,rt8092
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/regulator/samsung,s2mpg10-regulator.yaml b/Documentation/devicetree/bindings/regulator/samsung,s2mpg10-regulator.yaml
new file mode 100644
index 000000000000..7252f94b3a8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/samsung,s2mpg10-regulator.yaml
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/samsung,s2mpg10-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung S2MPG10 Power Management IC regulators
+
+maintainers:
+ - André Draszik <andre.draszik@linaro.org>
+
+description: |
+ This is part of the device tree bindings for the S2MG10 Power Management IC
+ (PMIC).
+
+ The S2MPG10 PMIC provides 10 buck and 31 LDO regulators.
+
+ See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
+ additional information and example.
+
+properties:
+ # 1 LDO with possible (but limited) external control
+ ldo20m:
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single LDO regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg10-ext-control"
+
+ properties:
+ regulator-ramp-delay: false
+
+ samsung,ext-control:
+ minimum: 11
+
+patternProperties:
+ # 10 bucks
+ "^buck([1-9]|10)m$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single buck regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg10-ext-control"
+
+ properties:
+ regulator-ramp-delay:
+ enum: [6250, 12500, 25000]
+ default: 6250
+
+ samsung,ext-control:
+ maximum: 10
+
+ # 12 standard LDOs
+ "^ldo(2[1-9]?|3[0-1])m$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for single LDO regulator.
+
+ properties:
+ regulator-ramp-delay: false
+
+ # 12 LDOs with possible external control
+ "^ldo([3-689]|1[046-9])m$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single LDO regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg10-ext-control"
+
+ properties:
+ regulator-ramp-delay: false
+
+ samsung,ext-control:
+ maximum: 10
+
+ # 6 LDOs with ramp support, 5 out of those with possible external control
+ "^ldo(1[1235]?|7)m$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single LDO regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg10-ext-control"
+
+ properties:
+ regulator-ramp-delay:
+ enum: [6250, 12500]
+ default: 6250
+
+ samsung,ext-control:
+ maximum: 10
+
+$defs:
+ s2mpg10-ext-control:
+ properties:
+ samsung,ext-control:
+ description: |
+ These rails can be controlled via one of several possible external
+ (hardware) signals. If so, this property configures the signal the PMIC
+ should monitor. For S2MPG10 rails where external control is possible other
+ than ldo20m, the following values generally corresponding to the
+ respective on-chip pin are valid:
+ - 0 # S2MPG10_EXTCTRL_PWREN - PWREN pin
+ - 1 # S2MPG10_EXTCTRL_PWREN_MIF - PWREN_MIF pin
+ - 2 # S2MPG10_EXTCTRL_AP_ACTIVE_N - ~AP_ACTIVE_N pin
+ - 3 # S2MPG10_EXTCTRL_CPUCL1_EN - CPUCL1_EN pin
+ - 4 # S2MPG10_EXTCTRL_CPUCL1_EN2 - CPUCL1_EN & PWREN pins
+ - 5 # S2MPG10_EXTCTRL_CPUCL2_EN - CPUCL2_EN pin
+ - 6 # S2MPG10_EXTCTRL_CPUCL2_EN2 - CPUCL2_E2 & PWREN pins
+ - 7 # S2MPG10_EXTCTRL_TPU_EN - TPU_EN pin
+ - 8 # S2MPG10_EXTCTRL_TPU_EN2 - TPU_EN & ~AP_ACTIVE_N pins
+ - 9 # S2MPG10_EXTCTRL_TCXO_ON - TCXO_ON pin
+ - 10 # S2MPG10_EXTCTRL_TCXO_ON2 - TCXO_ON & ~AP_ACTIVE_N pins
+
+ For S2MPG10 ldo20m, the following values are valid
+ - 11 # S2MPG10_EXTCTRL_LDO20M_EN2 - VLDO20M_EN & LDO20M_SFR
+ - 12 # S2MPG10_EXTCTRL_LDO20M_EN - VLDO20M_EN pin
+
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 12
+
+ enable-gpios:
+ description:
+ For rails where external control is done via a GPIO, this optional
+ property describes the GPIO line used.
+
+ dependentRequired:
+ enable-gpios: [ "samsung,ext-control" ]
+
+allOf:
+ # Bucks 8, 9, and LDO 1 can not be controlled externally - above definition
+ # allows it and we deny it here. This approach reduces repetition.
+ - if:
+ anyOf:
+ - required: [buck8m]
+ - required: [buck9m]
+ - required: [ldo1m]
+ then:
+ patternProperties:
+ "^(buck[8-9]|ldo1)m$":
+ properties:
+ samsung,ext-control: false
+
+additionalProperties: false
diff --git a/Documentation/devicetree/bindings/regulator/samsung,s2mpg11-regulator.yaml b/Documentation/devicetree/bindings/regulator/samsung,s2mpg11-regulator.yaml
new file mode 100644
index 000000000000..119386325d1b
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/samsung,s2mpg11-regulator.yaml
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/samsung,s2mpg11-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung S2MPG11 Power Management IC regulators
+
+maintainers:
+ - André Draszik <andre.draszik@linaro.org>
+
+description: |
+ This is part of the device tree bindings for the S2MG11 Power Management IC
+ (PMIC).
+
+ The S2MPG11 PMIC provides 12 buck, 1 buck-boost, and 15 LDO regulators.
+
+ See also Documentation/devicetree/bindings/mfd/samsung,s2mps11.yaml for
+ additional information and example.
+
+properties:
+ buckboost:
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for the buck-boost regulator.
+
+ properties:
+ regulator-ramp-delay: false
+
+patternProperties:
+ # 12 bucks
+ "^buck(([1-9]|10)s|[ad])$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single buck regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg11-ext-control"
+
+ properties:
+ regulator-ramp-delay:
+ enum: [6250, 12500, 25000]
+ default: 6250
+
+ # 11 standard LDOs
+ "^ldo([3-79]|1[01245])s$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single LDO regulator.
+
+ properties:
+ regulator-ramp-delay: false
+
+ # 2 LDOs with possible external control
+ "^ldo(8|13)s$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for single LDO regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg11-ext-control"
+
+ properties:
+ regulator-ramp-delay: false
+
+ # 2 LDOs with ramp support and possible external control
+ "^ldo[12]s$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single LDO regulator.
+
+ allOf:
+ - $ref: "#/$defs/s2mpg11-ext-control"
+
+ properties:
+ regulator-ramp-delay:
+ enum: [6250, 12500]
+ default: 6250
+
+$defs:
+ s2mpg11-ext-control:
+ properties:
+ samsung,ext-control:
+ description: |
+ These rails can be controlled via one of several possible external
+ (hardware) signals. If so, this property configures the signal the PMIC
+ should monitor. The following values generally corresponding to the
+ respective on-chip pin are valid:
+ - 0 # S2MPG11_EXTCTRL_PWREN - PWREN pin
+ - 1 # S2MPG11_EXTCTRL_PWREN_MIF - PWREN_MIF pin
+ - 2 # S2MPG11_EXTCTRL_AP_ACTIVE_N - ~AP_ACTIVE_N pin
+ - 3 # S2MPG11_EXTCTRL_G3D_EN - G3D_EN pin
+ - 4 # S2MPG11_EXTCTRL_G3D_EN2 - G3D_EN & ~AP_ACTIVE_N pins
+ - 5 # S2MPG11_EXTCTRL_AOC_VDD - AOC_VDD pin
+ - 6 # S2MPG11_EXTCTRL_AOC_RET - AOC_RET pin
+ - 7 # S2MPG11_EXTCTRL_UFS_EN - UFS_EN pin
+ - 8 # S2MPG11_EXTCTRL_LDO13S_EN - VLDO13S_EN pin
+
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 8
+
+ enable-gpios:
+ description:
+ For rails where external control is done via a GPIO, this optional
+ property describes the GPIO line used.
+
+ dependentRequired:
+ enable-gpios: [ "samsung,ext-control" ]
+
+allOf:
+ # Bucks 4, 6, 7 and 10 can not be controlled externally - above definition
+ # allows it and we deny it here. This approach reduces repetition.
+ - if:
+ anyOf:
+ - required: [buck4s]
+ - required: [buck6s]
+ - required: [buck7s]
+ - required: [buck10s]
+ then:
+ patternProperties:
+ "^buck([467]|10)s$":
+ properties:
+ samsung,ext-control: false
+
+additionalProperties: false
diff --git a/Documentation/devicetree/bindings/regulator/ti,tps65185.yaml b/Documentation/devicetree/bindings/regulator/ti,tps65185.yaml
new file mode 100644
index 000000000000..af0f638b80bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/ti,tps65185.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/ti,tps65185.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI TPS65185 Power Management Integrated Circuit
+
+maintainers:
+ - Andreas Kemnade <andreas@kemnade.info>
+
+description:
+ TPS65185 is a Power Management IC to provide Power for EPDs with one 3.3V
+ switch, 2 symmetric LDOs behind 2 DC/DC converters, and one unsymmetric
+ regulator for a compensation voltage.
+
+properties:
+ compatible:
+ const: ti,tps65185
+
+ reg:
+ maxItems: 1
+
+ enable-gpios:
+ description:
+ PWRUP pin
+ maxItems: 1
+
+ pwr-good-gpios:
+ maxItems: 1
+
+ vcom-ctrl-gpios:
+ maxItems: 1
+
+ wakeup-gpios:
+ maxItems: 1
+
+ vin-supply: true
+
+ interrupts:
+ maxItems: 1
+
+ regulators:
+ type: object
+ additionalProperties: false
+ patternProperties:
+ "^(vcom|vposneg|v3p3)$":
+ unevaluatedProperties: false
+ type: object
+ $ref: /schemas/regulator/regulator.yaml
+
+required:
+ - compatible
+ - reg
+ - pwr-good-gpios
+ - vin-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pmic@18 {
+ compatible = "ti,tps65185";
+ reg = <0x18>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_tps65185_gpio>;
+ pwr-good-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
+ vcom-ctrl-gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>;
+ wakeup-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>;
+ vin-supply = <&epdc_pmic_supply>;
+ interrupts-extended = <&gpio2 0 IRQ_TYPE_LEVEL_LOW>;
+
+ regulators {
+ vcom {
+ regulator-name = "vcom";
+ };
+
+ vposneg {
+ regulator-name = "vposneg";
+ regulator-min-microvolt = <15000000>;
+ regulator-max-microvolt = <15000000>;
+ };
+
+ v3p3 {
+ regulator-name = "v3p3";
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 630f155ffdac..c7d4308dbca2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23308,6 +23308,7 @@ F: drivers/mfd/sec*.[ch]
F: drivers/regulator/s2*.c
F: drivers/regulator/s5m*.c
F: drivers/rtc/rtc-s5m.c
+F: include/dt-bindings/regulator/samsung,s2m*.h
F: include/linux/mfd/samsung/
SAMSUNG S3C24XX/S3C64XX SOC SERIES CAMIF DRIVER
diff --git a/drivers/mfd/sec-acpm.c b/drivers/mfd/sec-acpm.c
index 36622069a788..537ea65685bf 100644
--- a/drivers/mfd/sec-acpm.c
+++ b/drivers/mfd/sec-acpm.c
@@ -13,6 +13,7 @@
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/rtc.h>
#include <linux/mfd/samsung/s2mpg10.h>
+#include <linux/mfd/samsung/s2mpg11.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -216,6 +217,155 @@ static const struct regmap_config s2mpg10_regmap_config_meter = {
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_range s2mpg11_common_registers[] = {
+ regmap_reg_range(0x00, 0x02), /* CHIP_ID_S, INT, INT_MASK */
+ regmap_reg_range(0x0a, 0x0c), /* Speedy control */
+ regmap_reg_range(0x1a, 0x27), /* Debug */
+};
+
+static const struct regmap_range s2mpg11_common_ro_registers[] = {
+ regmap_reg_range(0x00, 0x01), /* CHIP_ID_S, INT */
+ regmap_reg_range(0x25, 0x27), /* Debug */
+};
+
+static const struct regmap_range s2mpg11_common_nonvolatile_registers[] = {
+ regmap_reg_range(0x00, 0x00), /* CHIP_ID_S */
+ regmap_reg_range(0x02, 0x02), /* INT_MASK */
+ regmap_reg_range(0x0a, 0x0c), /* Speedy control */
+};
+
+static const struct regmap_range s2mpg11_common_precious_registers[] = {
+ regmap_reg_range(0x01, 0x01), /* INT */
+};
+
+static const struct regmap_access_table s2mpg11_common_wr_table = {
+ .yes_ranges = s2mpg11_common_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_common_registers),
+ .no_ranges = s2mpg11_common_ro_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg11_common_ro_registers),
+};
+
+static const struct regmap_access_table s2mpg11_common_rd_table = {
+ .yes_ranges = s2mpg11_common_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_common_registers),
+};
+
+static const struct regmap_access_table s2mpg11_common_volatile_table = {
+ .no_ranges = s2mpg11_common_nonvolatile_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg11_common_nonvolatile_registers),
+};
+
+static const struct regmap_access_table s2mpg11_common_precious_table = {
+ .yes_ranges = s2mpg11_common_precious_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_common_precious_registers),
+};
+
+static const struct regmap_config s2mpg11_regmap_config_common = {
+ .name = "common",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG11_COMMON_SPD_DEBUG4,
+ .wr_table = &s2mpg11_common_wr_table,
+ .rd_table = &s2mpg11_common_rd_table,
+ .volatile_table = &s2mpg11_common_volatile_table,
+ .precious_table = &s2mpg11_common_precious_table,
+ .num_reg_defaults_raw = S2MPG11_COMMON_SPD_DEBUG4 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_range s2mpg11_pmic_registers[] = {
+ regmap_reg_range(0x00, 0x5a), /* All PMIC registers */
+ regmap_reg_range(0x5c, 0xb7), /* All PMIC registers */
+};
+
+static const struct regmap_range s2mpg11_pmic_ro_registers[] = {
+ regmap_reg_range(0x00, 0x05), /* INTx */
+ regmap_reg_range(0x0c, 0x0d), /* STATUS OFFSRC */
+ regmap_reg_range(0x98, 0x98), /* GPIO input */
+};
+
+static const struct regmap_range s2mpg11_pmic_nonvolatile_registers[] = {
+ regmap_reg_range(0x06, 0x0b), /* INTxM */
+};
+
+static const struct regmap_range s2mpg11_pmic_precious_registers[] = {
+ regmap_reg_range(0x00, 0x05), /* INTx */
+};
+
+static const struct regmap_access_table s2mpg11_pmic_wr_table = {
+ .yes_ranges = s2mpg11_pmic_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_pmic_registers),
+ .no_ranges = s2mpg11_pmic_ro_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg11_pmic_ro_registers),
+};
+
+static const struct regmap_access_table s2mpg11_pmic_rd_table = {
+ .yes_ranges = s2mpg11_pmic_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_pmic_registers),
+};
+
+static const struct regmap_access_table s2mpg11_pmic_volatile_table = {
+ .no_ranges = s2mpg11_pmic_nonvolatile_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg11_pmic_nonvolatile_registers),
+};
+
+static const struct regmap_access_table s2mpg11_pmic_precious_table = {
+ .yes_ranges = s2mpg11_pmic_precious_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_pmic_precious_registers),
+};
+
+static const struct regmap_config s2mpg11_regmap_config_pmic = {
+ .name = "pmic",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG11_PMIC_LDO_SENSE2,
+ .wr_table = &s2mpg11_pmic_wr_table,
+ .rd_table = &s2mpg11_pmic_rd_table,
+ .volatile_table = &s2mpg11_pmic_volatile_table,
+ .precious_table = &s2mpg11_pmic_precious_table,
+ .num_reg_defaults_raw = S2MPG11_PMIC_LDO_SENSE2 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_range s2mpg11_meter_registers[] = {
+ regmap_reg_range(0x00, 0x3e), /* Meter config */
+ regmap_reg_range(0x40, 0x8a), /* Meter data */
+ regmap_reg_range(0x8d, 0x9c), /* Meter data */
+};
+
+static const struct regmap_range s2mpg11_meter_ro_registers[] = {
+ regmap_reg_range(0x40, 0x9c), /* Meter data */
+};
+
+static const struct regmap_access_table s2mpg11_meter_wr_table = {
+ .yes_ranges = s2mpg11_meter_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_meter_registers),
+ .no_ranges = s2mpg11_meter_ro_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg11_meter_ro_registers),
+};
+
+static const struct regmap_access_table s2mpg11_meter_rd_table = {
+ .yes_ranges = s2mpg11_meter_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_meter_registers),
+};
+
+static const struct regmap_access_table s2mpg11_meter_volatile_table = {
+ .yes_ranges = s2mpg11_meter_ro_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg11_meter_ro_registers),
+};
+
+static const struct regmap_config s2mpg11_regmap_config_meter = {
+ .name = "meter",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG11_METER_LPF_DATA_NTC7_2,
+ .wr_table = &s2mpg11_meter_wr_table,
+ .rd_table = &s2mpg11_meter_rd_table,
+ .volatile_table = &s2mpg11_meter_volatile_table,
+ .num_reg_defaults_raw = S2MPG11_METER_LPF_DATA_NTC7_2 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
struct sec_pmic_acpm_shared_bus_context {
const struct acpm_handle *acpm;
unsigned int acpm_chan_id;
@@ -364,10 +514,12 @@ static int sec_pmic_acpm_probe(struct platform_device *pdev)
if (IS_ERR(regmap_pmic))
return PTR_ERR(regmap_pmic);
- regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_RTC,
- pdata->regmap_cfg_rtc, true);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
+ if (pdata->regmap_cfg_rtc) {
+ regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_RTC,
+ pdata->regmap_cfg_rtc, true);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ }
regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_METER,
pdata->regmap_cfg_meter, true);
@@ -399,8 +551,18 @@ static const struct sec_pmic_acpm_platform_data s2mpg10_data = {
.regmap_cfg_meter = &s2mpg10_regmap_config_meter,
};
+static const struct sec_pmic_acpm_platform_data s2mpg11_data = {
+ .device_type = S2MPG11,
+ .acpm_chan_id = 2,
+ .speedy_channel = 1,
+ .regmap_cfg_common = &s2mpg11_regmap_config_common,
+ .regmap_cfg_pmic = &s2mpg11_regmap_config_pmic,
+ .regmap_cfg_meter = &s2mpg11_regmap_config_meter,
+};
+
static const struct of_device_id sec_pmic_acpm_of_match[] = {
{ .compatible = "samsung,s2mpg10-pmic", .data = &s2mpg10_data, },
+ { .compatible = "samsung,s2mpg11-pmic", .data = &s2mpg11_data, },
{ },
};
MODULE_DEVICE_TABLE(of, sec_pmic_acpm_of_match);
@@ -408,6 +570,7 @@ MODULE_DEVICE_TABLE(of, sec_pmic_acpm_of_match);
static struct platform_driver sec_pmic_acpm_driver = {
.driver = {
.name = "sec-pmic-acpm",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = pm_sleep_ptr(&sec_pmic_pm_ops),
.of_match_table = sec_pmic_acpm_of_match,
},
diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
index 42d55e70e34c..bd8b5f968689 100644
--- a/drivers/mfd/sec-common.c
+++ b/drivers/mfd/sec-common.c
@@ -23,9 +23,13 @@
#include <linux/regmap.h>
#include "sec-core.h"
+static const struct resource s5m8767_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S5M8767_IRQ_RTCA1, "alarm"),
+};
+
static const struct mfd_cell s5m8767_devs[] = {
MFD_CELL_NAME("s5m8767-pmic"),
- MFD_CELL_NAME("s5m-rtc"),
+ MFD_CELL_RES("s5m-rtc", s5m8767_rtc_resources),
MFD_CELL_OF("s5m8767-clk", NULL, NULL, 0, 0, "samsung,s5m8767-clk"),
};
@@ -33,50 +37,72 @@ static const struct mfd_cell s2dos05_devs[] = {
MFD_CELL_NAME("s2dos05-regulator"),
};
+static const struct resource s2mpg10_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPG10_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mpg10_devs[] = {
MFD_CELL_NAME("s2mpg10-meter"),
MFD_CELL_NAME("s2mpg10-regulator"),
- MFD_CELL_NAME("s2mpg10-rtc"),
+ MFD_CELL_RES("s2mpg10-rtc", s2mpg10_rtc_resources),
MFD_CELL_OF("s2mpg10-clk", NULL, NULL, 0, 0, "samsung,s2mpg10-clk"),
MFD_CELL_OF("s2mpg10-gpio", NULL, NULL, 0, 0, "samsung,s2mpg10-gpio"),
};
+static const struct mfd_cell s2mpg11_devs[] = {
+ MFD_CELL_NAME("s2mpg11-meter"),
+ MFD_CELL_NAME("s2mpg11-regulator"),
+ MFD_CELL_OF("s2mpg11-gpio", NULL, NULL, 0, 0, "samsung,s2mpg11-gpio"),
+};
+
+static const struct resource s2mps11_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPS11_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mps11_devs[] = {
MFD_CELL_NAME("s2mps11-regulator"),
- MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_RES("s2mps14-rtc", s2mps11_rtc_resources),
MFD_CELL_OF("s2mps11-clk", NULL, NULL, 0, 0, "samsung,s2mps11-clk"),
};
+static const struct resource s2mps14_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPS14_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mps13_devs[] = {
MFD_CELL_NAME("s2mps13-regulator"),
- MFD_CELL_NAME("s2mps13-rtc"),
+ MFD_CELL_RES("s2mps13-rtc", s2mps14_rtc_resources),
MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
};
static const struct mfd_cell s2mps14_devs[] = {
MFD_CELL_NAME("s2mps14-regulator"),
- MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_RES("s2mps14-rtc", s2mps14_rtc_resources),
MFD_CELL_OF("s2mps14-clk", NULL, NULL, 0, 0, "samsung,s2mps14-clk"),
};
static const struct mfd_cell s2mps15_devs[] = {
MFD_CELL_NAME("s2mps15-regulator"),
- MFD_CELL_NAME("s2mps15-rtc"),
+ MFD_CELL_RES("s2mps15-rtc", s2mps14_rtc_resources),
MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
};
static const struct mfd_cell s2mpa01_devs[] = {
MFD_CELL_NAME("s2mpa01-pmic"),
- MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_RES("s2mps14-rtc", s2mps14_rtc_resources),
};
static const struct mfd_cell s2mpu02_devs[] = {
MFD_CELL_NAME("s2mpu02-regulator"),
};
+static const struct resource s2mpu05_rtc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(S2MPU05_IRQ_RTCA0, "alarm"),
+};
+
static const struct mfd_cell s2mpu05_devs[] = {
MFD_CELL_NAME("s2mpu05-regulator"),
- MFD_CELL_NAME("s2mps15-rtc"),
+ MFD_CELL_RES("s2mps15-rtc", s2mpu05_rtc_resources),
};
static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
@@ -84,8 +110,13 @@ static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
unsigned int val;
/* For s2mpg1x, the revision is in a different regmap */
- if (sec_pmic->device_type == S2MPG10)
+ switch (sec_pmic->device_type) {
+ case S2MPG10:
+ case S2MPG11:
return;
+ default:
+ break;
+ }
/* For each device type, the REG_ID is always the first register */
if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val))
@@ -143,6 +174,7 @@ sec_pmic_parse_dt_pdata(struct device *dev)
int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
struct regmap *regmap, struct i2c_client *client)
{
+ struct regmap_irq_chip_data *irq_data;
struct sec_platform_data *pdata;
const struct mfd_cell *sec_devs;
struct sec_pmic_dev *sec_pmic;
@@ -167,9 +199,9 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
sec_pmic->pdata = pdata;
- ret = sec_irq_init(sec_pmic);
- if (ret)
- return ret;
+ irq_data = sec_irq_init(sec_pmic);
+ if (IS_ERR(irq_data))
+ return PTR_ERR(irq_data);
pm_runtime_set_active(sec_pmic->dev);
@@ -190,6 +222,10 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
sec_devs = s2mpg10_devs;
num_sec_devs = ARRAY_SIZE(s2mpg10_devs);
break;
+ case S2MPG11:
+ sec_devs = s2mpg11_devs;
+ num_sec_devs = ARRAY_SIZE(s2mpg11_devs);
+ break;
case S2MPS11X:
sec_devs = s2mps11_devs;
num_sec_devs = ARRAY_SIZE(s2mps11_devs);
@@ -220,7 +256,7 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
sec_pmic->device_type);
}
ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
- NULL, 0, NULL);
+ NULL, 0, regmap_irq_get_domain(irq_data));
if (ret)
return ret;
diff --git a/drivers/mfd/sec-core.h b/drivers/mfd/sec-core.h
index 92c7558ab8b0..8d85c70c2326 100644
--- a/drivers/mfd/sec-core.h
+++ b/drivers/mfd/sec-core.h
@@ -18,6 +18,6 @@ int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
struct regmap *regmap, struct i2c_client *client);
void sec_pmic_shutdown(struct device *dev);
-int sec_irq_init(struct sec_pmic_dev *sec_pmic);
+struct regmap_irq_chip_data *sec_irq_init(struct sec_pmic_dev *sec_pmic);
#endif /* __SEC_CORE_INT_H */
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index 74ac70002d1f..ee722fa35f3b 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -11,6 +11,7 @@
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/s2mpg10.h>
+#include <linux/mfd/samsung/s2mpg11.h>
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps14.h>
#include <linux/mfd/samsung/s2mpu02.h>
@@ -79,6 +80,64 @@ static const struct regmap_irq s2mpg10_pmic_irqs[] = {
REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH7, 5, S2MPG10_IRQ_PWR_WARN_CH7_MASK),
};
+static const struct regmap_irq s2mpg11_irqs[] = {
+ REGMAP_IRQ_REG(S2MPG11_COMMON_IRQ_PMIC, 0, S2MPG11_COMMON_INT_SRC_PMIC),
+ /* No documentation or other reference for remaining bits */
+ REGMAP_IRQ_REG(S2MPG11_COMMON_IRQ_UNUSED, 0, GENMASK(7, 1)),
+};
+
+static const struct regmap_irq s2mpg11_pmic_irqs[] = {
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWRONF, 0, S2MPG11_IRQ_PWRONF_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWRONR, 0, S2MPG11_IRQ_PWRONR_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PIF_TIMEOUT_MIF, 0, S2MPG11_IRQ_PIF_TIMEOUT_MIF_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PIF_TIMEOUTS, 0, S2MPG11_IRQ_PIF_TIMEOUTS_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_WTSR, 0, S2MPG11_IRQ_WTSR_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_SPD_ABNORMAL_STOP, 0, S2MPG11_IRQ_SPD_ABNORMAL_STOP_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_SPD_PARITY_ERR, 0, S2MPG11_IRQ_SPD_PARITY_ERR_MASK),
+
+ REGMAP_IRQ_REG(S2MPG11_IRQ_140C, 1, S2MPG11_IRQ_INT140C_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_120C, 1, S2MPG11_IRQ_INT120C_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_TSD, 1, S2MPG11_IRQ_TSD_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_WRST, 1, S2MPG11_IRQ_WRST_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_CYCLE_DONE, 1, S2MPG11_IRQ_NTC_CYCLE_DONE_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PMETER_OVERF, 1, S2MPG11_IRQ_PMETER_OVERF_MASK),
+
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B1S, 2, S2MPG11_IRQ_OCP_B1S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B2S, 2, S2MPG11_IRQ_OCP_B2S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B3S, 2, S2MPG11_IRQ_OCP_B3S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B4S, 2, S2MPG11_IRQ_OCP_B4S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B5S, 2, S2MPG11_IRQ_OCP_B5S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B6S, 2, S2MPG11_IRQ_OCP_B6S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B7S, 2, S2MPG11_IRQ_OCP_B7S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B8S, 2, S2MPG11_IRQ_OCP_B8S_MASK),
+
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B9S, 3, S2MPG11_IRQ_OCP_B9S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_B10S, 3, S2MPG11_IRQ_OCP_B10S_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_BDS, 3, S2MPG11_IRQ_OCP_BDS_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_BAS, 3, S2MPG11_IRQ_OCP_BAS_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_OCP_BBS, 3, S2MPG11_IRQ_OCP_BBS_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_WLWP_ACC, 3, S2MPG11_IRQ_WLWP_ACC_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_SPD_SRP_PKT_RST, 3, S2MPG11_IRQ_SPD_SRP_PKT_RST_MASK),
+
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH0, 4, S2MPG11_IRQ_PWR_WARN_CH0_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH1, 4, S2MPG11_IRQ_PWR_WARN_CH1_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH2, 4, S2MPG11_IRQ_PWR_WARN_CH2_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH3, 4, S2MPG11_IRQ_PWR_WARN_CH3_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH4, 4, S2MPG11_IRQ_PWR_WARN_CH4_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH5, 4, S2MPG11_IRQ_PWR_WARN_CH5_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH6, 4, S2MPG11_IRQ_PWR_WARN_CH6_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_PWR_WARN_CH7, 4, S2MPG11_IRQ_PWR_WARN_CH7_MASK),
+
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH0, 5, S2MPG11_IRQ_NTC_WARN_CH0_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH1, 5, S2MPG11_IRQ_NTC_WARN_CH1_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH2, 5, S2MPG11_IRQ_NTC_WARN_CH2_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH3, 5, S2MPG11_IRQ_NTC_WARN_CH3_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH4, 5, S2MPG11_IRQ_NTC_WARN_CH4_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH5, 5, S2MPG11_IRQ_NTC_WARN_CH5_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH6, 5, S2MPG11_IRQ_NTC_WARN_CH6_MASK),
+ REGMAP_IRQ_REG(S2MPG11_IRQ_NTC_WARN_CH7, 5, S2MPG11_IRQ_NTC_WARN_CH7_MASK),
+};
+
static const struct regmap_irq s2mps11_irqs[] = {
REGMAP_IRQ_REG(S2MPS11_IRQ_PWRONF, 0, S2MPS11_IRQ_PWRONF_MASK),
REGMAP_IRQ_REG(S2MPS11_IRQ_PWRONR, 0, S2MPS11_IRQ_PWRONR_MASK),
@@ -186,7 +245,7 @@ static const struct regmap_irq s5m8767_irqs[] = {
REGMAP_IRQ_REG(S5M8767_IRQ_WTSR, 2, S5M8767_IRQ_WTSR_MASK),
};
-/* All S2MPG10 interrupt sources are read-only and don't require clearing */
+/* All S2MPG1x interrupt sources are read-only and don't require clearing */
static const struct regmap_irq_chip s2mpg10_irq_chip = {
.name = "s2mpg10",
.status_base = S2MPG10_COMMON_INT,
@@ -205,6 +264,25 @@ static const struct regmap_irq_chip s2mpg10_irq_chip_pmic = {
.num_irqs = ARRAY_SIZE(s2mpg10_pmic_irqs),
};
+static const struct regmap_irq_chip s2mpg11_irq_chip = {
+ .name = "s2mpg11",
+ .status_base = S2MPG11_COMMON_INT,
+ .mask_base = S2MPG11_COMMON_INT_MASK,
+ .num_regs = 1,
+ .irqs = s2mpg11_irqs,
+ .num_irqs = ARRAY_SIZE(s2mpg11_irqs),
+};
+
+static const struct regmap_irq_chip s2mpg11_irq_chip_pmic = {
+ .name = "s2mpg11-pmic",
+ .domain_suffix = "pmic",
+ .status_base = S2MPG11_PMIC_INT1,
+ .mask_base = S2MPG11_PMIC_INT1M,
+ .num_regs = 6,
+ .irqs = s2mpg11_pmic_irqs,
+ .num_irqs = ARRAY_SIZE(s2mpg11_pmic_irqs),
+};
+
static const struct regmap_irq_chip s2mps11_irq_chip = {
.name = "s2mps11",
.irqs = s2mps11_irqs,
@@ -268,26 +346,28 @@ static const struct regmap_irq_chip s5m8767_irq_chip = {
.ack_base = S5M8767_REG_INT1,
};
-static int s2mpg1x_add_chained_irq_chip(struct device *dev, struct regmap *regmap, int pirq,
- struct regmap_irq_chip_data *parent,
- const struct regmap_irq_chip *chip,
- struct regmap_irq_chip_data **data)
+static struct regmap_irq_chip_data *
+s2mpg1x_add_chained_pmic(struct sec_pmic_dev *sec_pmic, int pirq,
+ struct regmap_irq_chip_data *parent, const struct regmap_irq_chip *chip)
{
+ struct device *dev = sec_pmic->dev;
+ struct regmap_irq_chip_data *data;
int irq, ret;
irq = regmap_irq_get_virq(parent, pirq);
if (irq < 0)
- return dev_err_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", pirq,
- chip->name);
+ return dev_err_ptr_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n",
+ pirq, chip->name);
- ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT | IRQF_SHARED, 0, chip, data);
+ ret = devm_regmap_add_irq_chip(dev, sec_pmic->regmap_pmic, irq,
+ IRQF_ONESHOT | IRQF_SHARED, 0, chip, &data);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name);
+ return dev_err_ptr_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name);
- return 0;
+ return data;
}
-static int sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
+static struct regmap_irq_chip_data *sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
{
const struct regmap_irq_chip *irq_chip, *chained_irq_chip;
struct regmap_irq_chip_data *irq_data;
@@ -301,28 +381,33 @@ static int sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
chained_irq_chip = &s2mpg10_irq_chip_pmic;
chained_pirq = S2MPG10_COMMON_IRQ_PMIC;
break;
+ case S2MPG11:
+ irq_chip = &s2mpg11_irq_chip;
+ chained_irq_chip = &s2mpg11_irq_chip_pmic;
+ chained_pirq = S2MPG11_COMMON_IRQ_PMIC;
+ break;
default:
- return dev_err_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
- sec_pmic->device_type);
+ return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
+ sec_pmic->device_type);
}
regmap_common = dev_get_regmap(sec_pmic->dev, "common");
if (!regmap_common)
- return dev_err_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n",
- sec_pmic->device_type);
+ return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n",
+ sec_pmic->device_type);
ret = devm_regmap_add_irq_chip(sec_pmic->dev, regmap_common, sec_pmic->irq, IRQF_ONESHOT, 0,
irq_chip, &irq_data);
if (ret)
- return dev_err_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
- irq_chip->name);
+ return dev_err_ptr_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
+ irq_chip->name);
- return s2mpg1x_add_chained_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic, chained_pirq,
- irq_data, chained_irq_chip, &sec_pmic->irq_data);
+ return s2mpg1x_add_chained_pmic(sec_pmic, chained_pirq, irq_data, chained_irq_chip);
}
-int sec_irq_init(struct sec_pmic_dev *sec_pmic)
+struct regmap_irq_chip_data *sec_irq_init(struct sec_pmic_dev *sec_pmic)
{
+ struct regmap_irq_chip_data *sec_irq_chip_data;
const struct regmap_irq_chip *sec_irq_chip;
int ret;
@@ -331,11 +416,12 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
sec_irq_chip = &s5m8767_irq_chip;
break;
case S2DOS05:
- return 0;
+ return NULL;
case S2MPA01:
sec_irq_chip = &s2mps14_irq_chip;
break;
case S2MPG10:
+ case S2MPG11:
return sec_irq_init_s2mpg1x(sec_pmic);
case S2MPS11X:
sec_irq_chip = &s2mps11_irq_chip;
@@ -356,30 +442,22 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
sec_irq_chip = &s2mpu05_irq_chip;
break;
default:
- return dev_err_probe(sec_pmic->dev, -EINVAL,
- "Unsupported device type %d\n",
- sec_pmic->device_type);
+ return dev_err_ptr_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
+ sec_pmic->device_type);
}
if (!sec_pmic->irq) {
dev_warn(sec_pmic->dev,
"No interrupt specified, no interrupts\n");
- return 0;
+ return NULL;
}
ret = devm_regmap_add_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic,
sec_pmic->irq, IRQF_ONESHOT,
- 0, sec_irq_chip, &sec_pmic->irq_data);
+ 0, sec_irq_chip, &sec_irq_chip_data);
if (ret)
- return dev_err_probe(sec_pmic->dev, ret,
- "Failed to add %s IRQ chip\n",
- sec_irq_chip->name);
-
- /*
- * The rtc-s5m driver requests S2MPS14_IRQ_RTCA0 also for S2MPS11
- * so the interrupt number must be consistent.
- */
- BUILD_BUG_ON(((enum s2mps14_irq)S2MPS11_IRQ_RTCA0) != S2MPS14_IRQ_RTCA0);
+ return dev_err_ptr_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
+ sec_irq_chip->name);
- return 0;
+ return sec_irq_chip_data;
}
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index d2335276cce5..35d58bc72a56 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -659,6 +659,15 @@ config REGULATOR_MAX77650
Semiconductor. This device has a SIMO with three independent
power rails and an LDO.
+config REGULATOR_MAX77675
+ tristate "Maxim MAX77675 regulator driver"
+ depends on I2C && OF
+ select REGMAP_I2C
+ help
+ This driver controls the Maxim MAX77675 power regulator via I2C.
+ It supports four programmable buck-boost outputs.
+ Say Y here to enable the regulator driver
+
config REGULATOR_MAX77857
tristate "ADI MAX77857/MAX77831 regulator support"
depends on I2C
@@ -1394,6 +1403,15 @@ config REGULATOR_RT6245
It can support up to 14A output current and adjustable output voltage
from 0.4375V to 1.3875V, per step 12.5mV.
+config REGULATOR_RT8092
+ tristate "Richtek RT8092 voltage regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ The RT8092 is a peak-current mode PWM step-down DC/DC converter with
+ I2C control interface. It is capable of delivering 4A continuing
+ current over a wide input range from 2.5V to 5.5V.
+
config REGULATOR_RTQ2134
tristate "Richtek RTQ2134 SubPMIC Regulator"
depends on I2C
@@ -1690,6 +1708,17 @@ config REGULATOR_TPS65132
This driver supports TPS65132 single inductor - dual output
power supply specifically designed for display panels.
+config REGULATOR_TPS65185
+ tristate "TI TPS65185 EPD regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports the TPS65185 voltage regulator chip
+ which is used to provide power to Electronic Paper Displays
+ so it is found in E-Book readers.
+ If HWWON is enabled, it also provides temperature measurement.
+
+
config REGULATOR_TPS65217
tristate "TI TPS65217 Power regulators"
depends on MFD_TPS65217
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 1beba1493241..35639f3115fd 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_REGULATOR_MAX77503) += max77503-regulator.o
obj-$(CONFIG_REGULATOR_MAX77541) += max77541-regulator.o
obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
obj-$(CONFIG_REGULATOR_MAX77650) += max77650-regulator.o
+obj-$(CONFIG_REGULATOR_MAX77675) += max77675-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8893) += max8893.o
@@ -161,6 +162,7 @@ obj-$(CONFIG_REGULATOR_RT5759) += rt5759-regulator.o
obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o
obj-$(CONFIG_REGULATOR_RT6190) += rt6190-regulator.o
obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o
+obj-$(CONFIG_REGULATOR_RT8092) += rt8092.o
obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o
obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o
obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o
@@ -192,6 +194,7 @@ obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65185) += tps65185.o
obj-$(CONFIG_REGULATOR_TPS65217) += tps65217-regulator.o
obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o
obj-$(CONFIG_REGULATOR_TPS65219) += tps65219-regulator.o
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 4b6182cde859..8c2fd20edd50 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -44,6 +44,8 @@ static LIST_HEAD(regulator_supply_alias_list);
static LIST_HEAD(regulator_coupler_list);
static bool has_full_constraints;
+static const struct bus_type regulator_bus;
+
static struct dentry *debugfs_root;
/*
@@ -96,6 +98,7 @@ struct regulator_event_work {
unsigned long event;
};
+static int _regulator_enable(struct regulator *regulator);
static int _regulator_is_enabled(struct regulator_dev *rdev);
static int _regulator_disable(struct regulator *regulator);
static int _regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags);
@@ -1183,7 +1186,7 @@ static void print_constraints_debug(struct regulator_dev *rdev)
count += scnprintf(buf + count, len - count, "standby ");
if (constraints->pw_budget_mW)
- count += scnprintf(buf + count, len - count, "%d mW budget",
+ count += scnprintf(buf + count, len - count, "%d mW budget ",
constraints->pw_budget_mW);
if (!count)
@@ -1430,6 +1433,7 @@ static int handle_notify_limits(struct regulator_dev *rdev,
/**
* set_machine_constraints - sets regulator constraints
* @rdev: regulator source
+ * @is_locked: whether or not this is called with locks held already
*
* Allows platform initialisation code to define and constrain
* regulator circuits e.g. valid voltage/current ranges, etc. NOTE:
@@ -1439,11 +1443,39 @@ static int handle_notify_limits(struct regulator_dev *rdev,
*
* Return: 0 on success or a negative error number on failure.
*/
-static int set_machine_constraints(struct regulator_dev *rdev)
+static int set_machine_constraints(struct regulator_dev *rdev,
+ bool is_locked)
{
int ret = 0;
const struct regulator_ops *ops = rdev->desc->ops;
+ /*
+ * If there is no mechanism for controlling the regulator then
+ * flag it as always_on so we don't end up duplicating checks
+ * for this so much. Note that we could control the state of
+ * a supply to control the output on a regulator that has no
+ * direct control.
+ */
+ if (!rdev->ena_pin && !ops->enable) {
+ if (rdev->supply_name && !rdev->supply)
+ return -EPROBE_DEFER;
+
+ if (rdev->supply)
+ rdev->constraints->always_on =
+ rdev->supply->rdev->constraints->always_on;
+ else
+ rdev->constraints->always_on = true;
+ }
+
+ /*
+ * If we want to enable this regulator, make sure that we know the
+ * supplying regulator.
+ */
+ if (rdev->constraints->always_on || rdev->constraints->boot_on) {
+ if (rdev->supply_name && !rdev->supply)
+ return -EPROBE_DEFER;
+ }
+
ret = machine_constraints_voltage(rdev, rdev->constraints);
if (ret != 0)
return ret;
@@ -1609,44 +1641,24 @@ static int set_machine_constraints(struct regulator_dev *rdev)
}
}
- /*
- * If there is no mechanism for controlling the regulator then
- * flag it as always_on so we don't end up duplicating checks
- * for this so much. Note that we could control the state of
- * a supply to control the output on a regulator that has no
- * direct control.
- */
- if (!rdev->ena_pin && !ops->enable) {
- if (rdev->supply_name && !rdev->supply)
- return -EPROBE_DEFER;
-
- if (rdev->supply)
- rdev->constraints->always_on =
- rdev->supply->rdev->constraints->always_on;
- else
- rdev->constraints->always_on = true;
- }
-
/* 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) {
bool supply_enabled = false;
- /* If we want to enable this regulator, make sure that we know
- * the supplying regulator.
- */
- if (rdev->supply_name && !rdev->supply)
- return -EPROBE_DEFER;
-
- /* If supplying regulator has already been enabled,
+ /* We have ensured a potential supply has been resolved above.
+ *
+ * If supplying regulator has already been enabled,
* it's not intended to have use_count increment
* when rdev is only boot-on.
*/
if (rdev->supply &&
(rdev->constraints->always_on ||
!regulator_is_enabled(rdev->supply))) {
- ret = regulator_enable(rdev->supply);
+ ret = (is_locked
+ ? _regulator_enable(rdev->supply)
+ : regulator_enable(rdev->supply));
if (ret < 0) {
_regulator_put(rdev->supply);
rdev->supply = NULL;
@@ -1774,6 +1786,15 @@ static int register_regulator_event_forwarding(struct regulator_dev *rdev)
return 0;
}
+static void unregister_regulator_event_forwarding(struct regulator_dev *rdev)
+{
+ if (!rdev->supply_fwd_nb.notifier_call)
+ return;
+
+ regulator_unregister_notifier(rdev->supply, &rdev->supply_fwd_nb);
+ rdev->supply_fwd_nb.notifier_call = NULL;
+}
+
/**
* set_supply - set regulator supply regulator
* @rdev: regulator (locked)
@@ -2162,6 +2183,8 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
struct regulator_dev *r;
struct device *dev = rdev->dev.parent;
struct ww_acquire_ctx ww_ctx;
+ struct regulator *supply;
+ bool do_final_setup;
int ret = 0;
/* No supply to resolve? */
@@ -2169,7 +2192,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
return 0;
/* Supply already resolved? (fast-path without locking contention) */
- if (rdev->supply)
+ if (rdev->supply && !rdev->constraints_pending)
return 0;
/* first do a dt based lookup on the node described in the virtual
@@ -2250,49 +2273,115 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
/* Supply just resolved by a concurrent task? */
if (rdev->supply) {
- regulator_unlock_two(rdev, r, &ww_ctx);
- put_device(&r->dev);
- goto out;
- }
+ /* Constraints might still be pending due to concurrency. */
+ bool done = !rdev->constraints_pending;
+
+ supply = rdev->supply;
- ret = set_supply(rdev, r);
- if (ret < 0) {
regulator_unlock_two(rdev, r, &ww_ctx);
put_device(&r->dev);
- goto out;
- }
- /*
- * Automatically register for event forwarding from the new supply.
- * This creates the downstream propagation link for events like
- * under-voltage.
- */
- ret = register_regulator_event_forwarding(rdev);
- if (ret < 0)
- rdev_warn(rdev, "Failed to register event forwarding: %pe\n",
- ERR_PTR(ret));
+ /*
+ * Supply resolved by concurrent task, and constraints set as
+ * well (or not required): fast path.
+ */
+ if (done)
+ goto out;
- regulator_unlock_two(rdev, r, &ww_ctx);
+ do_final_setup = false;
+ } else {
+ ret = set_supply(rdev, r);
+ if (ret < 0) {
+ regulator_unlock_two(rdev, r, &ww_ctx);
+ put_device(&r->dev);
+ goto out;
+ }
- /* rdev->supply was created in set_supply() */
- link_and_create_debugfs(rdev->supply, r, &rdev->dev);
+ supply = rdev->supply;
+
+ /*
+ * Automatically register for event forwarding from the new
+ * supply. This creates the downstream propagation link for
+ * events like under-voltage.
+ */
+ ret = register_regulator_event_forwarding(rdev);
+ if (ret < 0) {
+ rdev_warn(rdev,
+ "Failed to register event forwarding: %pe\n",
+ ERR_PTR(ret));
+
+ goto unset_supply;
+ }
+
+ regulator_unlock_two(rdev, r, &ww_ctx);
+
+ do_final_setup = true;
+ }
/*
- * 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.
+ * Now that we have the supply, we can retry setting the machine
+ * constraints, if necessary.
*/
- if (rdev->use_count) {
- ret = regulator_enable(rdev->supply);
- if (ret < 0) {
- _regulator_put(rdev->supply);
- rdev->supply = NULL;
+ regulator_lock_dependent(rdev, &ww_ctx);
+ if (rdev->constraints_pending) {
+ if (!rdev->supply) {
+ /*
+ * Supply could have been released by another task that
+ * failed to set the constraints or event forwarding.
+ */
+ regulator_unlock_dependent(rdev, &ww_ctx);
+ ret = -EPROBE_DEFER;
goto out;
}
+
+ ret = set_machine_constraints(rdev, true);
+ if (ret < 0) {
+ regulator_unlock_dependent(rdev, &ww_ctx);
+
+ rdev_warn(rdev,
+ "Failed to set machine constraints: %pe\n",
+ ERR_PTR(ret));
+
+ regulator_lock_two(rdev, r, &ww_ctx);
+
+ if (supply != rdev->supply) {
+ /*
+ * Supply could have been released by another
+ * task that got here before us. If it did, it
+ * will have released 'supply' (i.e. the
+ * previous rdev->supply) and we shouldn't do
+ * that again via unset_supply.
+ */
+ regulator_unlock_two(rdev, r, &ww_ctx);
+ goto out;
+ }
+
+ unregister_regulator_event_forwarding(rdev);
+ rdev->constraints_pending = true;
+ goto unset_supply;
+ }
+ rdev->constraints_pending = false;
}
+ regulator_unlock_dependent(rdev, &ww_ctx);
+
+ if (!do_final_setup)
+ goto out;
+
+ /* rdev->supply was created in set_supply() */
+ link_and_create_debugfs(rdev->supply, rdev->supply->rdev, &rdev->dev);
out:
return ret;
+
+unset_supply:
+ lockdep_assert_held_once(&rdev->mutex.base);
+ lockdep_assert_held_once(&r->mutex.base);
+ rdev->supply = NULL;
+ regulator_unlock_two(rdev, supply->rdev, &ww_ctx);
+
+ regulator_put(supply);
+
+ return ret;
}
/* common pre-checks for regulator requests */
@@ -5692,16 +5781,6 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
&rdev->bypass_count);
}
-static int regulator_register_resolve_supply(struct device *dev, void *data)
-{
- struct regulator_dev *rdev = dev_to_rdev(dev);
-
- if (regulator_resolve_supply(rdev))
- rdev_dbg(rdev, "unable to resolve supply\n");
-
- return 0;
-}
-
int regulator_coupler_register(struct regulator_coupler *coupler)
{
mutex_lock(&regulator_list_mutex);
@@ -5920,10 +5999,10 @@ regulator_register(struct device *dev,
struct regulator_config *config = NULL;
static atomic_t regulator_no = ATOMIC_INIT(-1);
struct regulator_dev *rdev;
+ bool tried_supply_resolve = false;
bool dangling_cfg_gpiod = false;
bool dangling_of_gpiod = false;
int ret, i;
- bool resolved_early = false;
if (cfg == NULL)
return ERR_PTR(-EINVAL);
@@ -6061,17 +6140,6 @@ regulator_register(struct device *dev,
goto wash;
}
- if ((rdev->supply_name && !rdev->supply) &&
- (rdev->constraints->always_on ||
- rdev->constraints->boot_on)) {
- ret = regulator_resolve_supply(rdev);
- if (ret)
- rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
- ERR_PTR(ret));
-
- resolved_early = true;
- }
-
if (config->ena_gpiod) {
ret = regulator_ena_gpio_request(rdev, config);
if (ret != 0) {
@@ -6084,10 +6152,11 @@ regulator_register(struct device *dev,
dangling_of_gpiod = false;
}
- ret = set_machine_constraints(rdev);
- if (ret == -EPROBE_DEFER && !resolved_early) {
- /* Regulator might be in bypass mode and so needs its supply
- * to set the constraints
+ ret = set_machine_constraints(rdev, false);
+ if (ret == -EPROBE_DEFER) {
+ /* Regulator might be in bypass mode or an always-on or boot-on
+ * regulator and so needs its supply to set the constraints or
+ * for enable.
*/
/* FIXME: this currently triggers a chicken-and-egg problem
* when creating -SUPPLY symlink in sysfs to a regulator
@@ -6097,13 +6166,17 @@ regulator_register(struct device *dev,
rdev->supply_name);
ret = regulator_resolve_supply(rdev);
if (!ret)
- ret = set_machine_constraints(rdev);
+ ret = set_machine_constraints(rdev, false);
else
rdev_dbg(rdev, "unable to resolve supply early: %pe\n",
ERR_PTR(ret));
+ tried_supply_resolve = true;
+ }
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ goto wash;
+ rdev->constraints_pending = true;
}
- if (ret < 0)
- goto wash;
ret = regulator_init_coupling(rdev);
if (ret < 0)
@@ -6132,6 +6205,37 @@ regulator_register(struct device *dev,
if (ret != 0)
goto unset_supplies;
+ if (!tried_supply_resolve) {
+ /*
+ * As an optimisation, try to resolve our supply (if any) now to
+ * avoid adding the bus device. Errors are not fatal at this
+ * stage, we'll simply try again later.
+ */
+ ret = regulator_resolve_supply(rdev);
+ if (ret)
+ rdev_dbg(rdev,
+ "unable to resolve supply (ignoring): %pe\n",
+ ERR_PTR(ret));
+ }
+
+ /*
+ * If we have a supply but couldn't resolve it yet, register a device
+ * with our bus, so that the bus probe gets called whenever any new
+ * driver binds, allowing us to retry matching supplies and which then
+ * triggers (re)probe of consumers if successful.
+ */
+ if (rdev->supply_name && !rdev->supply) {
+ device_initialize(&rdev->bdev);
+ rdev->bdev.bus = &regulator_bus;
+ rdev->bdev.parent = &rdev->dev;
+ device_set_pm_not_required(&rdev->dev);
+ dev_set_name(&rdev->bdev, "%s.bdev", dev_name(&rdev->dev));
+
+ ret = device_add(&rdev->bdev);
+ if (ret)
+ goto del_cdev_and_bdev;
+ }
+
rdev_init_debugfs(rdev);
/* try to resolve regulators coupling since a new one was registered */
@@ -6139,12 +6243,13 @@ regulator_register(struct device *dev,
regulator_resolve_coupling(rdev);
mutex_unlock(&regulator_list_mutex);
- /* try to resolve regulators supply since a new one was registered */
- class_for_each_device(&regulator_class, NULL, NULL,
- regulator_register_resolve_supply);
kfree(config);
return rdev;
+del_cdev_and_bdev:
+ if (rdev->bdev.bus == &regulator_bus)
+ put_device(&rdev->bdev);
+ device_del(&rdev->dev);
unset_supplies:
mutex_lock(&regulator_list_mutex);
unset_regulator_supplies(rdev);
@@ -6197,6 +6302,9 @@ void regulator_unregister(struct regulator_dev *rdev)
unset_regulator_supplies(rdev);
list_del(&rdev->list);
regulator_ena_gpio_free(rdev);
+ if (rdev->bdev.bus == &regulator_bus)
+ /* only if the device was added in the first place */
+ device_unregister(&rdev->bdev);
device_unregister(&rdev->dev);
mutex_unlock(&regulator_list_mutex);
@@ -6277,6 +6385,45 @@ const struct class regulator_class = {
.pm = &regulator_pm_ops,
#endif
};
+
+#define bdev_to_rdev(__bdev) container_of_const(__bdev, struct regulator_dev, bdev)
+
+static int regulator_bus_match(struct device *bdev,
+ const struct device_driver *drv)
+{
+ /* Match always succeeds, we only have one driver */
+ return 1;
+}
+
+static int regulator_bus_probe(struct device *bdev)
+{
+ struct regulator_dev *rdev = bdev_to_rdev(bdev);
+ int ret;
+
+ ret = regulator_resolve_supply(rdev);
+ if (ret)
+ rdev_dbg(rdev,
+ "unable to resolve supply or constraints '%s': %pe\n",
+ rdev->supply_name, ERR_PTR(ret));
+ else
+ rdev_dbg(rdev, "resolved supply '%s'\n", rdev->supply_name);
+
+ return ret;
+}
+
+static const struct bus_type regulator_bus = {
+ .name = "regulator",
+ .match = regulator_bus_match,
+ .probe = regulator_bus_probe,
+};
+
+static struct device_driver regulator_bus_driver = {
+ .name = "regulator-bus-drv",
+ .bus = &regulator_bus,
+ .suppress_bind_attrs = true,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+};
+
/**
* regulator_has_full_constraints - the system has fully specified constraints
*
@@ -6610,7 +6757,17 @@ static int __init regulator_init(void)
{
int ret;
+ ret = bus_register(&regulator_bus);
+ if (ret)
+ return ret;
+
ret = class_register(&regulator_class);
+ if (ret)
+ goto err_class;
+
+ ret = driver_register(&regulator_bus_driver);
+ if (ret)
+ goto err_driver;
debugfs_root = debugfs_create_dir("regulator", NULL);
if (IS_ERR(debugfs_root))
@@ -6627,6 +6784,12 @@ static int __init regulator_init(void)
regulator_coupler_register(&generic_regulator_coupler);
+ return 0;
+
+err_driver:
+ class_unregister(&regulator_class);
+err_class:
+ bus_unregister(&regulator_bus);
return ret;
}
@@ -6688,16 +6851,6 @@ __setup("regulator_ignore_unused", regulator_ignore_unused_setup);
static void regulator_init_complete_work_function(struct work_struct *work)
{
/*
- * Regulators may had failed to resolve their input supplies
- * when were registered, either because the input supply was
- * not registered yet or because its parent device was not
- * bound yet. So attempt to resolve the input supplies for
- * pending regulators before trying to disable unused ones.
- */
- class_for_each_device(&regulator_class, NULL, NULL,
- regulator_register_resolve_supply);
-
- /*
* For debugging purposes, it may be useful to prevent unused
* regulators from being disabled.
*/
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
index e5197ec7234d..c3e416fd3c3e 100644
--- a/drivers/regulator/dummy.c
+++ b/drivers/regulator/dummy.c
@@ -56,7 +56,7 @@ static int dummy_regulator_probe(struct faux_device *fdev)
return 0;
}
-struct faux_device_ops dummy_regulator_driver = {
+static struct faux_device_ops dummy_regulator_driver = {
.probe = dummy_regulator_probe,
};
diff --git a/drivers/regulator/max77675-regulator.c b/drivers/regulator/max77675-regulator.c
new file mode 100644
index 000000000000..af3eb7174875
--- /dev/null
+++ b/drivers/regulator/max77675-regulator.c
@@ -0,0 +1,1056 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Analog Devices, Inc.
+ * ADI regulator driver for MAX77675.
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/cleanup.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+
+/* Register Addresses */
+#define MAX77675_REG_CNFG_GLBL_A 0x00
+#define MAX77675_REG_CNFG_GLBL_B 0x01
+#define MAX77675_REG_INT_GLBL 0x02
+#define MAX77675_REG_INTM_GLBL 0x03
+#define MAX77675_REG_STAT_GLBL 0x04
+#define MAX77675_REG_ERCF_GLBL 0x05
+#define MAX77675_REG_CID 0x06
+#define MAX77675_REG_CNFG_SBB_TOP_A 0x07
+#define MAX77675_REG_CNFG_SBB0_A 0x08
+#define MAX77675_REG_CNFG_SBB0_B 0x09
+#define MAX77675_REG_CNFG_SBB1_A 0x0A
+#define MAX77675_REG_CNFG_SBB1_B 0x0B
+#define MAX77675_REG_CNFG_SBB2_A 0x0C
+#define MAX77675_REG_CNFG_SBB2_B 0x0D
+#define MAX77675_REG_CNFG_SBB3_A 0x0E
+#define MAX77675_REG_CNFG_SBB3_B 0x0F
+#define MAX77675_REG_CNFG_SBB_TOP_B 0x10
+
+/* CNFG_GLBL_A (0x00) bit masks and shifts */
+#define MAX77675_MRT_MASK GENMASK(7, 6) /* Manual Reset Time (bits 7:6) */
+#define MAX77675_MRT_SHIFT 6
+#define MAX77675_PU_DIS_BIT BIT(5) /* Pullup Disable (bit 5) */
+#define MAX77675_PU_DIS_SHIFT 5
+#define MAX77675_BIAS_LPM_BIT BIT(4) /* Bias Low Power Mode (bit 4) */
+#define MAX77675_BIAS_LPM_SHIFT 4
+#define MAX77675_SIMO_CH_DIS_BIT BIT(3) /* SIMO Internal Channel Disable (bit 3) */
+#define MAX77675_SIMO_CH_DIS_SHIFT 3
+#define MAX77675_EN_MODE_MASK GENMASK(2, 1) /* nEN Mode (bits 2:1) */
+#define MAX77675_EN_MODE_SHIFT 1
+#define MAX77675_DBEN_EN_BIT BIT(0) /* Debounce Enable (bit 0) */
+#define MAX77675_DBEN_EN_SHIFT 0
+
+/* CNFG_GLBL_B (0x01) */
+#define MAX77675_SFT_CTRL_MASK GENMASK(2, 0) /* Soft Start Control */
+#define MAX77675_SFT_CTRL_SHIFT 0
+
+/* INT_GLBL (0x02) bit bits and shifts */
+#define MAX77675_INT_SBB3_F_BIT BIT(7)
+#define MAX77675_INT_SBB3_F_SHIFT 7
+#define MAX77675_INT_SBB2_F_BIT BIT(6)
+#define MAX77675_INT_SBB2_F_SHIFT 6
+#define MAX77675_INT_SBB1_F_BIT BIT(5)
+#define MAX77675_INT_SBB1_F_SHIFT 5
+#define MAX77675_INT_SBB0_F_BIT BIT(4)
+#define MAX77675_INT_SBB0_F_SHIFT 4
+#define MAX77675_INT_TJAL2_R_BIT BIT(3)
+#define MAX77675_INT_TJAL2_R_SHIFT 3
+#define MAX77675_INT_TJAL1_R_BIT BIT(2)
+#define MAX77675_INT_TJAL1_R_SHIFT 2
+#define MAX77675_INT_EN_R_BIT BIT(1)
+#define MAX77675_INT_EN_R_SHIFT 1
+#define MAX77675_INT_EN_F_BIT BIT(0)
+#define MAX77675_INT_EN_F_SHIFT 0
+
+/* INTM_GLBL (0x03) bits and shifts */
+#define MAX77675_INTM_SBB3_F_BIT BIT(7)
+#define MAX77675_INTM_SBB3_F_SHIFT 7
+#define MAX77675_INTM_SBB2_F_BIT BIT(6)
+#define MAX77675_INTM_SBB2_F_SHIFT 6
+#define MAX77675_INTM_SBB1_F_BIT BIT(5)
+#define MAX77675_INTM_SBB1_F_SHIFT 5
+#define MAX77675_INTM_SBB0_F_BIT BIT(4)
+#define MAX77675_INTM_SBB0_F_SHIFT 4
+#define MAX77675_INTM_TJAL2_R_BIT BIT(3)
+#define MAX77675_INTM_TJAL2_R_SHIFT 3
+#define MAX77675_INTM_TJAL1_R_BIT BIT(2)
+#define MAX77675_INTM_TJAL1_R_SHIFT 2
+#define MAX77675_INTM_EN_R_BIT BIT(1)
+#define MAX77675_INTM_EN_R_SHIFT 1
+#define MAX77675_INTM_EN_F_BIT BIT(0)
+#define MAX77675_INTM_EN_F_SHIFT 0
+
+/* STAT_GLBL (0x04) bits and shifts */
+#define MAX77675_STAT_SBB3_S_BIT BIT(7)
+#define MAX77675_STAT_SBB3_S_SHIFT 7
+#define MAX77675_STAT_SBB2_S_BIT BIT(6)
+#define MAX77675_STAT_SBB2_S_SHIFT 6
+#define MAX77675_STAT_SBB1_S_BIT BIT(5)
+#define MAX77675_STAT_SBB1_S_SHIFT 5
+#define MAX77675_STAT_SBB0_S_BIT BIT(4)
+#define MAX77675_STAT_SBB0_S_SHIFT 4
+#define MAX77675_STAT_TJAL2_S_BIT BIT(2)
+#define MAX77675_STAT_TJAL2_S_SHIFT 2
+#define MAX77675_STAT_TJAL1_S_BIT BIT(1)
+#define MAX77675_STAT_TJAL1_S_SHIFT 1
+#define MAX77675_STAT_STAT_EN_BIT BIT(0)
+#define MAX77675_STAT_STAT_EN_SHIFT 0
+
+#define MAX77675_STAT_STAT_EN_BIT BIT(0)
+#define MAX77675_STAT_STAT_EN_SHIFT 0
+
+/* ERCFLAG (0x05) bits and shifts */
+#define MAX77675_SFT_CRST_F_BIT BIT(5) /* Software Cold Reset Flag */
+#define MAX77675_SFT_CRST_F_SHIFT 5
+#define MAX77675_SFT_OFF_F_BIT BIT(4) /* Software Off Flag */
+#define MAX77675_SFT_OFF_F_SHIFT 4
+#define MAX77675_MRST_BIT BIT(3) /* Manual Reset Timer Flag */
+#define MAX77675_MRST_SHIFT 3
+#define MAX77675_UVLO_BIT BIT(2) /* Undervoltage Lockout Flag */
+#define MAX77675_UVLO_SHIFT 2
+#define MAX77675_OVLO_BIT BIT(1) /* Overvoltage Lockout Flag */
+#define MAX77675_OVLO_SHIFT 1
+#define MAX77675_TOVLD_BIT BIT(0) /* Thermal Overload Flag */
+#define MAX77675_TOVLD_SHIFT 0
+
+/* CID (0x06) bits and shifts */
+#define MAX77675_CID_MASK GENMASK(4, 0) /* Chip Identification Code mask */
+#define MAX77675_CID_SHIFT 0 /* Starts at bit 0 */
+
+/* CNFG_SBB_TOP_A (0x07) bits and shifts */
+#define MAX77675_STEP_SZ_SBB3_BIT BIT(5)
+#define MAX77675_STEP_SZ_SBB3_SHIFT 5
+#define MAX77675_STEP_SZ_SBB2_BIT BIT(4)
+#define MAX77675_STEP_SZ_SBB2_SHIFT 4
+#define MAX77675_STEP_SZ_SBB1_BIT BIT(3)
+#define MAX77675_STEP_SZ_SBB1_SHIFT 3
+#define MAX77675_STEP_SZ_SBB0_BIT BIT(2)
+#define MAX77675_STEP_SZ_SBB0_SHIFT 2
+#define MAX77675_DRV_SBB_MASK GENMASK(1, 0)
+#define MAX77675_DRV_SBB_SHIFT 0
+
+/* CNFG_SBB0_A (0x08) bits and shifts */
+#define MAX77675_TV_SBB0_MASK GENMASK(7, 0)
+#define MAX77675_TV_SBB0_SHIFT 0
+
+/* CNFG_SBB0_B (0x09) bits and shifts */
+#define MAX77675_ADE_SBB0_BIT BIT(3)
+#define MAX77675_ADE_SBB0_SHIFT 3
+#define MAX77675_EN_SBB0_MASK GENMASK(2, 0)
+#define MAX77675_EN_SBB0_SHIFT 0
+
+/* CNFG_SBB1_A (0x0A) bits and shifts */
+#define MAX77675_TV_SBB1_MASK GENMASK(7, 0)
+#define MAX77675_TV_SBB1_SHIFT 0
+
+/* CNFG_SBB1_B (0x0B) bits and shifts */
+#define MAX77675_ADE_SBB1_BIT BIT(3)
+#define MAX77675_ADE_SBB1_SHIFT 3
+#define MAX77675_EN_SBB1_MASK GENMASK(2, 0)
+#define MAX77675_EN_SBB1_SHIFT 0
+
+/* CNFG_SBB2_A (0x0C) bits and shifts */
+#define MAX77675_TV_SBB2_MASK GENMASK(7, 0)
+#define MAX77675_TV_SBB2_SHIFT 0
+
+/* CNFG_SBB2_B (0x0D) bits and shifts */
+#define MAX77675_ADE_SBB2_BIT BIT(3)
+#define MAX77675_ADE_SBB2_SHIFT 3
+#define MAX77675_EN_SBB2_MASK GENMASK(2, 0)
+#define MAX77675_EN_SBB2_SHIFT 0
+
+/* CNFG_SBB3_A (0x0E) bits and shifts */
+#define MAX77675_TV_SBB3_MASK GENMASK(7, 0)
+#define MAX77675_TV_SBB3_SHIFT 0
+
+/* CNFG_SBB3_B (0x0F) bits and shifts */
+#define MAX77675_ADE_SBB3_BIT BIT(3)
+#define MAX77675_ADE_SBB3_SHIFT 3
+#define MAX77675_EN_SBB3_MASK GENMASK(2, 0)
+#define MAX77675_EN_SBB3_SHIFT 0
+
+#define MAX77675_EN_SBB_MASK GENMASK(2, 0)
+
+/* CNFG_SBB_TOP_B (0x10) bits and shifts */
+#define MAX77675_DVS_SLEW_BIT BIT(5)
+#define MAX77675_DVS_SLEW_SHIFT 5
+#define MAX77675_LAT_MODE_BIT BIT(4)
+#define MAX77675_LAT_MODE_SHIFT 4
+#define MAX77675_SR_SBB3_BIT BIT(3)
+#define MAX77675_SR_SBB3_SHIFT 3
+#define MAX77675_SR_SBB2_BIT BIT(2)
+#define MAX77675_SR_SBB2_SHIFT 2
+#define MAX77675_SR_SBB1_BIT BIT(1)
+#define MAX77675_SR_SBB1_SHIFT 1
+#define MAX77675_SR_SBB0_BIT BIT(0)
+#define MAX77675_SR_SBB0_SHIFT 0
+
+#define MAX77675_MAX_REGISTER 0x10
+
+/* Common minimum voltage (in microvolts) */
+#define MAX77675_MIN_UV 500000 // 500 mV
+
+/* Voltage step configuration for 25mV mode */
+#define MAX77675_STEP_25MV 25000 // Step size: 25 mV
+#define MAX77675_MAX_UV_25MV 5500000 // Max voltage: 5.5 V
+#define MAX77675_NUM_LEVELS_25MV 201 // levels = (5500mV - 500mV) / 25mV + 1
+
+/* Voltage step configuration for 12.5mV mode */
+#define MAX77675_STEP_12_5MV 12500 // Step size: 12.5 mV
+#define MAX77675_MAX_UV_12_5MV 3687500 // Max voltage: 3.6875 V
+#define MAX77675_NUM_LEVELS_12_5MV 255 // levels = (3687.5mV - 500mV) / 12.5mV + 1
+
+#define MAX77675_ENABLE_OFF 0x04
+#define MAX77675_ENABLE_ON 0x06
+
+#define MAX77675_REGULATOR_AD_OFF 0x00
+#define MAX77675_REGULATOR_AD_ON BIT(3)
+
+/* FPS source */
+#define MAX77675_FPS_SLOT_0 0x0
+#define MAX77675_FPS_SLOT_1 0x1
+#define MAX77675_FPS_SLOT_2 0x2
+#define MAX77675_FPS_SLOT_3 0x3
+#define MAX77675_FPS_DEF 0x4
+
+/* nEN Manual Reset Time Configuration (MRT) */
+#define MAX77675_MRT_4S 0x0
+#define MAX77675_MRT_8S 0x1
+#define MAX77675_MRT_12S 0x2
+#define MAX77675_MRT_16S 0x3
+
+/* nEN Mode Configuration */
+#define MAX77675_EN_PUSH_BUTTON 0x0
+#define MAX77675_EN_SLIDE_SWITCH 0x1
+#define MAX77675_EN_LOGIC 0x2
+
+/* Debounce Timer Enable (DBEN_nEN) */
+#define MAX77675_DBEN_100US 0x0
+#define MAX77675_DBEN_30000US 0x1
+
+/* Rising slew rate control for SBB0 when ramping up */
+#define MAX77675_SR_2MV_PER_US 0x0 // 2 mV/us
+#define MAX77675_SR_USE_DVS 0x1 // Use DVS slew rate setting (adi,dvs-slew-rate)
+
+/* Latency Mode */
+#define MAX77675_HIGH_LATENCY_MODE 0x0 // High latency, low quiescent current (~100us)
+#define MAX77675_LOW_LATENCY_MODE 0x1 // Low latency, high quiescent current (~10us)
+
+/* Dynamic Voltage Scaling (DVS) Slew Rate */
+#define MAX77675_DVS_SLEW_5MV_PER_US 0x0 // 5 mV/us
+#define MAX77675_DVS_SLEW_10MV_PER_US 0x1 // 10 mV/us
+
+/* SIMO Buck-Boost Drive Strength (All Channels) */
+#define MAX77675_DRV_SBB_STRENGTH_MAX 0x0 // Maximum drive strength (~0.6 ns transition time)
+#define MAX77675_DRV_SBB_STRENGTH_HIGH 0x1 // High drive strength (~1.2 ns transition time)
+#define MAX77675_DRV_SBB_STRENGTH_LOW 0x2 // Low drive strength (~1.8 ns transition time)
+#define MAX77675_DRV_SBB_STRENGTH_MIN 0x3 // Minimum drive strength (~8 ns transition time)
+
+/* Regulator ID enumeration */
+enum max77675_regulator_id {
+ MAX77675_ID_SBB0 = 0,
+ MAX77675_ID_SBB1,
+ MAX77675_ID_SBB2,
+ MAX77675_ID_SBB3,
+ MAX77675_ID_NUM_MAX,
+};
+
+struct max77675_regulator_sbb_setting {
+ u8 fps_slot;
+ bool fixed_slew_rate;
+};
+
+struct max77675_config {
+ u8 en_mode;
+ u8 voltage_change_latency;
+ u8 drv_sbb_strength;
+ u8 dvs_slew_rate;
+ u8 debounce_time;
+ u8 manual_reset_time;
+ bool en_pullup_disable;
+ bool bias_low_power_request;
+ bool simo_ldo_always_on;
+};
+
+struct max77675_regulator {
+ struct device *dev;
+ struct regmap *regmap;
+ struct max77675_config config;
+ struct max77675_regulator_sbb_setting sbb_setting[MAX77675_ID_NUM_MAX];
+};
+
+static int max77675_regulator_get_fps_src(struct max77675_regulator *maxreg, int id)
+{
+ unsigned int reg_addr;
+ unsigned int val;
+ int ret;
+
+ switch (id) {
+ case MAX77675_ID_SBB0:
+ reg_addr = MAX77675_REG_CNFG_SBB0_B;
+ break;
+ case MAX77675_ID_SBB1:
+ reg_addr = MAX77675_REG_CNFG_SBB1_B;
+ break;
+ case MAX77675_ID_SBB2:
+ reg_addr = MAX77675_REG_CNFG_SBB2_B;
+ break;
+ case MAX77675_ID_SBB3:
+ reg_addr = MAX77675_REG_CNFG_SBB3_B;
+ break;
+ default:
+ dev_err(maxreg->dev, "Invalid regulator id: %d\n", id);
+ return -EINVAL;
+ }
+
+ ret = regmap_read(maxreg->regmap, reg_addr, &val);
+ if (ret < 0) {
+ dev_err(maxreg->dev, "Failed to read FPS source (reg 0x%02x): %d\n",
+ reg_addr, ret);
+ return ret;
+ }
+
+ return FIELD_GET(MAX77675_EN_SBB_MASK, val);
+}
+
+static int max77675_regulator_set_fps_src(struct max77675_regulator *maxreg, int id, u8 fps_src)
+{
+ unsigned int reg_addr;
+
+ switch (id) {
+ case MAX77675_ID_SBB0:
+ reg_addr = MAX77675_REG_CNFG_SBB0_B;
+ break;
+ case MAX77675_ID_SBB1:
+ reg_addr = MAX77675_REG_CNFG_SBB1_B;
+ break;
+ case MAX77675_ID_SBB2:
+ reg_addr = MAX77675_REG_CNFG_SBB2_B;
+ break;
+ case MAX77675_ID_SBB3:
+ reg_addr = MAX77675_REG_CNFG_SBB3_B;
+ break;
+ default:
+ dev_err(maxreg->dev, "Invalid regulator id: %d\n", id);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(maxreg->regmap, reg_addr, MAX77675_EN_SBB_MASK, fps_src);
+}
+
+static int max77675_set_sbb_slew_rate_fixed(struct max77675_regulator *maxreg, int id, bool fixed)
+{
+ u8 mask, value;
+ u8 slew_src_ctrl_bit = fixed ? 0 : 1;
+
+ switch (id) {
+ case MAX77675_ID_SBB0:
+ mask = MAX77675_SR_SBB0_BIT;
+ value = FIELD_PREP(MAX77675_SR_SBB0_BIT, slew_src_ctrl_bit);
+ break;
+
+ case MAX77675_ID_SBB1:
+ mask = MAX77675_SR_SBB1_BIT;
+ value = FIELD_PREP(MAX77675_SR_SBB1_BIT, slew_src_ctrl_bit);
+ break;
+
+ case MAX77675_ID_SBB2:
+ mask = MAX77675_SR_SBB2_BIT;
+ value = FIELD_PREP(MAX77675_SR_SBB2_BIT, slew_src_ctrl_bit);
+ break;
+
+ case MAX77675_ID_SBB3:
+ mask = MAX77675_SR_SBB3_BIT;
+ value = FIELD_PREP(MAX77675_SR_SBB3_BIT, slew_src_ctrl_bit);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_SBB_TOP_B, mask, value);
+}
+
+static int max77675_init_regulator(struct max77675_regulator *maxreg, int id)
+{
+ struct max77675_regulator_sbb_setting *sbb_setting = &maxreg->sbb_setting[id];
+ int ret;
+
+ if (sbb_setting->fps_slot == MAX77675_FPS_DEF) {
+ ret = max77675_regulator_get_fps_src(maxreg, id);
+ if (ret < 0)
+ return ret;
+
+ sbb_setting->fps_slot = ret;
+ } else {
+ ret = max77675_regulator_set_fps_src(maxreg, id, sbb_setting->fps_slot);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = max77675_set_sbb_slew_rate_fixed(maxreg, id, sbb_setting->fixed_slew_rate);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int max77675_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct max77675_regulator *maxreg = config->driver_data;
+ struct max77675_regulator_sbb_setting *sbb_setting = &maxreg->sbb_setting[desc->id];
+ static const char * const fps_slots[] = { "slot0", "slot1", "slot2", "slot3", "default" };
+ const char *fps_str;
+ int slot;
+
+ /* Parse FPS slot from DT */
+ if (of_property_read_string(np, "adi,fps-slot", &fps_str)) {
+ /* Property not set, use default */
+ sbb_setting->fps_slot = MAX77675_FPS_DEF;
+ } else {
+ /* Match string to index */
+ slot = match_string(fps_slots, ARRAY_SIZE(fps_slots), fps_str);
+ if (slot < 0) {
+ dev_dbg(maxreg->dev, "Invalid fps-slot '%s', using default\n", fps_str);
+ sbb_setting->fps_slot = MAX77675_FPS_DEF;
+ } else {
+ sbb_setting->fps_slot = slot;
+ }
+ }
+
+ /* Parse slew rate control source */
+ sbb_setting->fixed_slew_rate = of_property_read_bool(np, "adi,fixed-slew-rate");
+
+ /* Apply parsed configuration */
+ return max77675_init_regulator(maxreg, desc->id);
+}
+
+static int max77675_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+{
+ struct max77675_regulator *maxreg = rdev_get_drvdata(rdev);
+ unsigned int int_flags;
+ int id = rdev_get_id(rdev);
+ int ret;
+
+ ret = regmap_read(maxreg->regmap, MAX77675_REG_INT_GLBL, &int_flags);
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to read INT_GLBL: %d\n", ret);
+ return ret;
+ }
+
+ *flags = 0;
+
+ switch (id) {
+ case MAX77675_ID_SBB0:
+ if (int_flags & MAX77675_INT_SBB0_F_BIT)
+ *flags |= REGULATOR_ERROR_FAIL;
+ break;
+ case MAX77675_ID_SBB1:
+ if (int_flags & MAX77675_INT_SBB1_F_BIT)
+ *flags |= REGULATOR_ERROR_FAIL;
+ break;
+ case MAX77675_ID_SBB2:
+ if (int_flags & MAX77675_INT_SBB2_F_BIT)
+ *flags |= REGULATOR_ERROR_FAIL;
+ break;
+ case MAX77675_ID_SBB3:
+ if (int_flags & MAX77675_INT_SBB3_F_BIT)
+ *flags |= REGULATOR_ERROR_FAIL;
+ break;
+ default:
+ dev_warn(maxreg->dev, "Unsupported regulator ID: %d\n", id);
+ break;
+ }
+
+ if (int_flags & MAX77675_INT_TJAL2_R_BIT) {
+ /* TJAL2 interrupt: Over-temperature condition (above 120 degree) */
+ *flags |= REGULATOR_ERROR_OVER_TEMP;
+ }
+
+ return 0;
+}
+
+static const struct regulator_ops max77675_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+ .get_error_flags = max77675_get_error_flags,
+};
+
+static struct regulator_desc max77675_regulators[MAX77675_ID_NUM_MAX] = {
+ {
+ .name = "sbb0",
+ .of_match = of_match_ptr("sbb0"),
+ .regulators_node = of_match_ptr("regulators"),
+ .of_parse_cb = max77675_of_parse_cb,
+ .id = MAX77675_ID_SBB0,
+ .ops = &max77675_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = MAX77675_NUM_LEVELS_25MV,
+ .min_uV = MAX77675_MIN_UV,
+ .uV_step = MAX77675_STEP_25MV,
+ .vsel_reg = MAX77675_REG_CNFG_SBB0_A,
+ .vsel_mask = MAX77675_TV_SBB0_MASK,
+ .enable_reg = MAX77675_REG_CNFG_SBB0_B,
+ .enable_mask = MAX77675_EN_SBB0_MASK,
+ .enable_val = MAX77675_ENABLE_ON,
+ .disable_val = MAX77675_ENABLE_OFF,
+ .active_discharge_off = MAX77675_REGULATOR_AD_OFF,
+ .active_discharge_on = MAX77675_REGULATOR_AD_ON,
+ .active_discharge_mask = MAX77675_ADE_SBB0_BIT,
+ .active_discharge_reg = MAX77675_REG_CNFG_SBB0_B,
+ },
+ {
+ .name = "sbb1",
+ .of_match = of_match_ptr("sbb1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .of_parse_cb = max77675_of_parse_cb,
+ .id = MAX77675_ID_SBB1,
+ .ops = &max77675_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = MAX77675_NUM_LEVELS_25MV,
+ .min_uV = MAX77675_MIN_UV,
+ .uV_step = MAX77675_STEP_25MV,
+ .vsel_reg = MAX77675_REG_CNFG_SBB1_A,
+ .vsel_mask = MAX77675_TV_SBB1_MASK,
+ .enable_reg = MAX77675_REG_CNFG_SBB1_B,
+ .enable_mask = MAX77675_EN_SBB1_MASK,
+ .enable_val = MAX77675_ENABLE_ON,
+ .disable_val = MAX77675_ENABLE_OFF,
+ .active_discharge_off = MAX77675_REGULATOR_AD_OFF,
+ .active_discharge_on = MAX77675_REGULATOR_AD_ON,
+ .active_discharge_mask = MAX77675_ADE_SBB1_BIT,
+ .active_discharge_reg = MAX77675_REG_CNFG_SBB1_B,
+ },
+ {
+ .name = "sbb2",
+ .of_match = of_match_ptr("sbb2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .of_parse_cb = max77675_of_parse_cb,
+ .id = MAX77675_ID_SBB2,
+ .ops = &max77675_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = MAX77675_NUM_LEVELS_25MV,
+ .min_uV = MAX77675_MIN_UV,
+ .uV_step = MAX77675_STEP_25MV,
+ .vsel_reg = MAX77675_REG_CNFG_SBB2_A,
+ .vsel_mask = MAX77675_TV_SBB2_MASK,
+ .enable_reg = MAX77675_REG_CNFG_SBB2_B,
+ .enable_mask = MAX77675_EN_SBB2_MASK,
+ .enable_val = MAX77675_ENABLE_ON,
+ .disable_val = MAX77675_ENABLE_OFF,
+ .active_discharge_off = MAX77675_REGULATOR_AD_OFF,
+ .active_discharge_on = MAX77675_REGULATOR_AD_ON,
+ .active_discharge_mask = MAX77675_ADE_SBB2_BIT,
+ .active_discharge_reg = MAX77675_REG_CNFG_SBB2_B,
+ },
+ {
+ .name = "sbb3",
+ .of_match = of_match_ptr("sbb3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .of_parse_cb = max77675_of_parse_cb,
+ .id = MAX77675_ID_SBB3,
+ .ops = &max77675_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = MAX77675_NUM_LEVELS_25MV,
+ .min_uV = MAX77675_MIN_UV,
+ .uV_step = MAX77675_STEP_25MV,
+ .vsel_reg = MAX77675_REG_CNFG_SBB3_A,
+ .vsel_mask = MAX77675_TV_SBB3_MASK,
+ .enable_reg = MAX77675_REG_CNFG_SBB3_B,
+ .enable_mask = MAX77675_EN_SBB3_MASK,
+ .enable_val = MAX77675_ENABLE_ON,
+ .disable_val = MAX77675_ENABLE_OFF,
+ .active_discharge_off = MAX77675_REGULATOR_AD_OFF,
+ .active_discharge_on = MAX77675_REGULATOR_AD_ON,
+ .active_discharge_mask = MAX77675_ADE_SBB3_BIT,
+ .active_discharge_reg = MAX77675_REG_CNFG_SBB3_B,
+ },
+};
+
+static bool max77675_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX77675_REG_CNFG_GLBL_B:
+ /* This register can be updated by an internal state machine */
+ case MAX77675_REG_INT_GLBL:
+ case MAX77675_REG_STAT_GLBL:
+ case MAX77675_REG_ERCF_GLBL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max77675_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77675_MAX_REGISTER,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = max77675_volatile_reg,
+};
+
+static int max77675_apply_config(struct max77675_regulator *maxreg)
+{
+ const struct max77675_config *cfg = &maxreg->config;
+ int ret;
+
+ /* Set EN pin mode */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_GLBL_A,
+ MAX77675_EN_MODE_MASK,
+ FIELD_PREP(MAX77675_EN_MODE_MASK, cfg->en_mode));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set EN mode: %d\n", ret);
+ return ret;
+ }
+
+ /* Set the latency between output voltage change and SBBx voltage ramp start */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_SBB_TOP_B,
+ MAX77675_LAT_MODE_BIT,
+ FIELD_PREP(MAX77675_LAT_MODE_BIT, cfg->voltage_change_latency));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set latency mode: %d\n", ret);
+ return ret;
+ }
+
+ /* Set drive strength */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_SBB_TOP_A,
+ MAX77675_DRV_SBB_MASK,
+ FIELD_PREP(MAX77675_DRV_SBB_MASK, cfg->drv_sbb_strength));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set drive strength: %d\n", ret);
+ return ret;
+ }
+
+ /* Set DVS slew rate */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_SBB_TOP_B,
+ MAX77675_DVS_SLEW_BIT,
+ FIELD_PREP(MAX77675_DVS_SLEW_BIT, cfg->dvs_slew_rate));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set DVS slew rate: %d\n", ret);
+ return ret;
+ }
+
+ /* Set debounce time for EN pin */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_GLBL_A,
+ MAX77675_DBEN_EN_BIT,
+ FIELD_PREP(MAX77675_DBEN_EN_BIT, cfg->debounce_time));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set EN debounce time: %d\n", ret);
+ return ret;
+ }
+
+ /* Set manual reset time (MRT) for EN pin */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_GLBL_A,
+ MAX77675_MRT_MASK,
+ FIELD_PREP(MAX77675_MRT_MASK, cfg->manual_reset_time));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set manual reset time: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable or disable internal pull-up resistor on EN pin */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_GLBL_A,
+ MAX77675_PU_DIS_BIT,
+ FIELD_PREP(MAX77675_PU_DIS_BIT, cfg->en_pullup_disable));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set EN pull-up disable: %d\n", ret);
+ return ret;
+ }
+
+ /* Request main bias to enter low-power mode */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_GLBL_A,
+ MAX77675_BIAS_LPM_BIT,
+ FIELD_PREP(MAX77675_BIAS_LPM_BIT, cfg->bias_low_power_request));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set bias low-power request: %d\n", ret);
+ return ret;
+ }
+
+ /* Force SIMO internal LDO to always supply 1.8V */
+ ret = regmap_update_bits(maxreg->regmap, MAX77675_REG_CNFG_GLBL_A,
+ MAX77675_SIMO_CH_DIS_BIT,
+ FIELD_PREP(MAX77675_SIMO_CH_DIS_BIT, cfg->simo_ldo_always_on));
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to set SIMO internal LDO always-on: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77675_parse_en_mode(struct device *dev,
+ struct device_node *np,
+ u8 *en_mode)
+{
+ static const char * const en_modes[] = {"push-button", "slide-switch", "logic"};
+ const char *str;
+ int index;
+
+ *en_mode = MAX77675_EN_SLIDE_SWITCH;
+
+ if (of_property_read_string(np, "adi,en-mode", &str))
+ return 0;
+
+ index = match_string(en_modes, ARRAY_SIZE(en_modes), str);
+ if (index < 0) {
+ dev_err(dev, "Invalid 'adi,en-mode' value '%s'\n", str);
+ return -EINVAL;
+ }
+
+ *en_mode = index;
+
+ return 0;
+}
+
+static int max77675_parse_voltage_change_latency(struct device *dev,
+ struct device_node *np,
+ u8 *latency_mode)
+{
+ u32 val;
+
+ *latency_mode = MAX77675_HIGH_LATENCY_MODE;
+
+ if (!of_property_read_u32(np, "adi,voltage-change-latency-us", &val)) {
+ switch (val) {
+ case 10:
+ *latency_mode = MAX77675_LOW_LATENCY_MODE;
+ break;
+ case 100:
+ *latency_mode = MAX77675_HIGH_LATENCY_MODE;
+ break;
+ default:
+ dev_err(dev, "Invalid voltage-change-latency-us value: %u\n", val);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int max77675_parse_manual_reset_time(struct device *dev,
+ struct device_node *np,
+ u8 *reset_time)
+{
+ u32 val;
+
+ *reset_time = MAX77675_MRT_4S;
+
+ if (!of_property_read_u32(np, "reset-time-sec", &val)) {
+ switch (val) {
+ case 4:
+ *reset_time = MAX77675_MRT_4S;
+ break;
+ case 8:
+ *reset_time = MAX77675_MRT_8S;
+ break;
+ case 12:
+ *reset_time = MAX77675_MRT_12S;
+ break;
+ case 16:
+ *reset_time = MAX77675_MRT_16S;
+ break;
+ default:
+ dev_err(dev, "Invalid reset-time-sec value: %u\n", val);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int max77675_parse_dvs_slew_rate(struct device *dev, struct device_node *np, u8 *slew_rate)
+{
+ u32 val;
+
+ /* Set default: 5 mV/us */
+ *slew_rate = MAX77675_DVS_SLEW_5MV_PER_US;
+
+ if (!of_property_read_u32(np, "adi,dvs-slew-rate-mv-per-us", &val)) {
+ switch (val) {
+ case 5:
+ *slew_rate = MAX77675_DVS_SLEW_5MV_PER_US;
+ break;
+ case 10:
+ *slew_rate = MAX77675_DVS_SLEW_10MV_PER_US;
+ break;
+ default:
+ dev_err(dev, "Invalid dvs-slew-rate-mv-per-us value: %u\n", val);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int max77675_parse_drv_sbb_strength(struct device *dev, struct device_node *np, u8 *strength)
+{
+ static const char * const strength_names[] = {"max", "high", "low", "min"};
+ const char *str;
+ int index;
+
+ /* Set default: maximum drive strength */
+ *strength = MAX77675_DRV_SBB_STRENGTH_MAX;
+
+ if (of_property_read_string(np, "adi,drv-sbb-strength", &str))
+ return 0;
+
+ index = match_string(strength_names, ARRAY_SIZE(strength_names), str);
+ if (index < 0) {
+ dev_err(dev, "Invalid 'adi,drv-sbb-strength' value: '%s'\n", str);
+ return -EINVAL;
+ }
+
+ *strength = index;
+
+ return 0;
+}
+
+static int max77675_parse_debounce_time_us(struct device *dev,
+ struct device_node *np,
+ u8 *debounce_time)
+{
+ u32 val;
+
+ *debounce_time = MAX77675_DBEN_100US;
+
+ if (!of_property_read_u32(np, "input-debounce", &val)) {
+ switch (val) {
+ case 100:
+ *debounce_time = MAX77675_DBEN_100US;
+ break;
+ case 30000:
+ *debounce_time = MAX77675_DBEN_30000US;
+ break;
+ default:
+ dev_err(dev, "Invalid input-debounce value: %u\n", val);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int max77675_parse_config(struct max77675_regulator *maxreg)
+{
+ struct device_node *np = maxreg->dev->of_node;
+ struct max77675_config *cfg = &maxreg->config;
+ int ret;
+
+ /* EN pin mode */
+ ret = max77675_parse_en_mode(maxreg->dev, np, &cfg->en_mode);
+ if (ret < 0)
+ return ret;
+
+ /* voltage change latency */
+ ret = max77675_parse_voltage_change_latency(maxreg->dev, np, &cfg->voltage_change_latency);
+ if (ret < 0)
+ return ret;
+
+ /* drive strength */
+ ret = max77675_parse_drv_sbb_strength(maxreg->dev, np, &cfg->drv_sbb_strength);
+ if (ret < 0)
+ return ret;
+
+ /* dvs slew rate */
+ ret = max77675_parse_dvs_slew_rate(maxreg->dev, np, &cfg->dvs_slew_rate);
+ if (ret < 0)
+ return ret;
+
+ /* Debounce time for EN pin */
+ ret = max77675_parse_debounce_time_us(maxreg->dev, np, &cfg->debounce_time);
+ if (ret < 0)
+ return ret;
+
+ /* Manual reset time for EN pin */
+ ret = max77675_parse_manual_reset_time(maxreg->dev, np, &cfg->manual_reset_time);
+ if (ret < 0)
+ return ret;
+
+ /* Disable internal pull-up resistor on EN pin */
+ cfg->en_pullup_disable = of_property_read_bool(np, "bias-disable");
+
+ /* Request low-power mode for main bias */
+ cfg->bias_low_power_request = of_property_read_bool(np, "adi,bias-low-power-request");
+
+ /* Force internal LDO to always supply 1.8V */
+ cfg->simo_ldo_always_on = of_property_read_bool(np, "adi,simo-ldo-always-on");
+
+ return ret;
+}
+
+static int max77675_init_event(struct max77675_regulator *maxreg)
+{
+ unsigned int ercflag, int_glbl;
+ int ret;
+
+ ret = regmap_read(maxreg->regmap, MAX77675_REG_ERCF_GLBL, &ercflag);
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to read CID register: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(maxreg->regmap, MAX77675_REG_INT_GLBL, &int_glbl);
+ if (ret) {
+ dev_err(maxreg->dev, "Failed to read INT_GLBL register: %d\n", ret);
+ return ret;
+ }
+
+ if (ercflag & MAX77675_SFT_CRST_F_BIT)
+ dev_dbg(maxreg->dev, "Software Cold Reset Flag is set\n");
+
+ if (ercflag & MAX77675_SFT_OFF_F_BIT)
+ dev_dbg(maxreg->dev, "Software Off Flag is set\n");
+
+ if (ercflag & MAX77675_MRST_BIT)
+ dev_dbg(maxreg->dev, "Manual Reset Timer Flag is set\n");
+
+ if (ercflag & MAX77675_UVLO_BIT)
+ dev_dbg(maxreg->dev, "Undervoltage Lockout Flag is set\n");
+
+ if (ercflag & MAX77675_OVLO_BIT)
+ dev_dbg(maxreg->dev, "Overvoltage Lockout Flag is set\n");
+
+ if (ercflag & MAX77675_TOVLD_BIT)
+ dev_dbg(maxreg->dev, "Thermal Overload Flag is set\n");
+
+ if (int_glbl & MAX77675_INT_SBB3_F_BIT)
+ dev_dbg(maxreg->dev, "SBB3 Channel Fault Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_SBB2_F_BIT)
+ dev_dbg(maxreg->dev, "SBB2 Channel Fault Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_SBB1_F_BIT)
+ dev_dbg(maxreg->dev, "SBB1 Channel Fault Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_SBB0_F_BIT)
+ dev_dbg(maxreg->dev, "SBB0 Channel Fault Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_TJAL2_R_BIT)
+ dev_dbg(maxreg->dev, "Thermal Alarm 2 Rising Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_TJAL1_R_BIT)
+ dev_dbg(maxreg->dev, "Thermal Alarm 1 Rising Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_EN_R_BIT)
+ dev_dbg(maxreg->dev, "nEN Rising Edge Interrupt occurred\n");
+
+ if (int_glbl & MAX77675_INT_EN_F_BIT)
+ dev_dbg(maxreg->dev, "nEN Falling Edge Interrupt occurred\n");
+
+ return 0;
+}
+
+static int max77675_regulator_probe(struct i2c_client *client)
+{
+ struct max77675_regulator *maxreg;
+ struct regulator_config config = {};
+ int i, ret;
+
+ maxreg = devm_kzalloc(&client->dev, sizeof(*maxreg), GFP_KERNEL);
+ if (!maxreg)
+ return -ENOMEM;
+
+ maxreg->dev = &client->dev;
+
+ maxreg->regmap = devm_regmap_init_i2c(client, &max77675_regmap_config);
+ if (IS_ERR(maxreg->regmap))
+ return dev_err_probe(maxreg->dev,
+ PTR_ERR(maxreg->regmap),
+ "Failed to init regmap\n");
+
+ ret = max77675_init_event(maxreg);
+ if (ret < 0)
+ return dev_err_probe(maxreg->dev, ret, "Failed to init event\n");
+
+ ret = max77675_parse_config(maxreg);
+ if (ret < 0)
+ return dev_err_probe(maxreg->dev, ret, "Failed to parse config\n");
+
+ ret = max77675_apply_config(maxreg);
+ if (ret < 0)
+ return dev_err_probe(maxreg->dev, ret, "Failed to apply config\n");
+
+ config.dev = &client->dev;
+ config.regmap = maxreg->regmap;
+ config.driver_data = maxreg;
+
+ struct device_node *regulators_np __free(device_node) =
+ of_get_child_by_name(client->dev.of_node, "regulators");
+ if (!regulators_np) {
+ dev_err(maxreg->dev, "No 'regulators' subnode found in DT\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX77675_ID_NUM_MAX; i++) {
+ const struct regulator_desc *desc = &max77675_regulators[i];
+ struct regulator_dev *rdev;
+
+ struct device_node *child_np __free(device_node) =
+ of_get_child_by_name(regulators_np, desc->name);
+ if (!child_np) {
+ dev_warn(maxreg->dev, "No DT node for regulator %s\n", desc->name);
+ continue;
+ }
+
+ config.of_node = child_np;
+
+ rdev = devm_regulator_register(&client->dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ return dev_err_probe(maxreg->dev, PTR_ERR(rdev),
+ "Failed to register regulator %d (%s)\n",
+ i, desc->name);
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id max77675_i2c_id[] = {
+ { "max77675", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max77675_i2c_id);
+
+static const struct of_device_id __maybe_unused max77675_of_match[] = {
+ { .compatible = "adi,max77675", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max77675_of_match);
+
+static struct i2c_driver max77675_regulator_driver = {
+ .driver = {
+ .name = "max77675",
+ .of_match_table = of_match_ptr(max77675_of_match),
+ },
+ .probe = max77675_regulator_probe,
+ .id_table = max77675_i2c_id,
+};
+
+module_i2c_driver(max77675_regulator_driver);
+
+MODULE_DESCRIPTION("MAX77675 Regulator Driver");
+MODULE_AUTHOR("Joan Na <joan.na@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/rt8092.c b/drivers/regulator/rt8092.c
new file mode 100644
index 000000000000..558bd04a2090
--- /dev/null
+++ b/drivers/regulator/rt8092.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2025 Richtek Technology Corp.
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define RT8092_REG_MNTRPT 0x00
+#define RT8092_REG_VOUTH 0x10
+#define RT8092_REG_VOUTL 0x11
+#define RT8092_REG_PWMMODE 0x14
+#define RT8092_REG_EVENT 0x18
+#define RT8092_REG_VBANKH 0x1C
+#define RT8092_REG_VBANKL 0x1D
+#define RT8092_REG_VBOUND 0x1E
+
+#define RT8092_TSDEVT_MASK BIT(7)
+#define RT8092_PGEVT_MASK BIT(0)
+#define RT8092_VSEL_MASK GENMASK(6, 0)
+#define RT8092_VOUTEN_MASK BIT(7)
+#define RT8092_FPWML_MASK BIT(7)
+#define RT8092_FPWMH_MASK BIT(6)
+#define RT8092_OCPEVT_MASK BIT(7)
+#define RT8092_SCPEVT_MASK BIT(4)
+#define RT8092_VINUVEVT_MASK BIT(1)
+#define RT8092_VBANK_MASK GENMASK(1, 0)
+
+#define RT8092_MODE_AUTO 0
+#define RT8092_MODE_FPWM 1
+#define RT8092_VOUT_BASEUV 303125
+#define RT8092_VOUT_STEPUV 3125
+#define RT8092_VOUT_MINSEL 15
+#define RT8092_NUM_VOLTS 128
+#define RT8092_INITSS_US 400
+
+static int rt8092_get_vbank_index(struct regmap *regmap, bool vsel_high, unsigned int *vbank_idx)
+{
+ unsigned int vbank_reg = vsel_high ? RT8092_REG_VBANKH : RT8092_REG_VBANKL;
+ unsigned int index;
+ int ret;
+
+ ret = regmap_read(regmap, vbank_reg, &index);
+ if (ret)
+ return ret;
+
+ *vbank_idx = FIELD_GET(RT8092_VBANK_MASK, index);
+ return 0;
+}
+
+static int rt8092_set_operating_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int mode_mask, mode_val;
+
+ mode_mask = desc->vsel_reg == RT8092_REG_VOUTH ? RT8092_FPWMH_MASK : RT8092_FPWML_MASK;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ mode_val = mode_mask;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ mode_val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, RT8092_REG_PWMMODE, mode_mask, mode_val);
+}
+
+static unsigned int rt8092_get_operating_mode(struct regulator_dev *rdev)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int mode_mask, mode_val;
+ int ret;
+
+ mode_mask = desc->vsel_reg == RT8092_REG_VOUTH ? RT8092_FPWMH_MASK : RT8092_FPWML_MASK;
+
+ ret = regmap_read(regmap, RT8092_REG_PWMMODE, &mode_val);
+ if (ret)
+ return REGULATOR_MODE_INVALID;
+
+ return mode_val & mode_mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
+}
+
+static int rt8092_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+{
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int mntrpt, evtrpt, events = 0;
+ int ret;
+
+ ret = regmap_read(regmap, RT8092_REG_MNTRPT, &mntrpt);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, RT8092_REG_EVENT, &evtrpt);
+ if (ret)
+ return ret;
+
+ if (!(mntrpt & RT8092_PGEVT_MASK) || evtrpt & RT8092_VINUVEVT_MASK)
+ events |= REGULATOR_ERROR_UNDER_VOLTAGE;
+
+ if (mntrpt & RT8092_TSDEVT_MASK)
+ events |= REGULATOR_ERROR_OVER_TEMP;
+
+ if (evtrpt & RT8092_OCPEVT_MASK)
+ events |= REGULATOR_ERROR_OVER_CURRENT;
+
+ if (evtrpt & RT8092_SCPEVT_MASK)
+ events |= REGULATOR_ERROR_FAIL;
+
+ *flags = events;
+ return 0;
+}
+
+
+static int rt8092_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int vsel_reg, vsel_val, vbank_idx;
+ bool vsel_high;
+ int ret;
+
+ vsel_reg = desc->vsel_reg == RT8092_REG_VOUTH ? RT8092_REG_VOUTL : RT8092_REG_VOUTH;
+ vsel_high = desc->vsel_reg == RT8092_REG_VOUTH;
+
+ ret = rt8092_get_vbank_index(regmap, vsel_high, &vbank_idx);
+ if (ret)
+ return ret;
+
+ /* VOUT = (BASEUV + STEPUV * VSEL) * 2^vbank_idx */
+ uV >>= vbank_idx;
+ if (uV < RT8092_VOUT_BASEUV)
+ return -EINVAL;
+
+ vsel_val = (uV - RT8092_VOUT_BASEUV) / RT8092_VOUT_STEPUV;
+ if (vsel_val < RT8092_VOUT_MINSEL || vsel_val >= RT8092_NUM_VOLTS)
+ return -EINVAL;
+
+ return regmap_update_bits(regmap, vsel_reg, RT8092_VSEL_MASK, vsel_val);
+}
+
+static int rt8092_set_suspend_enable(struct regulator_dev *rdev)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int enable_reg;
+
+ enable_reg = desc->vsel_reg == RT8092_REG_VOUTH ? RT8092_REG_VOUTL : RT8092_REG_VOUTH;
+ return regmap_set_bits(regmap, enable_reg, RT8092_VOUTEN_MASK);
+}
+
+static int rt8092_set_suspend_disable(struct regulator_dev *rdev)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int enable_reg;
+
+ enable_reg = desc->vsel_reg == RT8092_REG_VOUTH ? RT8092_REG_VOUTL : RT8092_REG_VOUTH;
+ return regmap_clear_bits(regmap, enable_reg, RT8092_VOUTEN_MASK);
+}
+
+static int rt8092_set_suspend_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ const struct regulator_desc *desc = rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int mode_mask, mode_val;
+
+ mode_mask = desc->vsel_reg == RT8092_REG_VOUTH ? RT8092_FPWML_MASK : RT8092_FPWMH_MASK;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ mode_val = mode_mask;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ mode_val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, RT8092_REG_PWMMODE, mode_mask, mode_val);
+}
+
+static const struct regulator_ops rt8092_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_mode = rt8092_set_operating_mode,
+ .get_mode = rt8092_get_operating_mode,
+ .get_error_flags = rt8092_get_error_flags,
+ .set_suspend_voltage = rt8092_set_suspend_voltage,
+ .set_suspend_enable = rt8092_set_suspend_enable,
+ .set_suspend_disable = rt8092_set_suspend_disable,
+ .set_suspend_mode = rt8092_set_suspend_mode,
+};
+
+static unsigned int rt8092_of_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case RT8092_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ case RT8092_MODE_FPWM:
+ return REGULATOR_MODE_FAST;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+static const struct regmap_config rt8092_regmap_cfg = {
+ .name = "rt8092",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RT8092_REG_VBOUND,
+};
+
+static int rt8092_probe(struct i2c_client *i2c)
+{
+ unsigned int vbank_idx, min_uV, step_uV;
+ struct regulator_config cfg = {};
+ struct device *dev = &i2c->dev;
+ struct regulator_desc *desc;
+ struct regulator_dev *rdev;
+ struct gpio_desc *enable;
+ struct regmap *regmap;
+ bool vsel_high;
+ int ret;
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(enable))
+ return dev_err_probe(dev, PTR_ERR(enable), "Failed get 'enable' gpio\n");
+
+ regmap = devm_regmap_init_i2c(i2c, &rt8092_regmap_cfg);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ vsel_high = device_property_read_bool(dev, "richtek,vsel-active-high");
+
+ ret = rt8092_get_vbank_index(regmap, vsel_high, &vbank_idx);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get VOUT bank index\n");
+
+ /*
+ * step VOUT = STEP_UV * 2^vbank_idx
+ * min VOUT = (BASEUV + STEPUV * VMIN_SEL) * 2^vbank_idx
+ */
+ step_uV = RT8092_VOUT_STEPUV << vbank_idx;
+ min_uV = (RT8092_VOUT_BASEUV + RT8092_VOUT_STEPUV * RT8092_VOUT_MINSEL) << vbank_idx;
+
+ desc->name = "rt8092";
+ desc->owner = THIS_MODULE;
+ desc->type = REGULATOR_VOLTAGE;
+ desc->ops = &rt8092_regulator_ops;
+ desc->n_voltages = RT8092_NUM_VOLTS;
+ desc->min_uV = min_uV;
+ desc->uV_step = step_uV;
+ desc->linear_min_sel = RT8092_VOUT_MINSEL;
+ desc->enable_reg = desc->vsel_reg = vsel_high ? RT8092_REG_VOUTH : RT8092_REG_VOUTL;
+ desc->vsel_mask = RT8092_VSEL_MASK;
+ desc->enable_mask = RT8092_VOUTEN_MASK;
+ desc->enable_time = RT8092_INITSS_US;
+ desc->of_map_mode = rt8092_of_map_mode;
+
+ cfg.dev = dev;
+ cfg.of_node = dev_of_node(dev);
+ cfg.init_data = of_get_regulator_init_data(dev, dev_of_node(dev), desc);
+
+ rdev = devm_regulator_register(dev, desc, &cfg);
+ if (IS_ERR(rdev))
+ return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register regulator\n");
+
+ return 0;
+}
+
+static const struct of_device_id rt8092_device_tables[] = {
+ { .compatible = "richtek,rt8092" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt8092_device_tables);
+
+static struct i2c_driver rt8092_driver = {
+ .driver = {
+ .name = "rt8092",
+ .of_match_table = rt8092_device_tables,
+ },
+ .probe = rt8092_probe,
+};
+module_i2c_driver(rt8092_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT8092 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 04ae9c6150bd..2d5510acd078 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -3,6 +3,7 @@
// Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
// http://www.samsung.com
+#include <dt-bindings/regulator/samsung,s2mpg10-regulator.h>
#include <linux/bug.h>
#include <linux/cleanup.h>
#include <linux/err.h>
@@ -16,6 +17,8 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s2mpg10.h>
+#include <linux/mfd/samsung/s2mpg11.h>
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
@@ -23,6 +26,11 @@
#include <linux/mfd/samsung/s2mpu02.h>
#include <linux/mfd/samsung/s2mpu05.h>
+enum {
+ S2MPG10_REGULATOR_OPS_STD,
+ S2MPG10_REGULATOR_OPS_EXTCONTROL,
+};
+
/* The highest number of possible regulators for supported devices. */
#define S2MPS_REGULATOR_MAX S2MPS13_REGULATOR_MAX
struct s2mps11_info {
@@ -40,12 +48,21 @@ struct s2mps11_info {
* the suspend mode was enabled.
*/
DECLARE_BITMAP(suspend_state, S2MPS_REGULATOR_MAX);
+};
- /*
- * Array (size: number of regulators) with GPIO-s for external
- * sleep control.
- */
- struct gpio_desc **ext_control_gpiod;
+#define to_s2mpg10_regulator_desc(x) container_of((x), struct s2mpg10_regulator_desc, desc)
+
+struct s2mpg10_regulator_desc {
+ struct regulator_desc desc;
+
+ /* Ramp rate during enable, valid for bucks only. */
+ unsigned int enable_ramp_rate;
+
+ /* Registers for external control of rail. */
+ unsigned int pctrlsel_reg;
+ unsigned int pctrlsel_mask;
+ /* Populated from DT. */
+ unsigned int pctrlsel_val;
};
static int get_ramp_delay(int ramp_delay)
@@ -244,7 +261,7 @@ static int s2mps11_regulator_enable(struct regulator_dev *rdev)
case S2MPS14X:
if (test_bit(rdev_id, s2mps11->suspend_state))
val = S2MPS14_ENABLE_SUSPEND;
- else if (s2mps11->ext_control_gpiod[rdev_id])
+ else if (rdev->ena_pin)
val = S2MPS14_ENABLE_EXT_CONTROL;
else
val = rdev->desc->enable_mask;
@@ -334,6 +351,916 @@ static int s2mps11_regulator_set_suspend_disable(struct regulator_dev *rdev)
rdev->desc->enable_mask, state);
}
+static int s2mps11_of_parse_gpiod(struct device_node *np,
+ const char *con_id, bool optional,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct gpio_desc *ena_gpiod;
+ int ret;
+
+ ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), con_id, 0,
+ GPIOD_OUT_HIGH |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+ desc->name
+ ? : dev_name(config->dev));
+ if (IS_ERR(ena_gpiod)) {
+ ret = PTR_ERR(ena_gpiod);
+
+ /* Ignore all errors except probe defer. */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ if (ret == -ENOENT) {
+ if (optional)
+ return 0;
+
+ dev_info(config->dev,
+ "No entry for control GPIO for %d/%s in node %pOF\n",
+ desc->id, desc->name, np);
+ } else {
+ dev_warn_probe(config->dev, ret,
+ "Failed to get control GPIO for %d/%s in node %pOF\n",
+ desc->id, desc->name, np);
+ }
+
+ return 0;
+ }
+
+ dev_info(config->dev, "Using GPIO for ext-control over %d/%s\n",
+ desc->id, desc->name);
+
+ config->ena_gpiod = ena_gpiod;
+
+ return 0;
+}
+
+static int s2mps11_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ const struct s2mps11_info *s2mps11 = config->driver_data;
+
+ if (s2mps11->dev_type == S2MPS14X)
+ switch (desc->id) {
+ case S2MPS14_LDO10:
+ case S2MPS14_LDO11:
+ case S2MPS14_LDO12:
+ break;
+
+ default:
+ return 0;
+ }
+ else
+ return 0;
+
+ return s2mps11_of_parse_gpiod(np, "samsung,ext-control", false, desc,
+ config);
+}
+
+static int s2mpg10_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ const struct s2mps11_info *s2mps11 = config->driver_data;
+ struct s2mpg10_regulator_desc *s2mpg10_desc = to_s2mpg10_regulator_desc(desc);
+ static const u32 ext_control_s2mpg10[] = {
+ [S2MPG10_EXTCTRL_PWREN] = S2MPG10_PCTRLSEL_PWREN,
+ [S2MPG10_EXTCTRL_PWREN_MIF] = S2MPG10_PCTRLSEL_PWREN_MIF,
+ [S2MPG10_EXTCTRL_AP_ACTIVE_N] = S2MPG10_PCTRLSEL_AP_ACTIVE_N,
+ [S2MPG10_EXTCTRL_CPUCL1_EN] = S2MPG10_PCTRLSEL_CPUCL1_EN,
+ [S2MPG10_EXTCTRL_CPUCL1_EN2] = S2MPG10_PCTRLSEL_CPUCL1_EN2,
+ [S2MPG10_EXTCTRL_CPUCL2_EN] = S2MPG10_PCTRLSEL_CPUCL2_EN,
+ [S2MPG10_EXTCTRL_CPUCL2_EN2] = S2MPG10_PCTRLSEL_CPUCL2_EN2,
+ [S2MPG10_EXTCTRL_TPU_EN] = S2MPG10_PCTRLSEL_TPU_EN,
+ [S2MPG10_EXTCTRL_TPU_EN2] = S2MPG10_PCTRLSEL_TPU_EN2,
+ [S2MPG10_EXTCTRL_TCXO_ON] = S2MPG10_PCTRLSEL_TCXO_ON,
+ [S2MPG10_EXTCTRL_TCXO_ON2] = S2MPG10_PCTRLSEL_TCXO_ON2,
+ [S2MPG10_EXTCTRL_LDO20M_EN2] = S2MPG10_PCTRLSEL_LDO20M_EN2,
+ [S2MPG10_EXTCTRL_LDO20M_EN] = S2MPG10_PCTRLSEL_LDO20M_EN,
+ };
+ static const u32 ext_control_s2mpg11[] = {
+ [S2MPG11_EXTCTRL_PWREN] = S2MPG10_PCTRLSEL_PWREN,
+ [S2MPG11_EXTCTRL_PWREN_MIF] = S2MPG10_PCTRLSEL_PWREN_MIF,
+ [S2MPG11_EXTCTRL_AP_ACTIVE_N] = S2MPG10_PCTRLSEL_AP_ACTIVE_N,
+ [S2MPG11_EXTCTRL_G3D_EN] = S2MPG10_PCTRLSEL_CPUCL1_EN,
+ [S2MPG11_EXTCTRL_G3D_EN2] = S2MPG10_PCTRLSEL_CPUCL1_EN2,
+ [S2MPG11_EXTCTRL_AOC_VDD] = S2MPG10_PCTRLSEL_CPUCL2_EN,
+ [S2MPG11_EXTCTRL_AOC_RET] = S2MPG10_PCTRLSEL_CPUCL2_EN2,
+ [S2MPG11_EXTCTRL_UFS_EN] = S2MPG10_PCTRLSEL_TPU_EN,
+ [S2MPG11_EXTCTRL_LDO13S_EN] = S2MPG10_PCTRLSEL_TPU_EN2,
+ };
+ u32 ext_control;
+
+ if (s2mps11->dev_type != S2MPG10 && s2mps11->dev_type != S2MPG11)
+ return 0;
+
+ if (of_property_read_u32(np, "samsung,ext-control", &ext_control))
+ return 0;
+
+ switch (s2mps11->dev_type) {
+ case S2MPG10:
+ switch (desc->id) {
+ case S2MPG10_BUCK1 ... S2MPG10_BUCK7:
+ case S2MPG10_BUCK10:
+ case S2MPG10_LDO3 ... S2MPG10_LDO19:
+ if (ext_control > S2MPG10_EXTCTRL_TCXO_ON2)
+ return -EINVAL;
+ break;
+
+ case S2MPG10_LDO20:
+ if (ext_control < S2MPG10_EXTCTRL_LDO20M_EN2 ||
+ ext_control > S2MPG10_EXTCTRL_LDO20M_EN)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ext_control > ARRAY_SIZE(ext_control_s2mpg10))
+ return -EINVAL;
+ ext_control = ext_control_s2mpg10[ext_control];
+ break;
+
+ case S2MPG11:
+ switch (desc->id) {
+ case S2MPG11_BUCK1 ... S2MPG11_BUCK3:
+ case S2MPG11_BUCK5:
+ case S2MPG11_BUCK8:
+ case S2MPG11_BUCK9:
+ case S2MPG11_BUCKD:
+ case S2MPG11_BUCKA:
+ case S2MPG11_LDO1:
+ case S2MPG11_LDO2:
+ case S2MPG11_LDO8:
+ case S2MPG11_LDO13:
+ if (ext_control > S2MPG11_EXTCTRL_LDO13S_EN)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ext_control > ARRAY_SIZE(ext_control_s2mpg11))
+ return -EINVAL;
+ ext_control = ext_control_s2mpg11[ext_control];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If the regulator should be configured for external control, then:
+ * 1) the PCTRLSELx register needs to be set accordingly
+ * 2) regulator_desc::enable_val needs to be:
+ * a) updated and
+ * b) written to the hardware
+ * 3) we switch to the ::ops that provide an empty ::enable() and no
+ * ::disable() implementations
+ *
+ * Points 1) and 2b) will be handled in _probe(), after
+ * devm_regulator_register() returns, so that we can properly act on
+ * failures, since the regulator core ignores most return values from
+ * this parse callback.
+ */
+ s2mpg10_desc->pctrlsel_val = ext_control;
+ s2mpg10_desc->pctrlsel_val <<= (ffs(s2mpg10_desc->pctrlsel_mask) - 1);
+
+ s2mpg10_desc->desc.enable_val = S2MPG10_PMIC_CTRL_ENABLE_EXT;
+ s2mpg10_desc->desc.enable_val <<= (ffs(desc->enable_mask) - 1);
+
+ ++s2mpg10_desc->desc.ops;
+
+ return s2mps11_of_parse_gpiod(np, "enable", true, desc, config);
+}
+
+static int s2mpg10_enable_ext_control(struct s2mps11_info *s2mps11,
+ struct regulator_dev *rdev)
+{
+ const struct s2mpg10_regulator_desc *s2mpg10_desc;
+ int ret;
+
+ switch (s2mps11->dev_type) {
+ case S2MPG10:
+ case S2MPG11:
+ s2mpg10_desc = to_s2mpg10_regulator_desc(rdev->desc);
+ break;
+
+ default:
+ return 0;
+ }
+
+ ret = regmap_update_bits(rdev_get_regmap(rdev),
+ s2mpg10_desc->pctrlsel_reg,
+ s2mpg10_desc->pctrlsel_mask,
+ s2mpg10_desc->pctrlsel_val);
+ if (ret)
+ return dev_err_probe(rdev_get_dev(rdev), ret,
+ "failed to configure pctrlsel for %s\n",
+ rdev->desc->name);
+
+ /*
+ * When using external control, the enable bit of the regulator still
+ * needs to be set. The actual state will still be determined by the
+ * external signal.
+ */
+ ret = regulator_enable_regmap(rdev);
+ if (ret)
+ return dev_err_probe(rdev_get_dev(rdev), ret,
+ "failed to enable regulator %s\n",
+ rdev->desc->name);
+
+ return 0;
+}
+
+static int s2mpg10_regulator_enable_nop(struct regulator_dev *rdev)
+{
+ /*
+ * We need to provide this, otherwise the regulator core's enable on
+ * this regulator will return a failure and subsequently disable our
+ * parent regulator.
+ */
+ return 0;
+}
+
+static int s2mpg10_regulator_buck_enable_time(struct regulator_dev *rdev)
+{
+ const struct s2mpg10_regulator_desc * const s2mpg10_desc =
+ to_s2mpg10_regulator_desc(rdev->desc);
+ const struct regulator_ops * const ops = rdev->desc->ops;
+ int vsel, curr_uV;
+
+ vsel = ops->get_voltage_sel(rdev);
+ if (vsel < 0)
+ return vsel;
+
+ curr_uV = ops->list_voltage(rdev, vsel);
+ if (curr_uV < 0)
+ return curr_uV;
+
+ return (rdev->desc->enable_time
+ + DIV_ROUND_UP(curr_uV, s2mpg10_desc->enable_ramp_rate));
+}
+
+static int s2mpg1x_regulator_buck_set_voltage_time(struct regulator_dev *rdev,
+ int old_uV, int new_uV,
+ unsigned int ramp_reg,
+ unsigned int ramp_mask)
+{
+ unsigned int ramp_sel, ramp_rate;
+ int ret;
+
+ if (old_uV == new_uV)
+ return 0;
+
+ ret = regmap_read(rdev->regmap, ramp_reg, &ramp_sel);
+ if (ret)
+ return ret;
+
+ ramp_sel &= ramp_mask;
+ ramp_sel >>= ffs(ramp_mask) - 1;
+ if (ramp_sel >= rdev->desc->n_ramp_values ||
+ !rdev->desc->ramp_delay_table)
+ return -EINVAL;
+
+ ramp_rate = rdev->desc->ramp_delay_table[ramp_sel];
+
+ return DIV_ROUND_UP(abs(new_uV - old_uV), ramp_rate);
+}
+
+static int s2mpg10_regulator_buck_set_voltage_time(struct regulator_dev *rdev,
+ int old_uV, int new_uV)
+{
+ unsigned int ramp_reg;
+
+ ramp_reg = rdev->desc->ramp_reg;
+ if (old_uV > new_uV)
+ /* The downwards ramp is at a different offset. */
+ ramp_reg += S2MPG10_PMIC_DVS_RAMP4 - S2MPG10_PMIC_DVS_RAMP1;
+
+ return s2mpg1x_regulator_buck_set_voltage_time(rdev, old_uV, new_uV,
+ ramp_reg,
+ rdev->desc->ramp_mask);
+}
+
+static int s2mpg11_regulator_buck_set_voltage_time(struct regulator_dev *rdev,
+ int old_uV, int new_uV)
+{
+ unsigned int ramp_mask;
+
+ ramp_mask = rdev->desc->ramp_mask;
+ if (old_uV > new_uV)
+ /* The downwards mask is at a different position. */
+ ramp_mask >>= 2;
+
+ return s2mpg1x_regulator_buck_set_voltage_time(rdev, old_uV, new_uV,
+ rdev->desc->ramp_reg,
+ ramp_mask);
+}
+
+/*
+ * We assign both, ::set_voltage_time() and ::set_voltage_time_sel(), because
+ * only if the latter is != NULL, the regulator core will call neither during
+ * DVS if the regulator is disabled. If the latter is NULL, the core always
+ * calls the ::set_voltage_time() callback, which would give incorrect results
+ * if the regulator is off.
+ * At the same time, we do need ::set_voltage_time() due to differing upwards
+ * and downwards ramps and we can not make that code dependent on the regulator
+ * enable state, as that would break regulator_set_voltage_time() which
+ * expects a correct result no matter the enable state.
+ */
+static const struct regulator_ops s2mpg10_reg_buck_ops[] = {
+ [S2MPG10_REGULATOR_OPS_STD] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .enable_time = s2mpg10_regulator_buck_enable_time,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time = s2mpg10_regulator_buck_set_voltage_time,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ },
+ [S2MPG10_REGULATOR_OPS_EXTCONTROL] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .enable = s2mpg10_regulator_enable_nop,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time = s2mpg10_regulator_buck_set_voltage_time,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ }
+};
+
+#define s2mpg10_buck_to_ramp_mask(n) (GENMASK(1, 0) << (((n) % 4) * 2))
+
+/*
+ * The ramp_delay during enable is fixed (12.5mV/μs), while the ramp during
+ * DVS can be adjusted. Linux can adjust the ramp delay via DT, in which case
+ * the regulator core will modify the regulator's constraints and call our
+ * .set_ramp_delay() which updates the DVS ramp in ramp_reg.
+ * For enable, our .enable_time() unconditionally uses enable_ramp_rate
+ * (12.5mV/μs) while our ::set_voltage_time() takes the value in ramp_reg
+ * into account.
+ */
+#define regulator_desc_s2mpg1x_buck_cmn(_name, _id, _supply, _ops, \
+ _vrange, _vsel_reg, _vsel_mask, _en_reg, _en_mask, \
+ _r_reg, _r_mask, _r_table, _r_table_sz, \
+ _en_time) { \
+ .name = "buck" _name, \
+ .supply_name = _supply, \
+ .of_match = of_match_ptr("buck" _name), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = s2mpg10_of_parse_cb, \
+ .id = _id, \
+ .ops = &(_ops)[0], \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = _vrange, \
+ .n_linear_ranges = ARRAY_SIZE(_vrange), \
+ .n_voltages = _vrange##_count, \
+ .vsel_reg = _vsel_reg, \
+ .vsel_mask = _vsel_mask, \
+ .enable_reg = _en_reg, \
+ .enable_mask = _en_mask, \
+ .ramp_reg = _r_reg, \
+ .ramp_mask = _r_mask, \
+ .ramp_delay_table = _r_table, \
+ .n_ramp_values = _r_table_sz, \
+ .enable_time = _en_time, /* + V/enable_ramp_rate */ \
+}
+
+#define regulator_desc_s2mpg10_buck(_num, _vrange, _r_reg) \
+ regulator_desc_s2mpg1x_buck_cmn(#_num "m", S2MPG10_BUCK##_num, \
+ "vinb"#_num "m", s2mpg10_reg_buck_ops, _vrange, \
+ S2MPG10_PMIC_B##_num##M_OUT1, GENMASK(7, 0), \
+ S2MPG10_PMIC_B##_num##M_CTRL, GENMASK(7, 6), \
+ S2MPG10_PMIC_##_r_reg, \
+ s2mpg10_buck_to_ramp_mask(S2MPG10_BUCK##_num \
+ - S2MPG10_BUCK1), \
+ s2mpg10_buck_ramp_table, \
+ ARRAY_SIZE(s2mpg10_buck_ramp_table), 30)
+
+#define s2mpg10_regulator_desc_buck_cm(_num, _vrange, _r_reg) \
+ .desc = regulator_desc_s2mpg10_buck(_num, _vrange, _r_reg), \
+ .enable_ramp_rate = 12500
+
+#define s2mpg10_regulator_desc_buck_gpio(_num, _vrange, _r_reg, \
+ _pc_reg, _pc_mask) \
+ [S2MPG10_BUCK##_num] = { \
+ s2mpg10_regulator_desc_buck_cm(_num, _vrange, _r_reg), \
+ .pctrlsel_reg = S2MPG10_PMIC_##_pc_reg, \
+ .pctrlsel_mask = _pc_mask, \
+ }
+
+#define s2mpg10_regulator_desc_buck(_num, _vrange, _r_reg) \
+ [S2MPG10_BUCK##_num] = { \
+ s2mpg10_regulator_desc_buck_cm(_num, _vrange, _r_reg), \
+ }
+
+/* ops for S2MPG1x LDO regulators without ramp control */
+static const struct regulator_ops s2mpg10_reg_ldo_ops[] = {
+ [S2MPG10_REGULATOR_OPS_STD] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ },
+ [S2MPG10_REGULATOR_OPS_EXTCONTROL] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .enable = s2mpg10_regulator_enable_nop,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ }
+};
+
+/* ops for S2MPG1x LDO regulators that have ramp control */
+static const struct regulator_ops s2mpg10_reg_ldo_ramp_ops[] = {
+ [S2MPG10_REGULATOR_OPS_STD] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ },
+ [S2MPG10_REGULATOR_OPS_EXTCONTROL] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .enable = s2mpg10_regulator_enable_nop,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ }
+};
+
+#define regulator_desc_s2mpg1x_ldo_cmn(_name, _id, _supply, _ops, \
+ _vrange, _vsel_reg, _vsel_mask, _en_reg, _en_mask, \
+ _ramp_delay, _r_reg, _r_mask, _r_table, _r_table_sz) { \
+ .name = "ldo" _name, \
+ .supply_name = _supply, \
+ .of_match = of_match_ptr("ldo" _name), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = s2mpg10_of_parse_cb, \
+ .id = _id, \
+ .ops = &(_ops)[0], \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = _vrange, \
+ .n_linear_ranges = ARRAY_SIZE(_vrange), \
+ .n_voltages = _vrange##_count, \
+ .vsel_reg = _vsel_reg, \
+ .vsel_mask = _vsel_mask, \
+ .enable_reg = _en_reg, \
+ .enable_mask = _en_mask, \
+ .ramp_delay = _ramp_delay, \
+ .ramp_reg = _r_reg, \
+ .ramp_mask = _r_mask, \
+ .ramp_delay_table = _r_table, \
+ .n_ramp_values = _r_table_sz, \
+ .enable_time = 130, /* startup 20+-10 + ramp 30..100μs */ \
+}
+
+#define s2mpg10_regulator_desc_ldo_cmn(_num, _supply, _ops, _vrange, \
+ _vsel_reg_sfx, _vsel_mask, _en_reg, _en_mask, \
+ _ramp_delay, _r_reg, _r_mask, _r_table, _r_table_sz, \
+ _pc_reg, _pc_mask) \
+ [S2MPG10_LDO##_num] = { \
+ .desc = regulator_desc_s2mpg1x_ldo_cmn(#_num "m", \
+ S2MPG10_LDO##_num, _supply, _ops, \
+ _vrange, \
+ S2MPG10_PMIC_L##_num##M_##_vsel_reg_sfx, \
+ _vsel_mask, \
+ S2MPG10_PMIC_##_en_reg, _en_mask, \
+ _ramp_delay, _r_reg, _r_mask, _r_table, \
+ _r_table_sz), \
+ .pctrlsel_reg = _pc_reg, \
+ .pctrlsel_mask = _pc_mask, \
+ }
+
+/* standard LDO via LxM_CTRL */
+#define s2mpg10_regulator_desc_ldo(_num, _supply, _vrange) \
+ s2mpg10_regulator_desc_ldo_cmn(_num, _supply, \
+ s2mpg10_reg_ldo_ops, _vrange, CTRL, GENMASK(5, 0), \
+ L##_num##M_CTRL, BIT(7), \
+ 0, 0, 0, NULL, 0, \
+ 0, 0)
+
+/* standard LDO but possibly GPIO controlled */
+#define s2mpg10_regulator_desc_ldo_gpio(_num, _supply, _vrange, \
+ _pc_reg, _pc_mask) \
+ s2mpg10_regulator_desc_ldo_cmn(_num, _supply, \
+ s2mpg10_reg_ldo_ops, _vrange, CTRL, GENMASK(5, 0), \
+ L##_num##M_CTRL, GENMASK(7, 6), \
+ 0, 0, 0, NULL, 0, \
+ S2MPG10_PMIC_##_pc_reg, _pc_mask)
+
+/* LDO with ramp support and possibly GPIO controlled */
+#define s2mpg10_regulator_desc_ldo_ramp(_num, _supply, _vrange, \
+ _en_mask, _r_reg, _pc_reg, _pc_mask) \
+ s2mpg10_regulator_desc_ldo_cmn(_num, _supply, \
+ s2mpg10_reg_ldo_ramp_ops, _vrange, CTRL1, GENMASK(6, 0), \
+ LDO_CTRL2, _en_mask, \
+ 6250, S2MPG10_PMIC_##_r_reg, GENMASK(1, 0), \
+ s2mpg10_ldo_ramp_table, \
+ ARRAY_SIZE(s2mpg10_ldo_ramp_table), \
+ S2MPG10_PMIC_##_pc_reg, _pc_mask)
+
+#define S2MPG10_VOLTAGE_RANGE(_prefix, _idx, _offs_uV, _min_uV, \
+ _max_uV, _step_uV) \
+static const struct linear_range _prefix##_vranges##_idx[] = { \
+ REGULATOR_LINEAR_VRANGE(_offs_uV, _min_uV, _max_uV, _step_uV) \
+}; \
+static const unsigned int _prefix##_vranges##_idx##_count = \
+ ((((_max_uV) - (_offs_uV)) / (_step_uV)) + 1)
+
+/* voltage range for s2mpg10 BUCK 1, 2, 3, 4, 5, 7, 8, 9, 10 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_buck, 1, 200000, 450000, 1300000, STEP_6_25_MV);
+
+/* voltage range for s2mpg10 BUCK 6 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_buck, 6, 200000, 450000, 1350000, STEP_6_25_MV);
+
+static const unsigned int s2mpg10_buck_ramp_table[] = {
+ 6250, 12500, 25000
+};
+
+/* voltage range for s2mpg10 LDO 1, 11, 12 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 1, 300000, 700000, 1300000, STEP_12_5_MV);
+
+/* voltage range for s2mpg10 LDO 2, 4, 9, 14, 18, 19, 20, 23, 25, 29, 30, 31 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 2, 700000, 1600000, 1950000, STEP_25_MV);
+
+/* voltage range for s2mpg10 LDO 3, 5, 6, 8, 16, 17, 24, 28 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 3, 725000, 725000, 1300000, STEP_12_5_MV);
+
+/* voltage range for s2mpg10 LDO 7 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 7, 300000, 450000, 1300000, STEP_12_5_MV);
+
+/* voltage range for s2mpg10 LDO 13, 15 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 13, 300000, 450000, 950000, STEP_12_5_MV);
+
+/* voltage range for s2mpg10 LDO 10 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 10, 1800000, 1800000, 3350000, STEP_25_MV);
+
+/* voltage range for s2mpg10 LDO 21, 22, 26, 27 */
+S2MPG10_VOLTAGE_RANGE(s2mpg10_ldo, 21, 1800000, 2500000, 3300000, STEP_25_MV);
+
+/* possible ramp values for s2mpg10 LDO 1, 7, 11, 12, 13, 15 */
+static const unsigned int s2mpg10_ldo_ramp_table[] = {
+ 6250, 12500
+};
+
+static const struct s2mpg10_regulator_desc s2mpg10_regulators[] = {
+ s2mpg10_regulator_desc_buck_gpio(1, s2mpg10_buck_vranges1, DVS_RAMP1,
+ PCTRLSEL1, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_buck_gpio(2, s2mpg10_buck_vranges1, DVS_RAMP1,
+ PCTRLSEL1, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_buck_gpio(3, s2mpg10_buck_vranges1, DVS_RAMP1,
+ PCTRLSEL2, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_buck_gpio(4, s2mpg10_buck_vranges1, DVS_RAMP1,
+ PCTRLSEL2, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_buck_gpio(5, s2mpg10_buck_vranges1, DVS_RAMP2,
+ PCTRLSEL3, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_buck_gpio(6, s2mpg10_buck_vranges6, DVS_RAMP2,
+ PCTRLSEL3, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_buck_gpio(7, s2mpg10_buck_vranges1, DVS_RAMP2,
+ PCTRLSEL4, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_buck(8, s2mpg10_buck_vranges1, DVS_RAMP2),
+ s2mpg10_regulator_desc_buck(9, s2mpg10_buck_vranges1, DVS_RAMP3),
+ s2mpg10_regulator_desc_buck_gpio(10, s2mpg10_buck_vranges1, DVS_RAMP3,
+ PCTRLSEL4, GENMASK(7, 4)),
+ /*
+ * Standard LDO via LxM_CTRL but non-standard (greater) V-range and with
+ * ramp support.
+ */
+ s2mpg10_regulator_desc_ldo_cmn(1, "vinl3m", s2mpg10_reg_ldo_ramp_ops,
+ s2mpg10_ldo_vranges1,
+ CTRL, GENMASK(6, 0),
+ L1M_CTRL, BIT(7),
+ 6250, S2MPG10_PMIC_DVS_RAMP6,
+ GENMASK(5, 4), s2mpg10_ldo_ramp_table,
+ ARRAY_SIZE(s2mpg10_ldo_ramp_table),
+ 0, 0),
+ s2mpg10_regulator_desc_ldo(2, "vinl9m", s2mpg10_ldo_vranges2),
+ s2mpg10_regulator_desc_ldo_gpio(3, "vinl4m", s2mpg10_ldo_vranges3,
+ PCTRLSEL5, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(4, "vinl9m", s2mpg10_ldo_vranges2,
+ PCTRLSEL5, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_gpio(5, "vinl3m", s2mpg10_ldo_vranges3,
+ PCTRLSEL6, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(6, "vinl7m", s2mpg10_ldo_vranges3,
+ PCTRLSEL6, GENMASK(7, 4)),
+ /*
+ * Ramp support, possibly GPIO controlled, non-standard (greater) V-
+ * range and enable reg & mask.
+ */
+ s2mpg10_regulator_desc_ldo_cmn(7, "vinl3m", s2mpg10_reg_ldo_ramp_ops,
+ s2mpg10_ldo_vranges7,
+ CTRL, GENMASK(6, 0),
+ LDO_CTRL1, GENMASK(4, 3),
+ 6250, S2MPG10_PMIC_DVS_RAMP6,
+ GENMASK(7, 6), s2mpg10_ldo_ramp_table,
+ ARRAY_SIZE(s2mpg10_ldo_ramp_table),
+ S2MPG10_PMIC_PCTRLSEL7, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(8, "vinl4m", s2mpg10_ldo_vranges3,
+ PCTRLSEL7, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_gpio(9, "vinl10m", s2mpg10_ldo_vranges2,
+ PCTRLSEL8, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(10, "vinl15m", s2mpg10_ldo_vranges10,
+ PCTRLSEL8, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_ramp(11, "vinl7m", s2mpg10_ldo_vranges1,
+ GENMASK(1, 0), DVS_SYNC_CTRL3,
+ PCTRLSEL9, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_ramp(12, "vinl8m", s2mpg10_ldo_vranges1,
+ GENMASK(3, 2), DVS_SYNC_CTRL4,
+ PCTRLSEL9, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_ramp(13, "vinl1m", s2mpg10_ldo_vranges13,
+ GENMASK(5, 4), DVS_SYNC_CTRL5,
+ PCTRLSEL10, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(14, "vinl10m", s2mpg10_ldo_vranges2,
+ PCTRLSEL10, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_ramp(15, "vinl2m", s2mpg10_ldo_vranges13,
+ GENMASK(7, 6), DVS_SYNC_CTRL6,
+ PCTRLSEL11, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(16, "vinl5m", s2mpg10_ldo_vranges3,
+ PCTRLSEL11, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_gpio(17, "vinl6m", s2mpg10_ldo_vranges3,
+ PCTRLSEL12, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(18, "vinl10m", s2mpg10_ldo_vranges2,
+ PCTRLSEL12, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo_gpio(19, "vinl10m", s2mpg10_ldo_vranges2,
+ PCTRLSEL13, GENMASK(3, 0)),
+ s2mpg10_regulator_desc_ldo_gpio(20, "vinl10m", s2mpg10_ldo_vranges2,
+ PCTRLSEL13, GENMASK(7, 4)),
+ s2mpg10_regulator_desc_ldo(21, "vinl14m", s2mpg10_ldo_vranges21),
+ s2mpg10_regulator_desc_ldo(22, "vinl15m", s2mpg10_ldo_vranges21),
+ s2mpg10_regulator_desc_ldo(23, "vinl11m", s2mpg10_ldo_vranges2),
+ s2mpg10_regulator_desc_ldo(24, "vinl7m", s2mpg10_ldo_vranges3),
+ s2mpg10_regulator_desc_ldo(25, "vinl10m", s2mpg10_ldo_vranges2),
+ s2mpg10_regulator_desc_ldo(26, "vinl15m", s2mpg10_ldo_vranges21),
+ s2mpg10_regulator_desc_ldo(27, "vinl15m", s2mpg10_ldo_vranges21),
+ s2mpg10_regulator_desc_ldo(28, "vinl7m", s2mpg10_ldo_vranges3),
+ s2mpg10_regulator_desc_ldo(29, "vinl12m", s2mpg10_ldo_vranges2),
+ s2mpg10_regulator_desc_ldo(30, "vinl13m", s2mpg10_ldo_vranges2),
+ s2mpg10_regulator_desc_ldo(31, "vinl11m", s2mpg10_ldo_vranges2)
+};
+
+static const struct regulator_ops s2mpg11_reg_buck_ops[] = {
+ [S2MPG10_REGULATOR_OPS_STD] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time = s2mpg11_regulator_buck_set_voltage_time,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable_time = s2mpg10_regulator_buck_enable_time,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ },
+ [S2MPG10_REGULATOR_OPS_EXTCONTROL] = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .enable = s2mpg10_regulator_enable_nop,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time = s2mpg11_regulator_buck_set_voltage_time,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .enable_time = s2mpg10_regulator_buck_enable_time,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+ }
+};
+
+#define s2mpg11_buck_to_ramp_mask(n) (GENMASK(3, 2) << (((n) % 2) * 4))
+
+#define regulator_desc_s2mpg11_buckx(_name, _id, _supply, _vrange, \
+ _vsel_reg, _en_reg, _en_mask, _r_reg) \
+ regulator_desc_s2mpg1x_buck_cmn(_name, _id, _supply, \
+ s2mpg11_reg_buck_ops, _vrange, \
+ S2MPG11_PMIC_##_vsel_reg, GENMASK(7, 0), \
+ S2MPG11_PMIC_##_en_reg, _en_mask, \
+ S2MPG11_PMIC_##_r_reg, \
+ s2mpg11_buck_to_ramp_mask(_id - S2MPG11_BUCK1), \
+ s2mpg10_buck_ramp_table, \
+ ARRAY_SIZE(s2mpg10_buck_ramp_table), 30)
+
+#define s2mpg11_regulator_desc_buck_xm(_num, _vrange, _vsel_reg_sfx, \
+ _en_mask, _r_reg, _en_rrate) \
+ .desc = regulator_desc_s2mpg11_buckx(#_num"s", \
+ S2MPG11_BUCK##_num, "vinb"#_num"s", \
+ _vrange, \
+ B##_num##S_##_vsel_reg_sfx, \
+ B##_num##S_CTRL, _en_mask, \
+ _r_reg), \
+ .enable_ramp_rate = _en_rrate
+
+#define s2mpg11_regulator_desc_buck_cm(_num, _vrange, _vsel_reg_sfx, \
+ _en_mask, _r_reg) \
+ [S2MPG11_BUCK##_num] = { \
+ s2mpg11_regulator_desc_buck_xm(_num, _vrange, \
+ _vsel_reg_sfx, _en_mask, _r_reg, 12500), \
+ }
+
+#define s2mpg11_regulator_desc_buckn_cm_gpio(_num, _vrange, \
+ _vsel_reg_sfx, _en_mask, _r_reg, _pc_reg, _pc_mask) \
+ [S2MPG11_BUCK##_num] = { \
+ s2mpg11_regulator_desc_buck_xm(_num, _vrange, \
+ _vsel_reg_sfx, _en_mask, _r_reg, 12500), \
+ .pctrlsel_reg = S2MPG11_PMIC_##_pc_reg, \
+ .pctrlsel_mask = _pc_mask, \
+ }
+
+#define s2mpg11_regulator_desc_buck_vm(_num, _vrange, _vsel_reg_sfx, \
+ _en_mask, _r_reg) \
+ [S2MPG11_BUCK##_num] = { \
+ s2mpg11_regulator_desc_buck_xm(_num, _vrange, \
+ _vsel_reg_sfx, _en_mask, _r_reg, 25000), \
+ }
+
+#define s2mpg11_regulator_desc_bucka(_num, _num_lower, _r_reg, \
+ _pc_reg, _pc_mask) \
+ [S2MPG11_BUCK##_num] = { \
+ .desc = regulator_desc_s2mpg11_buckx(#_num_lower, \
+ S2MPG11_BUCK##_num, "vinb"#_num_lower, \
+ s2mpg11_buck_vranges##_num_lower, \
+ BUCK##_num##_OUT, \
+ BUCK##_num##_CTRL, GENMASK(7, 6), \
+ _r_reg), \
+ .enable_ramp_rate = 25000, \
+ .pctrlsel_reg = S2MPG11_PMIC_##_pc_reg, \
+ .pctrlsel_mask = _pc_mask, \
+ }
+
+#define s2mpg11_regulator_desc_buckboost() \
+ [S2MPG11_BUCKBOOST] = { \
+ .desc = regulator_desc_s2mpg1x_buck_cmn("boost", \
+ S2MPG11_BUCKBOOST, "vinbb", \
+ s2mpg10_reg_ldo_ops, \
+ s2mpg11_buck_vrangesboost, \
+ S2MPG11_PMIC_BB_OUT1, GENMASK(6, 0), \
+ S2MPG11_PMIC_BB_CTRL, BIT(7), \
+ 0, 0, NULL, 0, 35), \
+ .enable_ramp_rate = 17500, \
+ }
+
+#define s2mpg11_regulator_desc_ldo_cmn(_num, _supply, _ops, \
+ _vrange, _vsel_reg_sfx, _vsel_mask, _en_reg, _en_mask, \
+ _ramp_delay, _r_reg, _r_mask, _r_table, _r_table_sz, \
+ _pc_reg, _pc_mask) \
+ [S2MPG11_LDO##_num] = { \
+ .desc = regulator_desc_s2mpg1x_ldo_cmn(#_num "s", \
+ S2MPG11_LDO##_num, _supply, _ops, \
+ _vrange, \
+ S2MPG11_PMIC_L##_num##S_##_vsel_reg_sfx, \
+ _vsel_mask, \
+ S2MPG11_PMIC_##_en_reg, _en_mask, \
+ _ramp_delay, _r_reg, _r_mask, _r_table, \
+ _r_table_sz), \
+ .pctrlsel_reg = _pc_reg, \
+ .pctrlsel_mask = _pc_mask, \
+ }
+
+/* standard LDO via LxM_CTRL */
+#define s2mpg11_regulator_desc_ldo(_num, _supply, _vrange) \
+ s2mpg11_regulator_desc_ldo_cmn(_num, _supply, \
+ s2mpg10_reg_ldo_ops, _vrange, CTRL, GENMASK(5, 0), \
+ L##_num##S_CTRL, BIT(7), \
+ 0, 0, 0, NULL, 0, \
+ 0, 0)
+
+/* standard LDO but possibly GPIO controlled */
+#define s2mpg11_regulator_desc_ldo_gpio(_num, _supply, _vrange, \
+ _pc_reg, _pc_mask) \
+ s2mpg11_regulator_desc_ldo_cmn(_num, _supply, \
+ s2mpg10_reg_ldo_ops, _vrange, CTRL, GENMASK(5, 0), \
+ L##_num##S_CTRL, GENMASK(7, 6), \
+ 0, 0, 0, NULL, 0, \
+ S2MPG11_PMIC_##_pc_reg, _pc_mask)
+
+/* LDO with ramp support and possibly GPIO controlled */
+#define s2mpg11_regulator_desc_ldo_ramp(_num, _supply, _vrange, \
+ _en_mask, _r_reg, _pc_reg, _pc_mask) \
+ s2mpg11_regulator_desc_ldo_cmn(_num, _supply, \
+ s2mpg10_reg_ldo_ramp_ops, _vrange, CTRL1, GENMASK(6, 0), \
+ LDO_CTRL1, _en_mask, \
+ 6250, S2MPG11_PMIC_##_r_reg, GENMASK(1, 0), \
+ s2mpg10_ldo_ramp_table, \
+ ARRAY_SIZE(s2mpg10_ldo_ramp_table), \
+ S2MPG11_PMIC_##_pc_reg, _pc_mask)
+
+/* voltage range for s2mpg11 BUCK 1, 2, 3, 4, 8, 9, 10 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, 1, 200000, 450000, 1300000, STEP_6_25_MV);
+
+/* voltage range for s2mpg11 BUCK 5 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, 5, 200000, 400000, 1300000, STEP_6_25_MV);
+
+/* voltage range for s2mpg11 BUCK 6 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, 6, 200000, 1000000, 1500000, STEP_6_25_MV);
+
+/* voltage range for s2mpg11 BUCK 7 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, 7, 600000, 1500000, 2200000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 BUCK D */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, d, 600000, 2400000, 3300000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 BUCK A */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, a, 600000, 1700000, 2100000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 BUCK BOOST */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_buck, boost,
+ 2600000, 3000000, 3600000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 LDO 1, 2 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 1, 300000, 450000, 950000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 LDO 3, 7, 10, 11, 12, 14, 15 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 3, 700000, 1600000, 1950000, STEP_25_MV);
+
+/* voltage range for s2mpg11 LDO 4, 6 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 4, 1800000, 2500000, 3300000, STEP_25_MV);
+
+/* voltage range for s2mpg11 LDO 5 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 5, 1600000, 1600000, 1950000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 LDO 8 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 8, 979600, 1130400, 1281200, 5800);
+
+/* voltage range for s2mpg11 LDO 9 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 9, 725000, 725000, 1300000, STEP_12_5_MV);
+
+/* voltage range for s2mpg11 LDO 13 */
+S2MPG10_VOLTAGE_RANGE(s2mpg11_ldo, 13, 1800000, 1800000, 3350000, STEP_25_MV);
+
+static const struct s2mpg10_regulator_desc s2mpg11_regulators[] = {
+ s2mpg11_regulator_desc_buckboost(),
+ s2mpg11_regulator_desc_buckn_cm_gpio(1, s2mpg11_buck_vranges1,
+ OUT1, GENMASK(7, 6), DVS_RAMP1,
+ PCTRLSEL1, GENMASK(3, 0)),
+ s2mpg11_regulator_desc_buckn_cm_gpio(2, s2mpg11_buck_vranges1,
+ OUT1, GENMASK(7, 6), DVS_RAMP1,
+ PCTRLSEL1, GENMASK(7, 4)),
+ s2mpg11_regulator_desc_buckn_cm_gpio(3, s2mpg11_buck_vranges1,
+ OUT1, GENMASK(7, 6), DVS_RAMP2,
+ PCTRLSEL2, GENMASK(3, 0)),
+ s2mpg11_regulator_desc_buck_cm(4, s2mpg11_buck_vranges1,
+ OUT, BIT(7), DVS_RAMP2),
+ s2mpg11_regulator_desc_buckn_cm_gpio(5, s2mpg11_buck_vranges5,
+ OUT, GENMASK(7, 6), DVS_RAMP3,
+ PCTRLSEL2, GENMASK(7, 4)),
+ s2mpg11_regulator_desc_buck_cm(6, s2mpg11_buck_vranges6,
+ OUT1, BIT(7), DVS_RAMP3),
+ s2mpg11_regulator_desc_buck_vm(7, s2mpg11_buck_vranges7,
+ OUT1, BIT(7), DVS_RAMP4),
+ s2mpg11_regulator_desc_buckn_cm_gpio(8, s2mpg11_buck_vranges1,
+ OUT1, GENMASK(7, 6), DVS_RAMP4,
+ PCTRLSEL3, GENMASK(3, 0)),
+ s2mpg11_regulator_desc_buckn_cm_gpio(9, s2mpg11_buck_vranges1,
+ OUT1, GENMASK(7, 6), DVS_RAMP5,
+ PCTRLSEL3, GENMASK(7, 4)),
+ s2mpg11_regulator_desc_buck_cm(10, s2mpg11_buck_vranges1,
+ OUT, BIT(7), DVS_RAMP5),
+ s2mpg11_regulator_desc_bucka(D, d, DVS_RAMP6, PCTRLSEL4, GENMASK(3, 0)),
+ s2mpg11_regulator_desc_bucka(A, a, DVS_RAMP6, PCTRLSEL4, GENMASK(7, 4)),
+ s2mpg11_regulator_desc_ldo_ramp(1, "vinl1s", s2mpg11_ldo_vranges1,
+ GENMASK(5, 4), DVS_SYNC_CTRL1,
+ PCTRLSEL5, GENMASK(3, 0)),
+ s2mpg11_regulator_desc_ldo_ramp(2, "vinl1s", s2mpg11_ldo_vranges1,
+ GENMASK(7, 6), DVS_SYNC_CTRL2,
+ PCTRLSEL5, GENMASK(7, 4)),
+ s2mpg11_regulator_desc_ldo(3, "vinl3s", s2mpg11_ldo_vranges3),
+ s2mpg11_regulator_desc_ldo(4, "vinl5s", s2mpg11_ldo_vranges4),
+ s2mpg11_regulator_desc_ldo(5, "vinl3s", s2mpg11_ldo_vranges5),
+ s2mpg11_regulator_desc_ldo(6, "vinl5s", s2mpg11_ldo_vranges4),
+ s2mpg11_regulator_desc_ldo(7, "vinl3s", s2mpg11_ldo_vranges3),
+ s2mpg11_regulator_desc_ldo_gpio(8, "vinl2s", s2mpg11_ldo_vranges8,
+ PCTRLSEL6, GENMASK(3, 0)),
+ s2mpg11_regulator_desc_ldo(9, "vinl2s", s2mpg11_ldo_vranges9),
+ s2mpg11_regulator_desc_ldo(10, "vinl4s", s2mpg11_ldo_vranges3),
+ s2mpg11_regulator_desc_ldo(11, "vinl4s", s2mpg11_ldo_vranges3),
+ s2mpg11_regulator_desc_ldo(12, "vinl4s", s2mpg11_ldo_vranges3),
+ s2mpg11_regulator_desc_ldo_gpio(13, "vinl6s", s2mpg11_ldo_vranges13,
+ PCTRLSEL6, GENMASK(7, 4)),
+ s2mpg11_regulator_desc_ldo(14, "vinl4s", s2mpg11_ldo_vranges3),
+ s2mpg11_regulator_desc_ldo(15, "vinl3s", s2mpg11_ldo_vranges3)
+};
+
static const struct regulator_ops s2mps11_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
@@ -362,6 +1289,8 @@ static const struct regulator_ops s2mps11_buck_ops = {
#define regulator_desc_s2mps11_ldo(num, step) { \
.name = "LDO"#num, \
.id = S2MPS11_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps11_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -378,6 +1307,8 @@ static const struct regulator_ops s2mps11_buck_ops = {
#define regulator_desc_s2mps11_buck1_4(num) { \
.name = "BUCK"#num, \
.id = S2MPS11_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps11_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -395,6 +1326,8 @@ static const struct regulator_ops s2mps11_buck_ops = {
#define regulator_desc_s2mps11_buck5 { \
.name = "BUCK5", \
.id = S2MPS11_BUCK5, \
+ .of_match = of_match_ptr("BUCK5"), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps11_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -412,6 +1345,8 @@ static const struct regulator_ops s2mps11_buck_ops = {
#define regulator_desc_s2mps11_buck67810(num, min, step, min_sel, voltages) { \
.name = "BUCK"#num, \
.id = S2MPS11_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps11_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -429,6 +1364,8 @@ static const struct regulator_ops s2mps11_buck_ops = {
#define regulator_desc_s2mps11_buck9 { \
.name = "BUCK9", \
.id = S2MPS11_BUCK9, \
+ .of_match = of_match_ptr("BUCK9"), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps11_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -502,6 +1439,8 @@ static const struct regulator_ops s2mps14_reg_ops;
#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \
.name = "LDO"#num, \
.id = S2MPS13_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps14_reg_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -518,6 +1457,8 @@ static const struct regulator_ops s2mps14_reg_ops;
#define regulator_desc_s2mps13_buck(num, min, step, min_sel) { \
.name = "BUCK"#num, \
.id = S2MPS13_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps14_reg_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -535,6 +1476,8 @@ static const struct regulator_ops s2mps14_reg_ops;
#define regulator_desc_s2mps13_buck7(num, min, step, min_sel) { \
.name = "BUCK"#num, \
.id = S2MPS13_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps14_reg_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -552,6 +1495,8 @@ static const struct regulator_ops s2mps14_reg_ops;
#define regulator_desc_s2mps13_buck8_10(num, min, step, min_sel) { \
.name = "BUCK"#num, \
.id = S2MPS13_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps14_reg_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -634,6 +1579,9 @@ static const struct regulator_ops s2mps14_reg_ops = {
#define regulator_desc_s2mps14_ldo(num, min, step) { \
.name = "LDO"#num, \
.id = S2MPS14_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = s2mps11_of_parse_cb, \
.ops = &s2mps14_reg_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -649,6 +1597,9 @@ static const struct regulator_ops s2mps14_reg_ops = {
#define regulator_desc_s2mps14_buck(num, min, step, min_sel) { \
.name = "BUCK"#num, \
.id = S2MPS14_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = s2mps11_of_parse_cb, \
.ops = &s2mps14_reg_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -725,6 +1676,8 @@ static const struct regulator_ops s2mps15_reg_buck_ops = {
#define regulator_desc_s2mps15_ldo(num, range) { \
.name = "LDO"#num, \
.id = S2MPS15_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps15_reg_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -740,6 +1693,8 @@ static const struct regulator_ops s2mps15_reg_buck_ops = {
#define regulator_desc_s2mps15_buck(num, range) { \
.name = "BUCK"#num, \
.id = S2MPS15_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mps15_reg_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -829,63 +1784,15 @@ static const struct regulator_desc s2mps15_regulators[] = {
};
static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11,
- struct regulator_dev *rdev)
-{
- return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, S2MPS14_ENABLE_EXT_CONTROL);
-}
-
-static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev,
- struct of_regulator_match *rdata, struct s2mps11_info *s2mps11)
+ struct regulator_dev *rdev)
{
- struct gpio_desc **gpio = s2mps11->ext_control_gpiod;
- unsigned int i;
- unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11,
- S2MPS14_LDO12 };
-
- for (i = 0; i < ARRAY_SIZE(valid_regulators); i++) {
- unsigned int reg = valid_regulators[i];
-
- if (!rdata[reg].init_data || !rdata[reg].of_node)
- continue;
-
- gpio[reg] = devm_fwnode_gpiod_get(&pdev->dev,
- of_fwnode_handle(rdata[reg].of_node),
- "samsung,ext-control",
- GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
- "s2mps11-regulator");
- if (PTR_ERR(gpio[reg]) == -ENOENT)
- gpio[reg] = NULL;
- else if (IS_ERR(gpio[reg])) {
- dev_err(&pdev->dev, "Failed to get control GPIO for %d/%s\n",
- reg, rdata[reg].name);
- gpio[reg] = NULL;
- continue;
- }
- if (gpio[reg])
- dev_dbg(&pdev->dev, "Using GPIO for ext-control over %d/%s\n",
- reg, rdata[reg].name);
- }
-}
-
-static int s2mps11_pmic_dt_parse(struct platform_device *pdev,
- struct of_regulator_match *rdata, struct s2mps11_info *s2mps11,
- unsigned int rdev_num)
-{
- struct device_node *reg_np;
-
- reg_np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
- if (!reg_np) {
- dev_err(&pdev->dev, "could not find regulators sub-node\n");
- return -EINVAL;
- }
-
- of_regulator_match(&pdev->dev, reg_np, rdata, rdev_num);
- if (s2mps11->dev_type == S2MPS14X)
- s2mps14_pmic_dt_parse_ext_control_gpio(pdev, rdata, s2mps11);
-
- of_node_put(reg_np);
-
+ int ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ S2MPS14_ENABLE_EXT_CONTROL);
+ if (ret < 0)
+ return dev_err_probe(rdev_get_dev(rdev), ret,
+ "failed to enable GPIO control over %d/%s\n",
+ rdev->desc->id, rdev->desc->name);
return 0;
}
@@ -946,6 +1853,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_ldo1(num) { \
.name = "LDO"#num, \
.id = S2MPU02_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -961,6 +1870,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_ldo2(num) { \
.name = "LDO"#num, \
.id = S2MPU02_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -976,6 +1887,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_ldo3(num) { \
.name = "LDO"#num, \
.id = S2MPU02_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -991,6 +1904,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_ldo4(num) { \
.name = "LDO"#num, \
.id = S2MPU02_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1006,6 +1921,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_ldo5(num) { \
.name = "LDO"#num, \
.id = S2MPU02_LDO##num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1022,6 +1939,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_buck1234(num) { \
.name = "BUCK"#num, \
.id = S2MPU02_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1038,6 +1957,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_buck5(num) { \
.name = "BUCK"#num, \
.id = S2MPU02_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1054,6 +1975,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_buck6(num) { \
.name = "BUCK"#num, \
.id = S2MPU02_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1070,6 +1993,8 @@ static const struct regulator_ops s2mpu02_buck_ops = {
#define regulator_desc_s2mpu02_buck7(num) { \
.name = "BUCK"#num, \
.id = S2MPU02_BUCK##num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1125,6 +2050,8 @@ static const struct regulator_desc s2mpu02_regulators[] = {
#define regulator_desc_s2mpu05_ldo_reg(num, min, step, reg) { \
.name = "ldo"#num, \
.id = S2MPU05_LDO##num, \
+ .of_match = of_match_ptr("ldo"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1156,6 +2083,8 @@ static const struct regulator_desc s2mpu02_regulators[] = {
#define regulator_desc_s2mpu05_buck(num, which) { \
.name = "buck"#num, \
.id = S2MPU05_BUCK##num, \
+ .of_match = of_match_ptr("buck"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.ops = &s2mpu02_buck_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -1202,14 +2131,48 @@ static const struct regulator_desc s2mpu05_regulators[] = {
regulator_desc_s2mpu05_buck45(5),
};
+static int s2mps11_handle_ext_control(struct s2mps11_info *s2mps11,
+ struct regulator_dev *rdev)
+{
+ int ret;
+
+ switch (s2mps11->dev_type) {
+ case S2MPS14X:
+ if (!rdev->ena_pin)
+ return 0;
+
+ ret = s2mps14_pmic_enable_ext_control(s2mps11, rdev);
+ break;
+
+ case S2MPG10:
+ case S2MPG11:
+ /*
+ * If desc.enable_val is != 0, then external control was
+ * requested. We can not test s2mpg10_desc::ext_control,
+ * because 0 is a valid value.
+ */
+ if (!rdev->desc->enable_val)
+ return 0;
+
+ ret = s2mpg10_enable_ext_control(s2mps11, rdev);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return ret;
+}
+
static int s2mps11_pmic_probe(struct platform_device *pdev)
{
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
struct s2mps11_info *s2mps11;
- unsigned int rdev_num = 0;
- int i, ret = 0;
+ unsigned int rdev_num;
+ int i, ret;
const struct regulator_desc *regulators;
+ const struct s2mpg10_regulator_desc *s2mpg1x_regulators = NULL;
s2mps11 = devm_kzalloc(&pdev->dev, sizeof(struct s2mps11_info),
GFP_KERNEL);
@@ -1218,58 +2181,64 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
s2mps11->dev_type = platform_get_device_id(pdev)->driver_data;
switch (s2mps11->dev_type) {
+ case S2MPG10:
+ rdev_num = ARRAY_SIZE(s2mpg10_regulators);
+ s2mpg1x_regulators = s2mpg10_regulators;
+ BUILD_BUG_ON(ARRAY_SIZE(s2mpg10_regulators) > S2MPS_REGULATOR_MAX);
+ break;
+ case S2MPG11:
+ rdev_num = ARRAY_SIZE(s2mpg11_regulators);
+ s2mpg1x_regulators = s2mpg11_regulators;
+ BUILD_BUG_ON(ARRAY_SIZE(s2mpg11_regulators) > S2MPS_REGULATOR_MAX);
+ break;
case S2MPS11X:
rdev_num = ARRAY_SIZE(s2mps11_regulators);
regulators = s2mps11_regulators;
- BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps11_regulators));
+ BUILD_BUG_ON(ARRAY_SIZE(s2mps11_regulators) > S2MPS_REGULATOR_MAX);
break;
case S2MPS13X:
rdev_num = ARRAY_SIZE(s2mps13_regulators);
regulators = s2mps13_regulators;
- BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps13_regulators));
+ BUILD_BUG_ON(ARRAY_SIZE(s2mps13_regulators) > S2MPS_REGULATOR_MAX);
break;
case S2MPS14X:
rdev_num = ARRAY_SIZE(s2mps14_regulators);
regulators = s2mps14_regulators;
- BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps14_regulators));
+ BUILD_BUG_ON(ARRAY_SIZE(s2mps14_regulators) > S2MPS_REGULATOR_MAX);
break;
case S2MPS15X:
rdev_num = ARRAY_SIZE(s2mps15_regulators);
regulators = s2mps15_regulators;
- BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mps15_regulators));
+ BUILD_BUG_ON(ARRAY_SIZE(s2mps15_regulators) > S2MPS_REGULATOR_MAX);
break;
case S2MPU02:
rdev_num = ARRAY_SIZE(s2mpu02_regulators);
regulators = s2mpu02_regulators;
- BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu02_regulators));
+ BUILD_BUG_ON(ARRAY_SIZE(s2mpu02_regulators) > S2MPS_REGULATOR_MAX);
break;
case S2MPU05:
rdev_num = ARRAY_SIZE(s2mpu05_regulators);
regulators = s2mpu05_regulators;
- BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu05_regulators));
+ BUILD_BUG_ON(ARRAY_SIZE(s2mpu05_regulators) > S2MPS_REGULATOR_MAX);
break;
default:
- dev_err(&pdev->dev, "Invalid device type: %u\n",
- s2mps11->dev_type);
- return -EINVAL;
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "Unsupported device type %d\n",
+ s2mps11->dev_type);
}
- s2mps11->ext_control_gpiod = devm_kcalloc(&pdev->dev, rdev_num,
- sizeof(*s2mps11->ext_control_gpiod), GFP_KERNEL);
- if (!s2mps11->ext_control_gpiod)
- return -ENOMEM;
+ if (s2mpg1x_regulators) {
+ size_t regulators_sz = rdev_num * sizeof(*s2mpg1x_regulators);
- struct of_regulator_match *rdata __free(kfree) =
- kcalloc(rdev_num, sizeof(*rdata), GFP_KERNEL);
- if (!rdata)
- return -ENOMEM;
+ s2mpg1x_regulators = devm_kmemdup(&pdev->dev,
+ s2mpg1x_regulators,
+ regulators_sz,
+ GFP_KERNEL);
+ if (!s2mpg1x_regulators)
+ return -ENOMEM;
+ }
- for (i = 0; i < rdev_num; i++)
- rdata[i].name = regulators[i].name;
-
- ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11, rdev_num);
- if (ret)
- return ret;
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
platform_set_drvdata(pdev, s2mps11);
@@ -1277,41 +2246,32 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
config.regmap = iodev->regmap_pmic;
config.driver_data = s2mps11;
for (i = 0; i < rdev_num; i++) {
+ const struct regulator_desc *rdesc;
struct regulator_dev *regulator;
- config.init_data = rdata[i].init_data;
- config.of_node = rdata[i].of_node;
- 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,
- &regulators[i], &config);
- if (IS_ERR(regulator)) {
- dev_err(&pdev->dev, "regulator init failed for %d\n",
- i);
- return PTR_ERR(regulator);
- }
+ if (s2mpg1x_regulators)
+ rdesc = &s2mpg1x_regulators[i].desc;
+ else
+ rdesc = &regulators[i];
- if (config.ena_gpiod) {
- ret = s2mps14_pmic_enable_ext_control(s2mps11,
- regulator);
- if (ret < 0) {
- dev_err(&pdev->dev,
- "failed to enable GPIO control over %s: %d\n",
- regulator->desc->name, ret);
- return ret;
- }
- }
+ regulator = devm_regulator_register(&pdev->dev,
+ rdesc, &config);
+ if (IS_ERR(regulator))
+ return dev_err_probe(&pdev->dev, PTR_ERR(regulator),
+ "regulator init failed for %d/%s\n",
+ rdesc->id, rdesc->name);
+
+ ret = s2mps11_handle_ext_control(s2mps11, regulator);
+ if (ret < 0)
+ return ret;
}
return 0;
}
static const struct platform_device_id s2mps11_pmic_id[] = {
+ { "s2mpg10-regulator", S2MPG10},
+ { "s2mpg11-regulator", S2MPG11},
{ "s2mps11-regulator", S2MPS11X},
{ "s2mps13-regulator", S2MPS13X},
{ "s2mps14-regulator", S2MPS14X},
diff --git a/drivers/regulator/tps65185.c b/drivers/regulator/tps65185.c
new file mode 100644
index 000000000000..3286c9ab33d0
--- /dev/null
+++ b/drivers/regulator/tps65185.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2025 Andreas Kemnade
+
+/* Datasheet: https://www.ti.com/lit/gpn/tps65185 */
+
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+
+#define TPS65185_REG_TMST_VALUE 0
+#define TPS65185_REG_ENABLE 1
+#define TPS65185_REG_VADJ 2
+#define TPS65185_REG_VCOM1 3
+#define TPS65185_REG_VCOM2 4
+#define TPS65185_REG_INT_EN1 5
+#define TPS65185_REG_INT_EN2 6
+#define TPS65185_REG_INT1 7
+#define TPS65185_REG_INT2 8
+#define TPS65185_REG_TMST1 0xd
+#define TPS65185_REG_TMST2 0xe
+#define TPS65185_REG_PG 0xf
+#define TPS65185_REG_REVID 0x10
+
+#define TPS65185_READ_THERM BIT(7)
+#define TPS65185_CONV_END BIT(5)
+
+#define TPS65185_ENABLE_ACTIVE BIT(7)
+#define TPS65185_ENABLE_STANDBY BIT(6)
+
+#define PGOOD_TIMEOUT_MSECS 200
+
+struct tps65185_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *pgood_gpio;
+ struct gpio_desc *pwrup_gpio;
+ struct gpio_desc *vcom_ctrl_gpio;
+ struct gpio_desc *wakeup_gpio;
+ struct completion pgood_completion;
+ int pgood_irq;
+ struct completion tmst_completion;
+};
+
+static const struct hwmon_channel_info *tps65185_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static int tps65185_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ struct tps65185_data *data = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ reinit_completion(&data->tmst_completion);
+ /* start acquisition */
+ regmap_update_bits(data->regmap, TPS65185_REG_TMST1,
+ TPS65185_READ_THERM, TPS65185_READ_THERM);
+ wait_for_completion_timeout(&data->tmst_completion,
+ msecs_to_jiffies(PGOOD_TIMEOUT_MSECS));
+ ret = regmap_read(data->regmap, TPS65185_REG_TMST1, &val);
+ if (!(val & TPS65185_CONV_END))
+ return -ETIMEDOUT;
+
+ ret = regmap_read(data->regmap, TPS65185_REG_TMST_VALUE, &val);
+ if (ret)
+ return ret;
+
+ *temp = (s8)val * 1000;
+
+ return 0;
+}
+
+static umode_t tps65185_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ return 0444;
+}
+
+static const struct hwmon_ops tps65185_hwmon_ops = {
+ .is_visible = tps65185_hwmon_is_visible,
+ .read = tps65185_hwmon_read,
+};
+
+static const struct hwmon_chip_info tps65185_chip_info = {
+ .ops = &tps65185_hwmon_ops,
+ .info = tps65185_info,
+};
+
+static bool tps65185_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TPS65185_REG_TMST_VALUE:
+ case TPS65185_REG_ENABLE:
+ case TPS65185_REG_VCOM2:
+ case TPS65185_REG_INT1:
+ case TPS65185_REG_INT2:
+ case TPS65185_REG_TMST1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x10,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = tps65185_volatile_reg,
+};
+
+static const struct regulator_ops tps65185_v3p3ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static int tps65185_check_powergood(struct regulator_dev *rdev)
+{
+ struct tps65185_data *data = rdev_get_drvdata(rdev);
+
+ return gpiod_get_value_cansleep(data->pgood_gpio);
+}
+
+static int tps65185_vposneg_get_voltage_sel(struct regulator_dev *rdev)
+{
+ int ret;
+
+ ret = regulator_get_voltage_sel_regmap(rdev);
+ if (ret < 0)
+ return ret;
+
+ /* highest value is lowest voltage */
+ return 6 - ret;
+}
+
+static int tps65185_vposneg_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
+{
+ return regulator_set_voltage_sel_regmap(rdev, 6 - selector);
+}
+
+static irqreturn_t pgood_handler(int irq, void *dev_id)
+{
+ struct tps65185_data *data = dev_id;
+
+ complete(&data->pgood_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int tps65185_vposneg_enable(struct regulator_dev *rdev)
+{
+ struct tps65185_data *data = rdev_get_drvdata(rdev);
+ int ret;
+
+ reinit_completion(&data->pgood_completion);
+ if (data->pwrup_gpio)
+ ret = gpiod_set_value_cansleep(data->pwrup_gpio, 1);
+ else
+ ret = regmap_update_bits(data->regmap, TPS65185_REG_ENABLE,
+ TPS65185_ENABLE_ACTIVE,
+ TPS65185_ENABLE_ACTIVE);
+
+ if (ret)
+ return ret;
+
+ dev_dbg(data->dev, "turning on...");
+ wait_for_completion_timeout(&data->pgood_completion,
+ msecs_to_jiffies(PGOOD_TIMEOUT_MSECS));
+ dev_dbg(data->dev, "turned on");
+ if (gpiod_get_value_cansleep(data->pgood_gpio) != 1)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int tps65185_vposneg_disable(struct regulator_dev *rdev)
+{
+ struct tps65185_data *data = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (data->pwrup_gpio)
+ ret = gpiod_set_value_cansleep(data->pwrup_gpio, 0);
+ else
+ ret = regmap_update_bits(data->regmap, TPS65185_REG_ENABLE,
+ TPS65185_ENABLE_STANDBY,
+ TPS65185_ENABLE_STANDBY);
+
+ return ret;
+}
+
+static int tps65185_vcom_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector)
+{
+ struct tps65185_data *data = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, TPS65185_REG_VCOM2, BIT(0), selector >> 8);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(data->regmap, TPS65185_REG_VCOM1, selector & 0xFF);
+}
+
+static int tps65185_vcom_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct tps65185_data *data = rdev_get_drvdata(rdev);
+ int ret;
+ unsigned int sel, sel2;
+
+ ret = regmap_read(data->regmap, TPS65185_REG_VCOM1, &sel);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(data->regmap, TPS65185_REG_VCOM2, &sel2);
+ if (ret < 0)
+ return ret;
+
+ if (sel2 & BIT(0))
+ sel |= 0x100;
+
+ return sel;
+}
+
+static const struct regulator_ops tps65185_vcom_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .set_voltage_sel = tps65185_vcom_set_voltage_sel,
+ .get_voltage_sel = tps65185_vcom_get_voltage_sel,
+};
+
+static const struct regulator_ops tps65185_vposneg_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .enable = tps65185_vposneg_enable,
+ .disable = tps65185_vposneg_disable,
+ .is_enabled = tps65185_check_powergood,
+ .set_voltage_sel = tps65185_vposneg_set_voltage_sel,
+ .get_voltage_sel = tps65185_vposneg_get_voltage_sel,
+};
+
+static const struct regulator_desc regulators[] = {
+ {
+ .name = "v3p3",
+ .of_match = of_match_ptr("v3p3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = 0,
+ .ops = &tps65185_v3p3ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .enable_reg = TPS65185_REG_ENABLE,
+ .enable_mask = BIT(5),
+ .n_voltages = 1,
+ .min_uV = 3300000,
+ },
+ {
+ .name = "vposneg",
+ .of_match = of_match_ptr("vposneg"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = 1,
+ .ops = &tps65185_vposneg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = 4,
+ .vsel_reg = TPS65185_REG_VADJ,
+ .vsel_mask = 0x7,
+ .min_uV = 14250000,
+ .uV_step = 250000,
+ }
+};
+
+static const struct regulator_desc vcom_regulator_desc = {
+ .name = "vcom",
+ .of_match = of_match_ptr("vcom"),
+ .regulators_node = of_match_ptr("regulators"),
+ .supply_name = "vposneg",
+ .id = 2,
+ .ops = &tps65185_vcom_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = 511,
+ .min_uV = 0,
+ .uV_step = 10000,
+};
+
+static irqreturn_t tps65185_irq_thread(int irq, void *dev_id)
+{
+ struct tps65185_data *data = dev_id;
+ unsigned int int_status_1, int_status_2;
+ int ret;
+
+ /* read both status to have irq cleared */
+ ret = regmap_read(data->regmap, TPS65185_REG_INT1, &int_status_1);
+ if (ret)
+ return IRQ_NONE;
+
+ ret = regmap_read(data->regmap, TPS65185_REG_INT2, &int_status_2);
+ if (ret)
+ return IRQ_NONE;
+
+ if (int_status_2 & BIT(0))
+ complete(&data->tmst_completion);
+
+ dev_dbg(data->dev, "irq status %02x %02x\n", int_status_1, int_status_2);
+
+ if (int_status_1 || int_status_2)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+static int tps65185_probe(struct i2c_client *client)
+{
+ struct tps65185_data *data;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int ret = 0;
+ int i;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ data->regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
+ "failed to allocate regmap!\n");
+
+ data->pgood_gpio = devm_gpiod_get(&client->dev, "pwr-good", GPIOD_IN);
+ if (IS_ERR(data->pgood_gpio))
+ return dev_err_probe(&client->dev,
+ PTR_ERR(data->pgood_gpio),
+ "failed to get power good gpio\n");
+
+ data->pgood_irq = gpiod_to_irq(data->pgood_gpio);
+ if (data->pgood_irq < 0)
+ return data->pgood_irq;
+
+ data->pwrup_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(data->pwrup_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(data->pwrup_gpio),
+ "failed to get pwrup gpio\n");
+
+ data->wakeup_gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_OUT_HIGH);
+ if (IS_ERR(data->wakeup_gpio))
+ return dev_err_probe(&client->dev,
+ PTR_ERR(data->wakeup_gpio),
+ "failed to get wakeup gpio\n");
+
+ data->vcom_ctrl_gpio = devm_gpiod_get_optional(&client->dev, "vcom-ctrl", GPIOD_OUT_LOW);
+ if (IS_ERR(data->vcom_ctrl_gpio))
+ return dev_err_probe(&client->dev,
+ PTR_ERR(data->vcom_ctrl_gpio),
+ "failed to get vcm ctrl gpio\n");
+
+ ret = devm_regulator_get_enable(&client->dev, "vin");
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get vin regulator\n");
+
+ data->dev = &client->dev;
+ i2c_set_clientdata(client, data);
+
+ init_completion(&data->pgood_completion);
+ init_completion(&data->tmst_completion);
+
+ ret = devm_request_threaded_irq(&client->dev, data->pgood_irq, NULL,
+ pgood_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "PGOOD", data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to request power good irq\n");
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, tps65185_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "tps65185", data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to request irq\n");
+ }
+
+ ret = regmap_update_bits(data->regmap, TPS65185_REG_INT_EN2, BIT(0), BIT(0));
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to enable temp irq\n");
+
+ config.driver_data = data;
+ config.dev = &client->dev;
+ config.regmap = data->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&client->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev))
+ return dev_err_probe(&client->dev, PTR_ERR(rdev),
+ "failed to register %s regulator\n",
+ regulators[i].name);
+ }
+
+ config.ena_gpiod = data->vcom_ctrl_gpio;
+ rdev = devm_regulator_register(&client->dev, &vcom_regulator_desc, &config);
+ if (IS_ERR(rdev))
+ return dev_err_probe(&client->dev, PTR_ERR(rdev),
+ "failed to register vcom regulator\n");
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon_dev;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, "tps65185", data,
+ &tps65185_chip_info, NULL);
+ if (IS_ERR(hwmon_dev))
+ dev_notice(&client->dev, "failed to register hwmon\n");
+ }
+
+ return 0;
+}
+
+static const struct of_device_id tps65185_dt_ids[] = {
+ {
+ .compatible = "ti,tps65185",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, tps65185_dt_ids);
+
+static struct i2c_driver tps65185_i2c_driver = {
+ .driver = {
+ .name = "tps65185",
+ .of_match_table = tps65185_dt_ids,
+ },
+ .probe = tps65185_probe,
+};
+
+module_i2c_driver(tps65185_i2c_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("TPS65185 regulator driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index a7220b4d0e8d..c6ed5a4ca8a0 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -15,7 +15,6 @@
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/mfd/samsung/core.h>
-#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/rtc.h>
#include <linux/mfd/samsung/s2mps14.h>
@@ -683,22 +682,18 @@ static int s5m_rtc_probe(struct platform_device *pdev)
case S2MPS15X:
regmap_cfg = &s2mps14_rtc_regmap_config;
info->regs = &s2mps15_rtc_regs;
- alarm_irq = S2MPS14_IRQ_RTCA0;
break;
case S2MPS14X:
regmap_cfg = &s2mps14_rtc_regmap_config;
info->regs = &s2mps14_rtc_regs;
- alarm_irq = S2MPS14_IRQ_RTCA0;
break;
case S2MPS13X:
regmap_cfg = &s2mps14_rtc_regmap_config;
info->regs = &s2mps13_rtc_regs;
- alarm_irq = S2MPS14_IRQ_RTCA0;
break;
case S5M8767X:
regmap_cfg = &s5m_rtc_regmap_config;
info->regs = &s5m_rtc_regs;
- alarm_irq = S5M8767_IRQ_RTCA1;
break;
default:
return dev_err_probe(&pdev->dev, -ENODEV,
@@ -719,7 +714,6 @@ static int s5m_rtc_probe(struct platform_device *pdev)
"Failed to allocate regmap\n");
} else if (device_type == S2MPG10) {
info->regs = &s2mpg10_rtc_regs;
- alarm_irq = S2MPG10_IRQ_RTCA0;
} else {
return dev_err_probe(&pdev->dev, -ENODEV,
"Unsupported device type %d\n",
@@ -730,13 +724,14 @@ static int s5m_rtc_probe(struct platform_device *pdev)
info->s5m87xx = s5m87xx;
info->device_type = device_type;
- if (s5m87xx->irq_data) {
- info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
- if (info->irq <= 0)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "Failed to get virtual IRQ %d\n",
- alarm_irq);
- }
+ alarm_irq = platform_get_irq_byname_optional(pdev, "alarm");
+ if (alarm_irq > 0)
+ info->irq = alarm_irq;
+ else if (alarm_irq == -ENXIO)
+ info->irq = 0;
+ else
+ return dev_err_probe(&pdev->dev, alarm_irq ? : -EINVAL,
+ "IRQ 'alarm' not found\n");
platform_set_drvdata(pdev, info);
diff --git a/include/dt-bindings/regulator/samsung,s2mpg10-regulator.h b/include/dt-bindings/regulator/samsung,s2mpg10-regulator.h
new file mode 100644
index 000000000000..d9c16bba4d85
--- /dev/null
+++ b/include/dt-bindings/regulator/samsung,s2mpg10-regulator.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright 2021 Google LLC
+ * Copyright 2025 Linaro Ltd.
+ *
+ * Device Tree binding constants for the Samsung S2MPG1x PMIC regulators
+ */
+
+#ifndef _DT_BINDINGS_REGULATOR_SAMSUNG_S2MPG10_H
+#define _DT_BINDINGS_REGULATOR_SAMSUNG_S2MPG10_H
+
+/*
+ * Several regulators may be controlled via external signals instead of via
+ * software. These constants describe the possible signals for such regulators
+ * and generally correspond to the respecitve on-chip pins.
+ *
+ * S2MPG10 regulators supporting these are:
+ * - buck1m .. buck7m buck10m
+ * - ldo3m .. ldo19m
+ *
+ * ldo20m supports external control, but using a different set of control
+ * signals.
+ *
+ * S2MPG11 regulators supporting these are:
+ * - buck1s .. buck3s buck5s buck8s buck9s bucka buckd
+ * - ldo1s ldo2s ldo8s ldo13s
+ */
+#define S2MPG10_EXTCTRL_PWREN 0 /* PWREN pin */
+#define S2MPG10_EXTCTRL_PWREN_MIF 1 /* PWREN_MIF pin */
+#define S2MPG10_EXTCTRL_AP_ACTIVE_N 2 /* ~AP_ACTIVE_N pin */
+#define S2MPG10_EXTCTRL_CPUCL1_EN 3 /* CPUCL1_EN pin */
+#define S2MPG10_EXTCTRL_CPUCL1_EN2 4 /* CPUCL1_EN & PWREN pins */
+#define S2MPG10_EXTCTRL_CPUCL2_EN 5 /* CPUCL2_EN pin */
+#define S2MPG10_EXTCTRL_CPUCL2_EN2 6 /* CPUCL2_E2 & PWREN pins */
+#define S2MPG10_EXTCTRL_TPU_EN 7 /* TPU_EN pin */
+#define S2MPG10_EXTCTRL_TPU_EN2 8 /* TPU_EN & ~AP_ACTIVE_N pins */
+#define S2MPG10_EXTCTRL_TCXO_ON 9 /* TCXO_ON pin */
+#define S2MPG10_EXTCTRL_TCXO_ON2 10 /* TCXO_ON & ~AP_ACTIVE_N pins */
+
+#define S2MPG10_EXTCTRL_LDO20M_EN2 11 /* VLDO20M_EN & LDO20M_SFR */
+#define S2MPG10_EXTCTRL_LDO20M_EN 12 /* VLDO20M_EN pin */
+
+#define S2MPG11_EXTCTRL_PWREN 0 /* PWREN pin */
+#define S2MPG11_EXTCTRL_PWREN_MIF 1 /* PWREN_MIF pin */
+#define S2MPG11_EXTCTRL_AP_ACTIVE_N 2 /* ~AP_ACTIVE_N pin */
+#define S2MPG11_EXTCTRL_G3D_EN 3 /* G3D_EN pin */
+#define S2MPG11_EXTCTRL_G3D_EN2 4 /* G3D_EN & ~AP_ACTIVE_N pins */
+#define S2MPG11_EXTCTRL_AOC_VDD 5 /* AOC_VDD pin */
+#define S2MPG11_EXTCTRL_AOC_RET 6 /* AOC_RET pin */
+#define S2MPG11_EXTCTRL_UFS_EN 7 /* UFS_EN pin */
+#define S2MPG11_EXTCTRL_LDO13S_EN 8 /* VLDO13S_EN pin */
+
+#endif /* _DT_BINDINGS_REGULATOR_SAMSUNG_S2MPG10_H */
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index d785e101fe79..4480c631110a 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -40,6 +40,7 @@ enum sec_device_type {
S2DOS05,
S2MPA01,
S2MPG10,
+ S2MPG11,
S2MPS11X,
S2MPS13X,
S2MPS14X,
@@ -69,7 +70,6 @@ struct sec_pmic_dev {
int device_type;
int irq;
- struct regmap_irq_chip_data *irq_data;
};
struct sec_platform_data {
diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h
index 8402a5f8e18a..6eab95de6fa8 100644
--- a/include/linux/mfd/samsung/irq.h
+++ b/include/linux/mfd/samsung/irq.h
@@ -166,6 +166,111 @@ enum s2mpg10_irq {
S2MPG10_IRQ_NR,
};
+enum s2mpg11_common_irq {
+ /* Top-level (common) block */
+ S2MPG11_COMMON_IRQ_PMIC,
+ S2MPG11_COMMON_IRQ_UNUSED,
+};
+
+enum s2mpg11_irq {
+ /* PMIC */
+ S2MPG11_IRQ_PWRONF,
+ S2MPG11_IRQ_PWRONR,
+ S2MPG11_IRQ_PIF_TIMEOUT_MIF,
+ S2MPG11_IRQ_PIF_TIMEOUTS,
+ S2MPG11_IRQ_WTSR,
+ S2MPG11_IRQ_SPD_ABNORMAL_STOP,
+ S2MPG11_IRQ_SPD_PARITY_ERR,
+#define S2MPG11_IRQ_PWRONF_MASK BIT(0)
+#define S2MPG11_IRQ_PWRONR_MASK BIT(1)
+#define S2MPG11_IRQ_PIF_TIMEOUT_MIF_MASK BIT(3)
+#define S2MPG11_IRQ_PIF_TIMEOUTS_MASK BIT(4)
+#define S2MPG11_IRQ_WTSR_MASK BIT(5)
+#define S2MPG11_IRQ_SPD_ABNORMAL_STOP_MASK BIT(6)
+#define S2MPG11_IRQ_SPD_PARITY_ERR_MASK BIT(7)
+
+ S2MPG11_IRQ_140C,
+ S2MPG11_IRQ_120C,
+ S2MPG11_IRQ_TSD,
+ S2MPG11_IRQ_WRST,
+ S2MPG11_IRQ_NTC_CYCLE_DONE,
+ S2MPG11_IRQ_PMETER_OVERF,
+#define S2MPG11_IRQ_INT140C_MASK BIT(0)
+#define S2MPG11_IRQ_INT120C_MASK BIT(1)
+#define S2MPG11_IRQ_TSD_MASK BIT(2)
+#define S2MPG11_IRQ_WRST_MASK BIT(5)
+#define S2MPG11_IRQ_NTC_CYCLE_DONE_MASK BIT(6)
+#define S2MPG11_IRQ_PMETER_OVERF_MASK BIT(7)
+
+ S2MPG11_IRQ_OCP_B1S,
+ S2MPG11_IRQ_OCP_B2S,
+ S2MPG11_IRQ_OCP_B3S,
+ S2MPG11_IRQ_OCP_B4S,
+ S2MPG11_IRQ_OCP_B5S,
+ S2MPG11_IRQ_OCP_B6S,
+ S2MPG11_IRQ_OCP_B7S,
+ S2MPG11_IRQ_OCP_B8S,
+#define S2MPG11_IRQ_OCP_B1S_MASK BIT(0)
+#define S2MPG11_IRQ_OCP_B2S_MASK BIT(1)
+#define S2MPG11_IRQ_OCP_B3S_MASK BIT(2)
+#define S2MPG11_IRQ_OCP_B4S_MASK BIT(3)
+#define S2MPG11_IRQ_OCP_B5S_MASK BIT(4)
+#define S2MPG11_IRQ_OCP_B6S_MASK BIT(5)
+#define S2MPG11_IRQ_OCP_B7S_MASK BIT(6)
+#define S2MPG11_IRQ_OCP_B8S_MASK BIT(7)
+
+ S2MPG11_IRQ_OCP_B9S,
+ S2MPG11_IRQ_OCP_B10S,
+ S2MPG11_IRQ_OCP_BDS,
+ S2MPG11_IRQ_OCP_BAS,
+ S2MPG11_IRQ_OCP_BBS,
+ S2MPG11_IRQ_WLWP_ACC,
+ S2MPG11_IRQ_SPD_SRP_PKT_RST,
+#define S2MPG11_IRQ_OCP_B9S_MASK BIT(0)
+#define S2MPG11_IRQ_OCP_B10S_MASK BIT(1)
+#define S2MPG11_IRQ_OCP_BDS_MASK BIT(2)
+#define S2MPG11_IRQ_OCP_BAS_MASK BIT(3)
+#define S2MPG11_IRQ_OCP_BBS_MASK BIT(4)
+#define S2MPG11_IRQ_WLWP_ACC_MASK BIT(5)
+#define S2MPG11_IRQ_SPD_SRP_PKT_RST_MASK BIT(7)
+
+ S2MPG11_IRQ_PWR_WARN_CH0,
+ S2MPG11_IRQ_PWR_WARN_CH1,
+ S2MPG11_IRQ_PWR_WARN_CH2,
+ S2MPG11_IRQ_PWR_WARN_CH3,
+ S2MPG11_IRQ_PWR_WARN_CH4,
+ S2MPG11_IRQ_PWR_WARN_CH5,
+ S2MPG11_IRQ_PWR_WARN_CH6,
+ S2MPG11_IRQ_PWR_WARN_CH7,
+#define S2MPG11_IRQ_PWR_WARN_CH0_MASK BIT(0)
+#define S2MPG11_IRQ_PWR_WARN_CH1_MASK BIT(1)
+#define S2MPG11_IRQ_PWR_WARN_CH2_MASK BIT(2)
+#define S2MPG11_IRQ_PWR_WARN_CH3_MASK BIT(3)
+#define S2MPG11_IRQ_PWR_WARN_CH4_MASK BIT(4)
+#define S2MPG11_IRQ_PWR_WARN_CH5_MASK BIT(5)
+#define S2MPG11_IRQ_PWR_WARN_CH6_MASK BIT(6)
+#define S2MPG11_IRQ_PWR_WARN_CH7_MASK BIT(7)
+
+ S2MPG11_IRQ_NTC_WARN_CH0,
+ S2MPG11_IRQ_NTC_WARN_CH1,
+ S2MPG11_IRQ_NTC_WARN_CH2,
+ S2MPG11_IRQ_NTC_WARN_CH3,
+ S2MPG11_IRQ_NTC_WARN_CH4,
+ S2MPG11_IRQ_NTC_WARN_CH5,
+ S2MPG11_IRQ_NTC_WARN_CH6,
+ S2MPG11_IRQ_NTC_WARN_CH7,
+#define S2MPG11_IRQ_NTC_WARN_CH0_MASK BIT(0)
+#define S2MPG11_IRQ_NTC_WARN_CH1_MASK BIT(1)
+#define S2MPG11_IRQ_NTC_WARN_CH2_MASK BIT(2)
+#define S2MPG11_IRQ_NTC_WARN_CH3_MASK BIT(3)
+#define S2MPG11_IRQ_NTC_WARN_CH4_MASK BIT(4)
+#define S2MPG11_IRQ_NTC_WARN_CH5_MASK BIT(5)
+#define S2MPG11_IRQ_NTC_WARN_CH6_MASK BIT(6)
+#define S2MPG11_IRQ_NTC_WARN_CH7_MASK BIT(7)
+
+ S2MPG11_IRQ_NR,
+};
+
enum s2mps11_irq {
S2MPS11_IRQ_PWRONF,
S2MPS11_IRQ_PWRONR,
diff --git a/include/linux/mfd/samsung/s2mpg10.h b/include/linux/mfd/samsung/s2mpg10.h
index 9f5919b89a3c..8e5cf21cbd5a 100644
--- a/include/linux/mfd/samsung/s2mpg10.h
+++ b/include/linux/mfd/samsung/s2mpg10.h
@@ -290,6 +290,30 @@ enum s2mpg10_pmic_reg {
S2MPG10_PMIC_LDO_SENSE4,
};
+/* Rail controlled externally, based on PCTRLSELx */
+#define S2MPG10_PMIC_CTRL_ENABLE_EXT BIT(0)
+
+/* For S2MPG10_PMIC_PCTRLSELx */
+#define S2MPG10_PCTRLSEL_PWREN 0x1 /* PWREN pin */
+#define S2MPG10_PCTRLSEL_PWREN_TRG 0x2 /* PWREN_TRG bit in MIMICKING_CTRL */
+#define S2MPG10_PCTRLSEL_PWREN_MIF 0x3 /* PWREN_MIF pin */
+#define S2MPG10_PCTRLSEL_PWREN_MIF_TRG 0x4 /* PWREN_MIF_TRG bit in MIMICKING_CTRL */
+#define S2MPG10_PCTRLSEL_AP_ACTIVE_N 0x5 /* ~AP_ACTIVE_N pin */
+#define S2MPG10_PCTRLSEL_AP_ACTIVE_N_TRG 0x6 /* ~AP_ACTIVE_N_TRG bit in MIMICKING_CTRL */
+#define S2MPG10_PCTRLSEL_CPUCL1_EN 0x7 /* CPUCL1_EN pin */
+#define S2MPG10_PCTRLSEL_CPUCL1_EN2 0x8 /* CPUCL1_EN & PWREN pins */
+#define S2MPG10_PCTRLSEL_CPUCL2_EN 0x9 /* CPUCL2_EN pin */
+#define S2MPG10_PCTRLSEL_CPUCL2_EN2 0xa /* CPUCL2_E2 & PWREN pins */
+#define S2MPG10_PCTRLSEL_TPU_EN 0xb /* TPU_EN pin */
+#define S2MPG10_PCTRLSEL_TPU_EN2 0xc /* TPU_EN & ~AP_ACTIVE_N pins */
+#define S2MPG10_PCTRLSEL_TCXO_ON 0xd /* TCXO_ON pin */
+#define S2MPG10_PCTRLSEL_TCXO_ON2 0xe /* TCXO_ON & ~AP_ACTIVE_N pins */
+
+/* For S2MPG10_PMIC_PCTRLSELx of LDO20M */
+#define S2MPG10_PCTRLSEL_LDO20M_EN2 0x1 /* VLDO20M_EN & LDO20M_SFR */
+#define S2MPG10_PCTRLSEL_LDO20M_EN 0x2 /* VLDO20M_EN pin */
+#define S2MPG10_PCTRLSEL_LDO20M_SFR 0x3 /* LDO20M_SFR bit in LDO_CTRL1 register */
+
/* Meter registers (type 0xa00) */
enum s2mpg10_meter_reg {
S2MPG10_METER_CTRL1,
@@ -407,6 +431,16 @@ enum s2mpg10_meter_reg {
/* S2MPG10 regulator IDs */
enum s2mpg10_regulators {
+ S2MPG10_BUCK1,
+ S2MPG10_BUCK2,
+ S2MPG10_BUCK3,
+ S2MPG10_BUCK4,
+ S2MPG10_BUCK5,
+ S2MPG10_BUCK6,
+ S2MPG10_BUCK7,
+ S2MPG10_BUCK8,
+ S2MPG10_BUCK9,
+ S2MPG10_BUCK10,
S2MPG10_LDO1,
S2MPG10_LDO2,
S2MPG10_LDO3,
@@ -438,16 +472,6 @@ enum s2mpg10_regulators {
S2MPG10_LDO29,
S2MPG10_LDO30,
S2MPG10_LDO31,
- S2MPG10_BUCK1,
- S2MPG10_BUCK2,
- S2MPG10_BUCK3,
- S2MPG10_BUCK4,
- S2MPG10_BUCK5,
- S2MPG10_BUCK6,
- S2MPG10_BUCK7,
- S2MPG10_BUCK8,
- S2MPG10_BUCK9,
- S2MPG10_BUCK10,
S2MPG10_REGULATOR_MAX,
};
diff --git a/include/linux/mfd/samsung/s2mpg11.h b/include/linux/mfd/samsung/s2mpg11.h
new file mode 100644
index 000000000000..66daa3bafa6e
--- /dev/null
+++ b/include/linux/mfd/samsung/s2mpg11.h
@@ -0,0 +1,434 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2015 Samsung Electronics
+ * Copyright 2020 Google Inc
+ * Copyright 2025 Linaro Ltd.
+ */
+
+#ifndef __LINUX_MFD_S2MPG11_H
+#define __LINUX_MFD_S2MPG11_H
+
+/* Common registers (type 0x000) */
+enum s2mpg11_common_reg {
+ S2MPG11_COMMON_CHIPID,
+ S2MPG11_COMMON_INT,
+ S2MPG11_COMMON_INT_MASK,
+ S2MPG11_COMMON_SPD_CTRL1 = 0x0a,
+ S2MPG11_COMMON_SPD_CTRL2,
+ S2MPG11_COMMON_SPD_CTRL3,
+ S2MPG11_COMMON_MON1SEL = 0x1a,
+ S2MPG11_COMMON_MON2SEL,
+ S2MPG11_COMMON_MONR,
+ S2MPG11_COMMON_DEBUG_CTRL1,
+ S2MPG11_COMMON_DEBUG_CTRL2,
+ S2MPG11_COMMON_DEBUG_CTRL3,
+ S2MPG11_COMMON_DEBUG_CTRL4,
+ S2MPG11_COMMON_DEBUG_CTRL5,
+ S2MPG11_COMMON_DEBUG_CTRL6,
+ S2MPG11_COMMON_TEST_MODE1,
+ S2MPG11_COMMON_SPD_DEBUG1,
+ S2MPG11_COMMON_SPD_DEBUG2,
+ S2MPG11_COMMON_SPD_DEBUG3,
+ S2MPG11_COMMON_SPD_DEBUG4,
+};
+
+/* For S2MPG11_COMMON_INT and S2MPG11_COMMON_INT_MASK */
+#define S2MPG11_COMMON_INT_SRC GENMASK(2, 0)
+#define S2MPG11_COMMON_INT_SRC_PMIC BIT(0)
+
+/* PMIC registers (type 0x100) */
+enum s2mpg11_pmic_reg {
+ S2MPG11_PMIC_INT1,
+ S2MPG11_PMIC_INT2,
+ S2MPG11_PMIC_INT3,
+ S2MPG11_PMIC_INT4,
+ S2MPG11_PMIC_INT5,
+ S2MPG11_PMIC_INT6,
+ S2MPG11_PMIC_INT1M,
+ S2MPG11_PMIC_INT2M,
+ S2MPG11_PMIC_INT3M,
+ S2MPG11_PMIC_INT4M,
+ S2MPG11_PMIC_INT5M,
+ S2MPG11_PMIC_INT6M,
+ S2MPG11_PMIC_STATUS1,
+ S2MPG11_PMIC_OFFSRC,
+ S2MPG11_PMIC_COMMON_CTRL1,
+ S2MPG11_PMIC_COMMON_CTRL2,
+ S2MPG11_PMIC_COMMON_CTRL3,
+ S2MPG11_PMIC_MIMICKING_CTRL,
+ S2MPG11_PMIC_B1S_CTRL,
+ S2MPG11_PMIC_B1S_OUT1,
+ S2MPG11_PMIC_B1S_OUT2,
+ S2MPG11_PMIC_B2S_CTRL,
+ S2MPG11_PMIC_B2S_OUT1,
+ S2MPG11_PMIC_B2S_OUT2,
+ S2MPG11_PMIC_B3S_CTRL,
+ S2MPG11_PMIC_B3S_OUT1,
+ S2MPG11_PMIC_B3S_OUT2,
+ S2MPG11_PMIC_B4S_CTRL,
+ S2MPG11_PMIC_B4S_OUT,
+ S2MPG11_PMIC_B5S_CTRL,
+ S2MPG11_PMIC_B5S_OUT,
+ S2MPG11_PMIC_B6S_CTRL,
+ S2MPG11_PMIC_B6S_OUT1,
+ S2MPG11_PMIC_B6S_OUT2,
+ S2MPG11_PMIC_B7S_CTRL,
+ S2MPG11_PMIC_B7S_OUT1,
+ S2MPG11_PMIC_B7S_OUT2,
+ S2MPG11_PMIC_B8S_CTRL,
+ S2MPG11_PMIC_B8S_OUT1,
+ S2MPG11_PMIC_B8S_OUT2,
+ S2MPG11_PMIC_B9S_CTRL,
+ S2MPG11_PMIC_B9S_OUT1,
+ S2MPG11_PMIC_B9S_OUT2,
+ S2MPG11_PMIC_B10S_CTRL,
+ S2MPG11_PMIC_B10S_OUT,
+ S2MPG11_PMIC_BUCKD_CTRL,
+ S2MPG11_PMIC_BUCKD_OUT,
+ S2MPG11_PMIC_BUCKA_CTRL,
+ S2MPG11_PMIC_BUCKA_OUT,
+ S2MPG11_PMIC_BB_CTRL,
+ S2MPG11_PMIC_BB_OUT1,
+ S2MPG11_PMIC_BB_OUT2,
+ S2MPG11_PMIC_BUCK1S_USONIC,
+ S2MPG11_PMIC_BUCK2S_USONIC,
+ S2MPG11_PMIC_BUCK3S_USONIC,
+ S2MPG11_PMIC_BUCK4S_USONIC,
+ S2MPG11_PMIC_BUCK5S_USONIC,
+ S2MPG11_PMIC_BUCK6S_USONIC,
+ S2MPG11_PMIC_BUCK7S_USONIC,
+ S2MPG11_PMIC_BUCK8S_USONIC,
+ S2MPG11_PMIC_BUCK9S_USONIC,
+ S2MPG11_PMIC_BUCK10S_USONIC,
+ S2MPG11_PMIC_BUCKD_USONIC,
+ S2MPG11_PMIC_BUCKA_USONIC,
+ S2MPG11_PMIC_BB_USONIC,
+ S2MPG11_PMIC_L1S_CTRL1,
+ S2MPG11_PMIC_L1S_CTRL2,
+ S2MPG11_PMIC_L2S_CTRL1,
+ S2MPG11_PMIC_L2S_CTRL2,
+ S2MPG11_PMIC_L3S_CTRL,
+ S2MPG11_PMIC_L4S_CTRL,
+ S2MPG11_PMIC_L5S_CTRL,
+ S2MPG11_PMIC_L6S_CTRL,
+ S2MPG11_PMIC_L7S_CTRL,
+ S2MPG11_PMIC_L8S_CTRL,
+ S2MPG11_PMIC_L9S_CTRL,
+ S2MPG11_PMIC_L10S_CTRL,
+ S2MPG11_PMIC_L11S_CTRL,
+ S2MPG11_PMIC_L12S_CTRL,
+ S2MPG11_PMIC_L13S_CTRL,
+ S2MPG11_PMIC_L14S_CTRL,
+ S2MPG11_PMIC_L15S_CTRL,
+ S2MPG11_PMIC_LDO_CTRL1,
+ S2MPG11_PMIC_LDO_DSCH1,
+ S2MPG11_PMIC_LDO_DSCH2,
+ S2MPG11_PMIC_DVS_RAMP1,
+ S2MPG11_PMIC_DVS_RAMP2,
+ S2MPG11_PMIC_DVS_RAMP3,
+ S2MPG11_PMIC_DVS_RAMP4,
+ S2MPG11_PMIC_DVS_RAMP5,
+ S2MPG11_PMIC_DVS_RAMP6,
+ /* Nothing @ 0x5a */
+ S2MPG11_PMIC_DVS_SYNC_CTRL1 = 0x5c,
+ S2MPG11_PMIC_DVS_SYNC_CTRL2,
+ S2MPG11_PMIC_OFF_CTRL1,
+ S2MPG11_PMIC_OFF_CTRL2,
+ S2MPG11_PMIC_OFF_CTRL3,
+ S2MPG11_PMIC_SEQ_CTRL1,
+ S2MPG11_PMIC_SEQ_CTRL2,
+ S2MPG11_PMIC_SEQ_CTRL3,
+ S2MPG11_PMIC_SEQ_CTRL4,
+ S2MPG11_PMIC_SEQ_CTRL5,
+ S2MPG11_PMIC_SEQ_CTRL6,
+ S2MPG11_PMIC_SEQ_CTRL7,
+ S2MPG11_PMIC_SEQ_CTRL8,
+ S2MPG11_PMIC_SEQ_CTRL9,
+ S2MPG11_PMIC_SEQ_CTRL10,
+ S2MPG11_PMIC_SEQ_CTRL11,
+ S2MPG11_PMIC_SEQ_CTRL12,
+ S2MPG11_PMIC_SEQ_CTRL13,
+ S2MPG11_PMIC_SEQ_CTRL14,
+ S2MPG11_PMIC_SEQ_CTRL15,
+ S2MPG11_PMIC_SEQ_CTRL16,
+ S2MPG11_PMIC_SEQ_CTRL17,
+ S2MPG11_PMIC_SEQ_CTRL18,
+ S2MPG11_PMIC_SEQ_CTRL19,
+ S2MPG11_PMIC_SEQ_CTRL20,
+ S2MPG11_PMIC_SEQ_CTRL21,
+ S2MPG11_PMIC_SEQ_CTRL22,
+ S2MPG11_PMIC_SEQ_CTRL23,
+ S2MPG11_PMIC_SEQ_CTRL24,
+ S2MPG11_PMIC_SEQ_CTRL25,
+ S2MPG11_PMIC_SEQ_CTRL26,
+ S2MPG11_PMIC_SEQ_CTRL27,
+ S2MPG11_PMIC_OFF_SEQ_CTRL1,
+ S2MPG11_PMIC_OFF_SEQ_CTRL2,
+ S2MPG11_PMIC_OFF_SEQ_CTRL3,
+ S2MPG11_PMIC_OFF_SEQ_CTRL4,
+ S2MPG11_PMIC_OFF_SEQ_CTRL5,
+ S2MPG11_PMIC_OFF_SEQ_CTRL6,
+ S2MPG11_PMIC_OFF_SEQ_CTRL7,
+ S2MPG11_PMIC_OFF_SEQ_CTRL8,
+ S2MPG11_PMIC_OFF_SEQ_CTRL9,
+ S2MPG11_PMIC_OFF_SEQ_CTRL10,
+ S2MPG11_PMIC_OFF_SEQ_CTRL11,
+ S2MPG11_PMIC_OFF_SEQ_CTRL12,
+ S2MPG11_PMIC_OFF_SEQ_CTRL13,
+ S2MPG11_PMIC_OFF_SEQ_CTRL14,
+ S2MPG11_PMIC_OFF_SEQ_CTRL15,
+ S2MPG11_PMIC_OFF_SEQ_CTRL16,
+ S2MPG11_PMIC_OFF_SEQ_CTRL17,
+ S2MPG11_PMIC_PCTRLSEL1,
+ S2MPG11_PMIC_PCTRLSEL2,
+ S2MPG11_PMIC_PCTRLSEL3,
+ S2MPG11_PMIC_PCTRLSEL4,
+ S2MPG11_PMIC_PCTRLSEL5,
+ S2MPG11_PMIC_PCTRLSEL6,
+ S2MPG11_PMIC_DCTRLSEL1,
+ S2MPG11_PMIC_DCTRLSEL2,
+ S2MPG11_PMIC_DCTRLSEL3,
+ S2MPG11_PMIC_DCTRLSEL4,
+ S2MPG11_PMIC_DCTRLSEL5,
+ S2MPG11_PMIC_GPIO_CTRL1,
+ S2MPG11_PMIC_GPIO_CTRL2,
+ S2MPG11_PMIC_GPIO_CTRL3,
+ S2MPG11_PMIC_GPIO_CTRL4,
+ S2MPG11_PMIC_GPIO_CTRL5,
+ S2MPG11_PMIC_GPIO_CTRL6,
+ S2MPG11_PMIC_GPIO_CTRL7,
+ S2MPG11_PMIC_B2S_OCP_WARN,
+ S2MPG11_PMIC_B2S_OCP_WARN_X,
+ S2MPG11_PMIC_B2S_OCP_WARN_Y,
+ S2MPG11_PMIC_B2S_OCP_WARN_Z,
+ S2MPG11_PMIC_B2S_SOFT_OCP_WARN,
+ S2MPG11_PMIC_B2S_SOFT_OCP_WARN_X,
+ S2MPG11_PMIC_B2S_SOFT_OCP_WARN_Y,
+ S2MPG11_PMIC_B2S_SOFT_OCP_WARN_Z,
+ S2MPG11_PMIC_BUCK_OCP_EN1,
+ S2MPG11_PMIC_BUCK_OCP_EN2,
+ S2MPG11_PMIC_BUCK_OCP_PD_EN1,
+ S2MPG11_PMIC_BUCK_OCP_PD_EN2,
+ S2MPG11_PMIC_BUCK_OCP_CTRL1,
+ S2MPG11_PMIC_BUCK_OCP_CTRL2,
+ S2MPG11_PMIC_BUCK_OCP_CTRL3,
+ S2MPG11_PMIC_BUCK_OCP_CTRL4,
+ S2MPG11_PMIC_BUCK_OCP_CTRL5,
+ S2MPG11_PMIC_BUCK_OCP_CTRL6,
+ S2MPG11_PMIC_BUCK_OCP_CTRL7,
+ S2MPG11_PMIC_PIF_CTRL,
+ S2MPG11_PMIC_BUCK_HR_MODE1,
+ S2MPG11_PMIC_BUCK_HR_MODE2,
+ S2MPG11_PMIC_FAULTOUT_CTRL,
+ S2MPG11_PMIC_LDO_SENSE1,
+ S2MPG11_PMIC_LDO_SENSE2,
+};
+
+/* For S2MPG11_PMIC_PCTRLSELx */
+#define S2MPG11_PCTRLSEL_PWREN 0x1 /* PWREN pin */
+#define S2MPG11_PCTRLSEL_PWREN_TRG 0x2 /* PWREN_TRG bit in MIMICKING_CTRL */
+#define S2MPG11_PCTRLSEL_PWREN_MIF 0x3 /* PWREN_MIF pin */
+#define S2MPG11_PCTRLSEL_PWREN_MIF_TRG 0x4 /* PWREN_MIF_TRG bit in MIMICKING_CTRL */
+#define S2MPG11_PCTRLSEL_AP_ACTIVE_N 0x5 /* ~AP_ACTIVE_N pin */
+#define S2MPG11_PCTRLSEL_AP_ACTIVE_N_TRG 0x6 /* ~AP_ACTIVE_N_TRG bit in MIMICKING_CTRL */
+#define S2MPG11_PCTRLSEL_G3D_EN 0x7 /* G3D_EN pin */
+#define S2MPG11_PCTRLSEL_G3D_EN2 0x8 /* G3D_EN & ~AP_ACTIVE_N pins */
+#define S2MPG11_PCTRLSEL_AOC_VDD 0x9 /* AOC_VDD pin */
+#define S2MPG11_PCTRLSEL_AOC_RET 0xa /* AOC_RET pin */
+#define S2MPG11_PCTRLSEL_UFS_EN 0xb /* UFS_EN pin */
+#define S2MPG11_PCTRLSEL_LDO13S_EN 0xc /* VLDO13S_EN pin */
+
+/* Meter registers (type 0xa00) */
+enum s2mpg11_meter_reg {
+ S2MPG11_METER_CTRL1,
+ S2MPG11_METER_CTRL2,
+ S2MPG11_METER_CTRL3,
+ S2MPG11_METER_CTRL4,
+ S2MPG11_METER_CTRL5,
+ S2MPG11_METER_BUCKEN1,
+ S2MPG11_METER_BUCKEN2,
+ S2MPG11_METER_MUXSEL0,
+ S2MPG11_METER_MUXSEL1,
+ S2MPG11_METER_MUXSEL2,
+ S2MPG11_METER_MUXSEL3,
+ S2MPG11_METER_MUXSEL4,
+ S2MPG11_METER_MUXSEL5,
+ S2MPG11_METER_MUXSEL6,
+ S2MPG11_METER_MUXSEL7,
+ S2MPG11_METER_LPF_C0_0,
+ S2MPG11_METER_LPF_C0_1,
+ S2MPG11_METER_LPF_C0_2,
+ S2MPG11_METER_LPF_C0_3,
+ S2MPG11_METER_LPF_C0_4,
+ S2MPG11_METER_LPF_C0_5,
+ S2MPG11_METER_LPF_C0_6,
+ S2MPG11_METER_LPF_C0_7,
+ S2MPG11_METER_NTC_LPF_C0_0,
+ S2MPG11_METER_NTC_LPF_C0_1,
+ S2MPG11_METER_NTC_LPF_C0_2,
+ S2MPG11_METER_NTC_LPF_C0_3,
+ S2MPG11_METER_NTC_LPF_C0_4,
+ S2MPG11_METER_NTC_LPF_C0_5,
+ S2MPG11_METER_NTC_LPF_C0_6,
+ S2MPG11_METER_NTC_LPF_C0_7,
+ S2MPG11_METER_PWR_WARN0,
+ S2MPG11_METER_PWR_WARN1,
+ S2MPG11_METER_PWR_WARN2,
+ S2MPG11_METER_PWR_WARN3,
+ S2MPG11_METER_PWR_WARN4,
+ S2MPG11_METER_PWR_WARN5,
+ S2MPG11_METER_PWR_WARN6,
+ S2MPG11_METER_PWR_WARN7,
+ S2MPG11_METER_NTC_L_WARN0,
+ S2MPG11_METER_NTC_L_WARN1,
+ S2MPG11_METER_NTC_L_WARN2,
+ S2MPG11_METER_NTC_L_WARN3,
+ S2MPG11_METER_NTC_L_WARN4,
+ S2MPG11_METER_NTC_L_WARN5,
+ S2MPG11_METER_NTC_L_WARN6,
+ S2MPG11_METER_NTC_L_WARN7,
+ S2MPG11_METER_NTC_H_WARN0,
+ S2MPG11_METER_NTC_H_WARN1,
+ S2MPG11_METER_NTC_H_WARN2,
+ S2MPG11_METER_NTC_H_WARN3,
+ S2MPG11_METER_NTC_H_WARN4,
+ S2MPG11_METER_NTC_H_WARN5,
+ S2MPG11_METER_NTC_H_WARN6,
+ S2MPG11_METER_NTC_H_WARN7,
+ S2MPG11_METER_PWR_HYS1,
+ S2MPG11_METER_PWR_HYS2,
+ S2MPG11_METER_PWR_HYS3,
+ S2MPG11_METER_PWR_HYS4,
+ S2MPG11_METER_NTC_HYS1,
+ S2MPG11_METER_NTC_HYS2,
+ S2MPG11_METER_NTC_HYS3,
+ S2MPG11_METER_NTC_HYS4,
+ /* Nothing @ 0x3f */
+ S2MPG11_METER_ACC_DATA_CH0_1 = 0x40,
+ S2MPG11_METER_ACC_DATA_CH0_2,
+ S2MPG11_METER_ACC_DATA_CH0_3,
+ S2MPG11_METER_ACC_DATA_CH0_4,
+ S2MPG11_METER_ACC_DATA_CH0_5,
+ S2MPG11_METER_ACC_DATA_CH0_6,
+ S2MPG11_METER_ACC_DATA_CH1_1,
+ S2MPG11_METER_ACC_DATA_CH1_2,
+ S2MPG11_METER_ACC_DATA_CH1_3,
+ S2MPG11_METER_ACC_DATA_CH1_4,
+ S2MPG11_METER_ACC_DATA_CH1_5,
+ S2MPG11_METER_ACC_DATA_CH1_6,
+ S2MPG11_METER_ACC_DATA_CH2_1,
+ S2MPG11_METER_ACC_DATA_CH2_2,
+ S2MPG11_METER_ACC_DATA_CH2_3,
+ S2MPG11_METER_ACC_DATA_CH2_4,
+ S2MPG11_METER_ACC_DATA_CH2_5,
+ S2MPG11_METER_ACC_DATA_CH2_6,
+ S2MPG11_METER_ACC_DATA_CH3_1,
+ S2MPG11_METER_ACC_DATA_CH3_2,
+ S2MPG11_METER_ACC_DATA_CH3_3,
+ S2MPG11_METER_ACC_DATA_CH3_4,
+ S2MPG11_METER_ACC_DATA_CH3_5,
+ S2MPG11_METER_ACC_DATA_CH3_6,
+ S2MPG11_METER_ACC_DATA_CH4_1,
+ S2MPG11_METER_ACC_DATA_CH4_2,
+ S2MPG11_METER_ACC_DATA_CH4_3,
+ S2MPG11_METER_ACC_DATA_CH4_4,
+ S2MPG11_METER_ACC_DATA_CH4_5,
+ S2MPG11_METER_ACC_DATA_CH4_6,
+ S2MPG11_METER_ACC_DATA_CH5_1,
+ S2MPG11_METER_ACC_DATA_CH5_2,
+ S2MPG11_METER_ACC_DATA_CH5_3,
+ S2MPG11_METER_ACC_DATA_CH5_4,
+ S2MPG11_METER_ACC_DATA_CH5_5,
+ S2MPG11_METER_ACC_DATA_CH5_6,
+ S2MPG11_METER_ACC_DATA_CH6_1,
+ S2MPG11_METER_ACC_DATA_CH6_2,
+ S2MPG11_METER_ACC_DATA_CH6_3,
+ S2MPG11_METER_ACC_DATA_CH6_4,
+ S2MPG11_METER_ACC_DATA_CH6_5,
+ S2MPG11_METER_ACC_DATA_CH6_6,
+ S2MPG11_METER_ACC_DATA_CH7_1,
+ S2MPG11_METER_ACC_DATA_CH7_2,
+ S2MPG11_METER_ACC_DATA_CH7_3,
+ S2MPG11_METER_ACC_DATA_CH7_4,
+ S2MPG11_METER_ACC_DATA_CH7_5,
+ S2MPG11_METER_ACC_DATA_CH7_6,
+ S2MPG11_METER_ACC_COUNT_1,
+ S2MPG11_METER_ACC_COUNT_2,
+ S2MPG11_METER_ACC_COUNT_3,
+ S2MPG11_METER_LPF_DATA_CH0_1,
+ S2MPG11_METER_LPF_DATA_CH0_2,
+ S2MPG11_METER_LPF_DATA_CH0_3,
+ S2MPG11_METER_LPF_DATA_CH1_1,
+ S2MPG11_METER_LPF_DATA_CH1_2,
+ S2MPG11_METER_LPF_DATA_CH1_3,
+ S2MPG11_METER_LPF_DATA_CH2_1,
+ S2MPG11_METER_LPF_DATA_CH2_2,
+ S2MPG11_METER_LPF_DATA_CH2_3,
+ S2MPG11_METER_LPF_DATA_CH3_1,
+ S2MPG11_METER_LPF_DATA_CH3_2,
+ S2MPG11_METER_LPF_DATA_CH3_3,
+ S2MPG11_METER_LPF_DATA_CH4_1,
+ S2MPG11_METER_LPF_DATA_CH4_2,
+ S2MPG11_METER_LPF_DATA_CH4_3,
+ S2MPG11_METER_LPF_DATA_CH5_1,
+ S2MPG11_METER_LPF_DATA_CH5_2,
+ S2MPG11_METER_LPF_DATA_CH5_3,
+ S2MPG11_METER_LPF_DATA_CH6_1,
+ S2MPG11_METER_LPF_DATA_CH6_2,
+ S2MPG11_METER_LPF_DATA_CH6_3,
+ S2MPG11_METER_LPF_DATA_CH7_1,
+ S2MPG11_METER_LPF_DATA_CH7_2,
+ S2MPG11_METER_LPF_DATA_CH7_3,
+ /* Nothing @ 0x8b 0x8c */
+ S2MPG11_METER_LPF_DATA_NTC0_1 = 0x8d,
+ S2MPG11_METER_LPF_DATA_NTC0_2,
+ S2MPG11_METER_LPF_DATA_NTC1_1,
+ S2MPG11_METER_LPF_DATA_NTC1_2,
+ S2MPG11_METER_LPF_DATA_NTC2_1,
+ S2MPG11_METER_LPF_DATA_NTC2_2,
+ S2MPG11_METER_LPF_DATA_NTC3_1,
+ S2MPG11_METER_LPF_DATA_NTC3_2,
+ S2MPG11_METER_LPF_DATA_NTC4_1,
+ S2MPG11_METER_LPF_DATA_NTC4_2,
+ S2MPG11_METER_LPF_DATA_NTC5_1,
+ S2MPG11_METER_LPF_DATA_NTC5_2,
+ S2MPG11_METER_LPF_DATA_NTC6_1,
+ S2MPG11_METER_LPF_DATA_NTC6_2,
+ S2MPG11_METER_LPF_DATA_NTC7_1,
+ S2MPG11_METER_LPF_DATA_NTC7_2,
+};
+
+/* S2MPG11 regulator IDs */
+enum s2mpg11_regulators {
+ S2MPG11_BUCKBOOST,
+ S2MPG11_BUCK1,
+ S2MPG11_BUCK2,
+ S2MPG11_BUCK3,
+ S2MPG11_BUCK4,
+ S2MPG11_BUCK5,
+ S2MPG11_BUCK6,
+ S2MPG11_BUCK7,
+ S2MPG11_BUCK8,
+ S2MPG11_BUCK9,
+ S2MPG11_BUCK10,
+ S2MPG11_BUCKD,
+ S2MPG11_BUCKA,
+ S2MPG11_LDO1,
+ S2MPG11_LDO2,
+ S2MPG11_LDO3,
+ S2MPG11_LDO4,
+ S2MPG11_LDO5,
+ S2MPG11_LDO6,
+ S2MPG11_LDO7,
+ S2MPG11_LDO8,
+ S2MPG11_LDO9,
+ S2MPG11_LDO10,
+ S2MPG11_LDO11,
+ S2MPG11_LDO12,
+ S2MPG11_LDO13,
+ S2MPG11_LDO14,
+ S2MPG11_LDO15,
+ S2MPG11_REGULATOR_MAX,
+};
+
+#endif /* __LINUX_MFD_S2MPG11_H */
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 978cf593b662..cc6ce709ec86 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -53,6 +53,11 @@ enum regulator_detection_severity {
#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV) \
LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV)
+/* Initialize struct linear_range using voltages, not selectors */
+#define REGULATOR_LINEAR_VRANGE(_offs_uV, _min_uV, _max_uV, _step_uV) \
+ LINEAR_RANGE(_min_uV, ((_min_uV) - (_offs_uV)) / (_step_uV), \
+ ((_max_uV) - (_offs_uV)) / (_step_uV), _step_uV)
+
/**
* struct regulator_ops - regulator operations.
*
@@ -635,6 +640,7 @@ struct regulator_dev {
int ref_cnt;
struct module *owner;
struct device dev;
+ struct device bdev;
struct regulation_constraints *constraints;
struct regulator *supply; /* for tree */
const char *supply_name;
@@ -649,6 +655,7 @@ struct regulator_dev {
struct regulator_enable_gpio *ena_pin;
unsigned int ena_gpio_state:1;
+ unsigned int constraints_pending:1;
unsigned int is_switch:1;
/* time when this regulator was disabled last time */
diff --git a/rust/helpers/regulator.c b/rust/helpers/regulator.c
index 11bc332443bd..9ec5237f449b 100644
--- a/rust/helpers/regulator.c
+++ b/rust/helpers/regulator.c
@@ -4,48 +4,52 @@
#ifndef CONFIG_REGULATOR
-void rust_helper_regulator_put(struct regulator *regulator)
+__rust_helper void rust_helper_regulator_put(struct regulator *regulator)
{
regulator_put(regulator);
}
-int rust_helper_regulator_set_voltage(struct regulator *regulator, int min_uV,
- int max_uV)
+__rust_helper int rust_helper_regulator_set_voltage(struct regulator *regulator,
+ int min_uV, int max_uV)
{
return regulator_set_voltage(regulator, min_uV, max_uV);
}
-int rust_helper_regulator_get_voltage(struct regulator *regulator)
+__rust_helper int rust_helper_regulator_get_voltage(struct regulator *regulator)
{
return regulator_get_voltage(regulator);
}
-struct regulator *rust_helper_regulator_get(struct device *dev, const char *id)
+__rust_helper struct regulator *rust_helper_regulator_get(struct device *dev,
+ const char *id)
{
return regulator_get(dev, id);
}
-int rust_helper_regulator_enable(struct regulator *regulator)
+__rust_helper int rust_helper_regulator_enable(struct regulator *regulator)
{
return regulator_enable(regulator);
}
-int rust_helper_regulator_disable(struct regulator *regulator)
+__rust_helper int rust_helper_regulator_disable(struct regulator *regulator)
{
return regulator_disable(regulator);
}
-int rust_helper_regulator_is_enabled(struct regulator *regulator)
+__rust_helper int rust_helper_regulator_is_enabled(struct regulator *regulator)
{
return regulator_is_enabled(regulator);
}
-int rust_helper_devm_regulator_get_enable(struct device *dev, const char *id)
+__rust_helper int rust_helper_devm_regulator_get_enable(struct device *dev,
+ const char *id)
{
return devm_regulator_get_enable(dev, id);
}
-int rust_helper_devm_regulator_get_enable_optional(struct device *dev, const char *id)
+__rust_helper int
+rust_helper_devm_regulator_get_enable_optional(struct device *dev,
+ const char *id)
{
return devm_regulator_get_enable_optional(dev, id);
}
diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs
index 2c44827ad0b7..4f7837c7e53a 100644
--- a/rust/kernel/regulator.rs
+++ b/rust/kernel/regulator.rs
@@ -122,12 +122,11 @@ pub fn devm_enable_optional(dev: &Device<Bound>, name: &CStr) -> Result {
///
/// ```
/// # use kernel::prelude::*;
-/// # use kernel::c_str;
/// # use kernel::device::Device;
/// # use kernel::regulator::{Voltage, Regulator, Disabled, Enabled};
/// fn enable(dev: &Device, min_voltage: Voltage, max_voltage: Voltage) -> Result {
/// // Obtain a reference to a (fictitious) regulator.
-/// let regulator: Regulator<Disabled> = Regulator::<Disabled>::get(dev, c_str!("vcc"))?;
+/// let regulator: Regulator<Disabled> = Regulator::<Disabled>::get(dev, c"vcc")?;
///
/// // The voltage can be set before enabling the regulator if needed, e.g.:
/// regulator.set_voltage(min_voltage, max_voltage)?;
@@ -166,12 +165,11 @@ pub fn devm_enable_optional(dev: &Device<Bound>, name: &CStr) -> Result {
///
/// ```
/// # use kernel::prelude::*;
-/// # use kernel::c_str;
/// # use kernel::device::Device;
/// # use kernel::regulator::{Voltage, Regulator, Enabled};
/// fn enable(dev: &Device) -> Result {
/// // Obtain a reference to a (fictitious) regulator and enable it.
-/// let regulator: Regulator<Enabled> = Regulator::<Enabled>::get(dev, c_str!("vcc"))?;
+/// let regulator: Regulator<Enabled> = Regulator::<Enabled>::get(dev, c"vcc")?;
///
/// // Dropping an enabled regulator will disable it. The refcount will be
/// // decremented.
@@ -193,13 +191,12 @@ pub fn devm_enable_optional(dev: &Device<Bound>, name: &CStr) -> Result {
///
/// ```
/// # use kernel::prelude::*;
-/// # use kernel::c_str;
/// # use kernel::device::{Bound, Device};
/// # use kernel::regulator;
/// fn enable(dev: &Device<Bound>) -> Result {
/// // Obtain a reference to a (fictitious) regulator and enable it. This
/// // call only returns whether the operation succeeded.
-/// regulator::devm_enable(dev, c_str!("vcc"))?;
+/// regulator::devm_enable(dev, c"vcc")?;
///
/// // The regulator will be disabled and put when `dev` is unbound.
/// Ok(())