summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/arm,scmi.txt34
-rw-r--r--Documentation/devicetree/bindings/regulator/dlg,da9121.yaml189
-rw-r--r--Documentation/devicetree/bindings/regulator/fixed-regulator.yaml47
-rw-r--r--Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt2
-rw-r--r--Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml211
-rw-r--r--Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt6
-rw-r--r--Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml48
-rw-r--r--Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml49
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/firmware/arm_scmi/Makefile2
-rw-r--r--drivers/firmware/arm_scmi/common.h1
-rw-r--r--drivers/firmware/arm_scmi/driver.c3
-rw-r--r--drivers/firmware/arm_scmi/voltage.c380
-rw-r--r--drivers/regulator/Kconfig41
-rw-r--r--drivers/regulator/Makefile3
-rw-r--r--drivers/regulator/as3722-regulator.c3
-rw-r--r--drivers/regulator/axp20x-regulator.c2
-rw-r--r--drivers/regulator/bd718x7-regulator.c164
-rw-r--r--drivers/regulator/core.c14
-rw-r--r--drivers/regulator/da9121-regulator.c1075
-rw-r--r--drivers/regulator/da9121-regulator.h291
-rw-r--r--drivers/regulator/fixed.c63
-rw-r--r--drivers/regulator/helpers.c2
-rw-r--r--drivers/regulator/lp872x.c2
-rw-r--r--drivers/regulator/max14577-regulator.c2
-rw-r--r--drivers/regulator/mc13892-regulator.c4
-rw-r--r--drivers/regulator/mcp16502.c135
-rw-r--r--drivers/regulator/of_regulator.c8
-rw-r--r--drivers/regulator/pf8x00-regulator.c496
-rw-r--r--drivers/regulator/pfuze100-regulator.c34
-rw-r--r--drivers/regulator/qcom-rpmh-regulator.c93
-rw-r--r--drivers/regulator/scmi-regulator.c417
-rw-r--r--include/dt-bindings/regulator/dlg,da9121-regulator.h22
-rw-r--r--include/linux/regulator/da9121.h36
-rw-r--r--include/linux/regulator/driver.h3
-rw-r--r--include/linux/regulator/pfuze100.h6
-rw-r--r--include/linux/scmi_protocol.h64
37 files changed, 3876 insertions, 84 deletions
diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt
index 55deb68230eb..b5ce5b39bb9c 100644
--- a/Documentation/devicetree/bindings/arm/arm,scmi.txt
+++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt
@@ -62,6 +62,20 @@ Required properties:
- #power-domain-cells : Should be 1. Contains the device or the power
domain ID value used by SCMI commands.
+Regulator bindings for the SCMI Regulator based on SCMI Message Protocol
+------------------------------------------------------------
+An SCMI Regulator is permanently bound to a well defined SCMI Voltage Domain,
+and should be always positioned as a root regulator.
+It does not support any current operation.
+
+SCMI Regulators are grouped under a 'regulators' node which in turn is a child
+of the SCMI Voltage protocol node inside the desired SCMI instance node.
+
+This binding uses the common regulator binding[6].
+
+Required properties:
+ - reg : shall identify an existent SCMI Voltage Domain.
+
Sensor bindings for the sensors based on SCMI Message Protocol
--------------------------------------------------------------
SCMI provides an API to access the various sensors on the SoC.
@@ -105,6 +119,7 @@ Required sub-node properties:
[3] Documentation/devicetree/bindings/thermal/thermal*.yaml
[4] Documentation/devicetree/bindings/sram/sram.yaml
[5] Documentation/devicetree/bindings/reset/reset.txt
+[6] Documentation/devicetree/bindings/regulator/regulator.yaml
Example:
@@ -169,6 +184,25 @@ firmware {
reg = <0x16>;
#reset-cells = <1>;
};
+
+ scmi_voltage: protocol@17 {
+ reg = <0x17>;
+
+ regulators {
+ regulator_devX: regulator@0 {
+ reg = <0x0>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ regulator_devY: regulator@9 {
+ reg = <0x9>;
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <4200000>;
+ };
+
+ ...
+ };
+ };
};
};
diff --git a/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml
new file mode 100644
index 000000000000..6f2164f7bc57
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/dlg,da9121.yaml
@@ -0,0 +1,189 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/dlg,da9121.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Dialog Semiconductor DA9121 voltage regulator
+
+maintainers:
+ - Adam Ward <Adam.Ward.opensource@diasemi.com>
+
+description: |
+ Dialog Semiconductor DA9121 Single-channel 10A double-phase buck converter
+ Dialog Semiconductor DA9122 Double-channel 5A single-phase buck converter
+ Dialog Semiconductor DA9220 Double-channel 3A single-phase buck converter
+ Dialog Semiconductor DA9217 Single-channel 6A double-phase buck converter
+ Dialog Semiconductor DA9130 Single-channel 10A double-phase buck converter
+ Dialog Semiconductor DA9131 Double-channel 5A single-phase buck converter
+ Dialog Semiconductor DA9132 Double-channel 3A single-phase buck converter
+
+ Current limits
+
+ This is PER PHASE, and the current limit setting in the devices reflect
+ that with a maximum 10A limit. Allowing for transients at/near double
+ the rated current, this translates across the device range to per
+ channel figures as so...
+
+ | DA9121 DA9122 DA9220 DA9217 DA9140
+ | /DA9130 /DA9131 /DA9132
+ -----------------------------------------------------------------------------
+ Output current / channel | 10000000 5000000 3000000 6000000 40000000
+ Output current / phase | 5000000 5000000 3000000 3000000 9500000
+ -----------------------------------------------------------------------------
+ Min regulator-min-microvolt| 300000 300000 300000 300000 500000
+ Max regulator-max-microvolt| 1900000 1900000 1900000 1900000 1000000
+ Device hardware default | 1000000 1000000 1000000 1000000 1000000
+ -----------------------------------------------------------------------------
+ Min regulator-min-microamp | 7000000 3500000 3500000 7000000 26000000
+ Max regulator-max-microamp | 20000000 10000000 6000000 12000000 78000000
+ Device hardware default | 15000000 7500000 5500000 11000000 58000000
+
+properties:
+ $nodename:
+ pattern: "pmic@[0-9a-f]{1,2}"
+ compatible:
+ enum:
+ - dlg,da9121
+ - dlg,da9122
+ - dlg,da9220
+ - dlg,da9217
+ - dlg,da9130
+ - dlg,da9131
+ - dlg,da9132
+ - dlg,da9140
+
+ reg:
+ maxItems: 1
+ description: Specifies the I2C slave address.
+
+ interrupts:
+ maxItems: 1
+ description: IRQ line information.
+
+ dlg,irq-polling-delay-passive-ms:
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+ minimum: 1000
+ maximum: 10000
+ description: |
+ Specify the polling period, measured in milliseconds, between interrupt status
+ update checks. Range 1000-10000 ms.
+
+ regulators:
+ type: object
+ $ref: regulator.yaml#
+ description: |
+ This node defines the settings for the BUCK. The content of the
+ sub-node is defined by the standard binding for regulators; see regulator.yaml.
+ The DA9121 regulator is bound using their names listed below
+ buck1 - BUCK1
+ buck2 - BUCK2 //DA9122, DA9220, DA9131, DA9132 only
+
+ patternProperties:
+ "^buck([1-2])$":
+ type: object
+ $ref: regulator.yaml#
+
+ properties:
+ regulator-mode:
+ maxItems: 1
+ description: Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h
+
+ regulator-initial-mode:
+ maxItems: 1
+ description: Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h
+
+ enable-gpios:
+ maxItems: 1
+ description: Specify a valid GPIO for platform control of the regulator
+
+ dlg,ripple-cancel:
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+ description: |
+ Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h
+ Only present on multi-channel devices (DA9122, DA9220, DA9131, DA9132)
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - regulators
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/regulator/dlg,da9121-regulator.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pmic@68 {
+ compatible = "dlg,da9121";
+ reg = <0x68>;
+
+ interrupt-parent = <&gpio6>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+
+ dlg,irq-polling-delay-passive-ms = <2000>;
+
+ regulators {
+ DA9121_BUCK1: buck1 {
+ regulator-name = "BUCK1";
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-min-microamp = <7000000>;
+ regulator-max-microamp = <20000000>;
+ regulator-boot-on;
+ regulator-initial-mode = <DA9121_BUCK_MODE_AUTO>;
+ enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ };
+ };
+ };
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/regulator/dlg,da9121-regulator.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pmic@68 {
+ compatible = "dlg,da9122";
+ reg = <0x68>;
+
+ interrupt-parent = <&gpio6>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+
+ dlg,irq-polling-delay-passive-ms = <2000>;
+
+ regulators {
+ DA9122_BUCK1: buck1 {
+ regulator-name = "BUCK1";
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-min-microamp = <3500000>;
+ regulator-max-microamp = <10000000>;
+ regulator-boot-on;
+ regulator-initial-mode = <DA9121_BUCK_MODE_AUTO>;
+ enable-gpios = <&gpio6 1 GPIO_ACTIVE_HIGH>;
+ dlg,ripple-cancel = <DA9121_BUCK_RIPPLE_CANCEL_NONE>;
+ };
+ DA9122_BUCK2: buck2 {
+ regulator-name = "BUCK2";
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1900000>;
+ regulator-min-microamp = <3500000>;
+ regulator-max-microamp = <10000000>;
+ regulator-boot-on;
+ regulator-initial-mode = <DA9121_BUCK_MODE_AUTO>;
+ enable-gpios = <&gpio6 2 GPIO_ACTIVE_HIGH>;
+ dlg,ripple-cancel = <DA9121_BUCK_RIPPLE_CANCEL_NONE>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml b/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml
index 92211f2b3b0c..d3d0dc13dd8b 100644
--- a/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.yaml
@@ -26,12 +26,22 @@ if:
const: regulator-fixed-clock
required:
- clocks
+else:
+ if:
+ properties:
+ compatible:
+ contains:
+ const: regulator-fixed-domain
+ required:
+ - power-domains
+ - required-opps
properties:
compatible:
enum:
- regulator-fixed
- regulator-fixed-clock
+ - regulator-fixed-domain
regulator-name: true
@@ -46,6 +56,20 @@ properties:
is mandatory if compatible is chosen to regulator-fixed-clock.
maxItems: 1
+ power-domains:
+ description:
+ Power domain to use for enable control. This binding is only
+ available if the compatible is chosen to regulator-fixed-domain.
+ maxItems: 1
+
+ required-opps:
+ description:
+ Performance state to use for enable control. This binding is only
+ available if the compatible is chosen to regulator-fixed-domain. The
+ power-domain binding is mandatory if compatible is chosen to
+ regulator-fixed-domain.
+ maxItems: 1
+
startup-delay-us:
description: startup time in microseconds
$ref: /schemas/types.yaml#/definitions/uint32
@@ -89,4 +113,27 @@ examples:
gpio-open-drain;
vin-supply = <&parent_reg>;
};
+ reg_1v8_clk: regulator-1v8-clk {
+ compatible = "regulator-fixed-clock";
+ regulator-name = "1v8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ clocks = <&clock1>;
+ startup-delay-us = <70000>;
+ enable-active-high;
+ regulator-boot-on;
+ vin-supply = <&parent_reg>;
+ };
+ reg_1v8_domain: regulator-1v8-domain {
+ compatible = "regulator-fixed-domain";
+ regulator-name = "1v8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ power-domains = <&domain1>;
+ required-opps = <&domain1_state1>;
+ startup-delay-us = <70000>;
+ enable-active-high;
+ regulator-boot-on;
+ vin-supply = <&parent_reg>;
+ };
...
diff --git a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
index b8f843fa6092..d86584ed4d93 100644
--- a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
@@ -10,7 +10,7 @@ Required properties:
name. The content of each sub-node is defined by the
standard binding for regulators; see regulator.txt.
-Regualtors of MCP16502 PMIC:
+Regulators of MCP16502 PMIC:
1) VDD_IO - Buck (1.2 - 3.7 V)
2) VDD_DDR - Buck (0.6 - 1.85 V)
3) VDD_CORE - Buck (0.6 - 1.85 V)
diff --git a/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml
new file mode 100644
index 000000000000..a6c259ce9785
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml
@@ -0,0 +1,211 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/nxp,pf8x00-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP PF8100/PF8121A/PF8200 PMIC regulators
+
+maintainers:
+ - Jagan Teki <jagan@amarulasolutions.com>
+ - Troy Kisky <troy.kisky@boundarydevices.com>
+
+description: |
+ PF8100/PF8121A/PF8200 is a PMIC designed for highperformance consumer
+ applications. It features seven high efficiency buck converters, four
+ linear and one vsnvs regulators. It has built-in one time programmable
+ fuse bank for device configurations.
+
+properties:
+ compatible:
+ enum:
+ - nxp,pf8x00
+
+ reg:
+ maxItems: 1
+
+ regulators:
+ type: object
+ description: |
+ list of regulators provided by this controller
+
+ patternProperties:
+ "^ldo[1-4]$":
+ type: object
+ $ref: regulator.yaml#
+ description:
+ Properties for single LDO regulator.
+
+ properties:
+ regulator-name:
+ pattern: "^ldo[1-4]$"
+ description:
+ should be "ldo1", ..., "ldo4"
+
+ unevaluatedProperties: false
+
+ "^buck[1-7]$":
+ type: object
+ $ref: regulator.yaml#
+ description:
+ Properties for single BUCK regulator.
+
+ properties:
+ regulator-name:
+ pattern: "^buck[1-7]$"
+ description:
+ should be "buck1", ..., "buck7"
+
+ nxp,ilim-ma:
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+ minimum: 2100
+ maximum: 4500
+ description:
+ BUCK regulators current limit in mA.
+
+ Listed current limits in mA are,
+ 2100 (default)
+ 2600
+ 3000
+ 4500
+
+ nxp,phase-shift:
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+ minimum: 45
+ maximum: 0
+ description:
+ BUCK regulators phase shift control in degrees.
+
+ Listed phase shift control values in degrees are,
+ 45
+ 90
+ 135
+ 180
+ 225
+ 270
+ 315
+ 0 (default)
+
+ unevaluatedProperties: false
+
+ "^vsnvs$":
+ type: object
+ $ref: regulator.yaml#
+ description:
+ Properties for single VSNVS regulator.
+
+ properties:
+ regulator-name:
+ pattern: "^vsnvs$"
+ description:
+ should be "vsnvs"
+
+ unevaluatedProperties: false
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - regulators
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pmic@8 {
+ compatible = "nxp,pf8x00";
+ reg = <0x08>;
+
+ regulators {
+ reg_ldo1: ldo1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <1500000>;
+ };
+
+ reg_ldo2: ldo2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <1500000>;
+ };
+
+ reg_ldo3: ldo3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <1500000>;
+ };
+
+ reg_ldo4: ldo4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <1500000>;
+ };
+
+ reg_buck1: buck1 {
+ nxp,ilim-ma = <4500>;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <400000>;
+ };
+
+ reg_buck2: buck2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <400000>;
+ };
+
+ reg_buck3: buck3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <400000>;
+ };
+
+ reg_buck4: buck4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <400000>;
+ };
+
+ reg_buck5: buck5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <400000>;
+ };
+
+ reg_buck6: buck6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <400000>;
+ };
+
+ reg_buck7: buck7 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <3300000>;
+ };
+
+ reg_vsnvs: vsnvs {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <1800000>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
index 97c3e0b7611c..b8f0b7809c02 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
@@ -26,10 +26,13 @@ Supported regulator node names:
PM8009: smps1 - smps2, ldo1 - ldo7
PM8150: smps1 - smps10, ldo1 - ldo18
PM8150L: smps1 - smps8, ldo1 - ldo11, bob, flash, rgb
+ PM8350: smps1 - smps12, ldo1 - ldo10,
+ PM8350C: smps1 - smps10, ldo1 - ldo13, bob
PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
PMI8998: bob
PM6150: smps1 - smps5, ldo1 - ldo19
PM6150L: smps1 - smps8, ldo1 - ldo11, bob
+ PMX55: smps1 - smps7, ldo1 - ldo16
========================
First Level Nodes - PMIC
@@ -43,10 +46,13 @@ First Level Nodes - PMIC
"qcom,pm8009-rpmh-regulators"
"qcom,pm8150-rpmh-regulators"
"qcom,pm8150l-rpmh-regulators"
+ "qcom,pm8350-rpmh-regulators"
+ "qcom,pm8350c-rpmh-regulators"
"qcom,pm8998-rpmh-regulators"
"qcom,pmi8998-rpmh-regulators"
"qcom,pm6150-rpmh-regulators"
"qcom,pm6150l-rpmh-regulators"
+ "qcom,pmx55-rpmh-regulators"
- qcom,pmic-id
Usage: required
diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml
index f5e31196a646..1941b36cf1ef 100644
--- a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml
@@ -105,6 +105,54 @@ patternProperties:
PMIC hardware state machine.
type: boolean
+ # Setups where regulator (especially the buck8) output voltage is scaled
+ # by adding external connection where some other regulator output is
+ # connected to feedback-pin (over suitable resistors) is getting popular
+ # amongst users of BD71837. (This allows for example scaling down the
+ # buck8 voltages to suit lover GPU voltages for projects where buck8 is
+ # (ab)used to supply power for GPU.
+ #
+ # So we allow describing this external connection from DT and scale the
+ # voltages accordingly. This is what the connection should look like:
+ #
+ # |---------------|
+ # | buck 8 |-------+----->Vout
+ # | | |
+ # |---------------| |
+ # | |
+ # | |
+ # +-------+--R2----+
+ # |
+ # R1
+ # |
+ # V FB-pull-up
+ #
+ # Here the buck output is sifted according to formula:
+ #
+ # Vout_o = Vo - (Vpu - Vo)*R2/R1
+ # Linear_step = step_orig*(R1+R2)/R1
+ #
+ # where:
+ # Vout_o is adjusted voltage output at vsel reg value 0
+ # Vo is original voltage output at vsel reg value 0
+ # Vpu is the pull-up voltage V FB-pull-up in the picture
+ # R1 and R2 are resistor values.
+
+ rohm,fb-pull-up-microvolt:
+ description:
+ Feedback-pin has pull-up connection to adjust voltage range. This is
+ the used pull-up voltage before R1.
+
+ rohm,feedback-pull-up-r1-ohms:
+ description:
+ Feedback-pin has pull-up connection to adjust voltage range. This is
+ the used R1 resistor.
+
+ rohm,feedback-pull-up-r2-ohms:
+ description:
+ Feedback-pin has pull-up connection to adjust voltage range. This is
+ the used R2 resistor.
+
required:
- regulator-name
diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml b/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml
index eeac32cd15d6..a1b806373853 100644
--- a/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml
@@ -99,6 +99,55 @@ patternProperties:
Enable/Disable control of this regulator must be left to the
PMIC hardware state machine.
type: boolean
+
+ # Setups where regulator (especially the buck8) output voltage is scaled
+ # by adding external connection where some other regulator output is
+ # connected to feedback-pin (over suitable resistors) is getting popular
+ # amongst users of BD71837. (This allows for example scaling down the
+ # buck8 voltages to suit lover GPU voltages for projects where buck8 is
+ # (ab)used to supply power for GPU.
+ #
+ # So we allow describing this external connection from DT and scale the
+ # voltages accordingly. This is what the connection should look like:
+ #
+ # |---------------|
+ # | buck 8 |-------+----->Vout
+ # | | |
+ # |---------------| |
+ # | |
+ # | |
+ # +-------+--R2----+
+ # |
+ # R1
+ # |
+ # V FB-pull-up
+ #
+ # Here the buck output is sifted according to formula:
+ #
+ # Vout_o = Vo - (Vpu - Vo)*R2/R1
+ # Linear_step = step_orig*(R1+R2)/R1
+ #
+ # where:
+ # Vout_o is adjusted voltage output at vsel reg value 0
+ # Vo is original voltage output at vsel reg value 0
+ # Vpu is the pull-up voltage V FB-pull-up in the picture
+ # R1 and R2 are resistor values.
+
+ rohm,fb-pull-up-microvolt:
+ description:
+ Feedback-pin has pull-up connection to adjust voltage range. This is
+ the used pull-up voltage before R1.
+
+ rohm,feedback-pull-up-r1-ohms:
+ description:
+ Feedback-pin has pull-up connection to adjust voltage range. This is
+ the used R1 resistor.
+
+ rohm,feedback-pull-up-r2-ohms:
+ description:
+ Feedback-pin has pull-up connection to adjust voltage range. This is
+ the used R2 resistor.
+
required:
- regulator-name
diff --git a/MAINTAINERS b/MAINTAINERS
index a355db292486..125819e4857d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5138,6 +5138,7 @@ S: Supported
W: http://www.dialog-semiconductor.com/products
F: Documentation/devicetree/bindings/input/da90??-onkey.txt
F: Documentation/devicetree/bindings/mfd/da90*.txt
+F: Documentation/devicetree/bindings/regulator/dlg,da9*.yaml
F: Documentation/devicetree/bindings/regulator/da92*.txt
F: Documentation/devicetree/bindings/regulator/slg51000.txt
F: Documentation/devicetree/bindings/sound/da[79]*.txt
@@ -5162,6 +5163,7 @@ F: drivers/rtc/rtc-da90??.c
F: drivers/thermal/da90??-thermal.c
F: drivers/video/backlight/da90??_bl.c
F: drivers/watchdog/da90??_wdt.c
+F: include/dt-bindings/regulator/dlg,da9*-regulator.h
F: include/linux/mfd/da903x.h
F: include/linux/mfd/da9052/
F: include/linux/mfd/da9055/
@@ -12666,6 +12668,12 @@ S: Maintained
F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml
F: drivers/gpu/drm/imx/dcss/
+NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
+M: Jagan Teki <jagan@amarulasolutions.com>
+S: Maintained
+F: Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml
+F: drivers/regulator/pf8x00-regulator.c
+
NXP PTN5150A CC LOGIC AND EXTCON DRIVER
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-kernel@vger.kernel.org
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index bc0d54f8e861..6a2ef63306d6 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
-scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
+scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 65063fa948d4..c0fb45e7c3e8 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -169,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf);
DECLARE_SCMI_REGISTER_UNREGISTER(power);
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
+DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
DECLARE_SCMI_REGISTER_UNREGISTER(system);
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 3dfd8b6a0ebf..5392e1fc6b4e 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
{ SCMI_PROTOCOL_CLOCK, { "clocks" },},
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
{ SCMI_PROTOCOL_RESET, { "reset" },},
+ { SCMI_PROTOCOL_VOLTAGE, { "regulator" },},
};
static inline void
@@ -946,6 +947,7 @@ static int __init scmi_driver_init(void)
scmi_power_register();
scmi_reset_register();
scmi_sensors_register();
+ scmi_voltage_register();
scmi_system_register();
return platform_driver_register(&scmi_driver);
@@ -961,6 +963,7 @@ static void __exit scmi_driver_exit(void)
scmi_power_unregister();
scmi_reset_unregister();
scmi_sensors_unregister();
+ scmi_voltage_unregister();
scmi_system_unregister();
platform_driver_unregister(&scmi_driver);
diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c
new file mode 100644
index 000000000000..e794e4349ae6
--- /dev/null
+++ b/drivers/firmware/arm_scmi/voltage.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Voltage Protocol
+ *
+ * Copyright (C) 2020 ARM Ltd.
+ */
+
+#include <linux/scmi_protocol.h>
+
+#include "common.h"
+
+#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
+#define REMAINING_LEVELS_MASK GENMASK(31, 16)
+#define RETURNED_LEVELS_MASK GENMASK(11, 0)
+
+enum scmi_voltage_protocol_cmd {
+ VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
+ VOLTAGE_DESCRIBE_LEVELS = 0x4,
+ VOLTAGE_CONFIG_SET = 0x5,
+ VOLTAGE_CONFIG_GET = 0x6,
+ VOLTAGE_LEVEL_SET = 0x7,
+ VOLTAGE_LEVEL_GET = 0x8,
+};
+
+#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
+
+struct scmi_msg_resp_domain_attributes {
+ __le32 attr;
+ u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_msg_cmd_describe_levels {
+ __le32 domain_id;
+ __le32 level_index;
+};
+
+struct scmi_msg_resp_describe_levels {
+ __le32 flags;
+#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
+#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
+#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12))
+ __le32 voltage[];
+};
+
+struct scmi_msg_cmd_config_set {
+ __le32 domain_id;
+ __le32 config;
+};
+
+struct scmi_msg_cmd_level_set {
+ __le32 domain_id;
+ __le32 flags;
+ __le32 voltage_level;
+};
+
+struct voltage_info {
+ unsigned int version;
+ unsigned int num_domains;
+ struct scmi_voltage_info *domains;
+};
+
+static int scmi_protocol_attributes_get(const struct scmi_handle *handle,
+ struct voltage_info *vinfo)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
+ SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t);
+ if (ret)
+ return ret;
+
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ vinfo->num_domains =
+ NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_init_voltage_levels(struct device *dev,
+ struct scmi_voltage_info *v,
+ u32 num_returned, u32 num_remaining,
+ bool segmented)
+{
+ u32 num_levels;
+
+ num_levels = num_returned + num_remaining;
+ /*
+ * segmented levels entries are represented by a single triplet
+ * returned all in one go.
+ */
+ if (!num_levels ||
+ (segmented && (num_remaining || num_returned != 3))) {
+ dev_err(dev,
+ "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
+ num_levels, num_returned, num_remaining, v->id);
+ return -EINVAL;
+ }
+
+ v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
+ if (!v->levels_uv)
+ return -ENOMEM;
+
+ v->num_levels = num_levels;
+ v->segmented = segmented;
+
+ return 0;
+}
+
+static int scmi_voltage_descriptors_get(const struct scmi_handle *handle,
+ struct voltage_info *vinfo)
+{
+ int ret, dom;
+ struct scmi_xfer *td, *tl;
+ struct device *dev = handle->dev;
+ struct scmi_msg_resp_domain_attributes *resp_dom;
+ struct scmi_msg_resp_describe_levels *resp_levels;
+
+ ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES,
+ SCMI_PROTOCOL_VOLTAGE, sizeof(__le32),
+ sizeof(*resp_dom), &td);
+ if (ret)
+ return ret;
+ resp_dom = td->rx.buf;
+
+ ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS,
+ SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl);
+ if (ret)
+ goto outd;
+ resp_levels = tl->rx.buf;
+
+ for (dom = 0; dom < vinfo->num_domains; dom++) {
+ u32 desc_index = 0;
+ u16 num_returned = 0, num_remaining = 0;
+ struct scmi_msg_cmd_describe_levels *cmd;
+ struct scmi_voltage_info *v;
+
+ /* Retrieve domain attributes at first ... */
+ put_unaligned_le32(dom, td->tx.buf);
+ ret = scmi_do_xfer(handle, td);
+ /* Skip domain on comms error */
+ if (ret)
+ continue;
+
+ v = vinfo->domains + dom;
+ v->id = dom;
+ v->attributes = le32_to_cpu(resp_dom->attr);
+ strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
+
+ cmd = tl->tx.buf;
+ /* ...then retrieve domain levels descriptions */
+ do {
+ u32 flags;
+ int cnt;
+
+ cmd->domain_id = cpu_to_le32(v->id);
+ cmd->level_index = desc_index;
+ ret = scmi_do_xfer(handle, tl);
+ if (ret)
+ break;
+
+ flags = le32_to_cpu(resp_levels->flags);
+ num_returned = NUM_RETURNED_LEVELS(flags);
+ num_remaining = NUM_REMAINING_LEVELS(flags);
+
+ /* Allocate space for num_levels if not already done */
+ if (!v->num_levels) {
+ ret = scmi_init_voltage_levels(dev, v,
+ num_returned,
+ num_remaining,
+ SUPPORTS_SEGMENTED_LEVELS(flags));
+ if (ret)
+ break;
+ }
+
+ if (desc_index + num_returned > v->num_levels) {
+ dev_err(handle->dev,
+ "No. of voltage levels can't exceed %d\n",
+ v->num_levels);
+ ret = -EINVAL;
+ break;
+ }
+
+ for (cnt = 0; cnt < num_returned; cnt++) {
+ s32 val;
+
+ val =
+ (s32)le32_to_cpu(resp_levels->voltage[cnt]);
+ v->levels_uv[desc_index + cnt] = val;
+ if (val < 0)
+ v->negative_volts_allowed = true;
+ }
+
+ desc_index += num_returned;
+
+ scmi_reset_rx_to_maxsz(handle, tl);
+ /* check both to avoid infinite loop due to buggy fw */
+ } while (num_returned && num_remaining);
+
+ if (ret) {
+ v->num_levels = 0;
+ devm_kfree(dev, v->levels_uv);
+ }
+
+ scmi_reset_rx_to_maxsz(handle, td);
+ }
+
+ scmi_xfer_put(handle, tl);
+outd:
+ scmi_xfer_put(handle, td);
+
+ return ret;
+}
+
+static int __scmi_voltage_get_u32(const struct scmi_handle *handle,
+ u8 cmd_id, u32 domain_id, u32 *value)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct voltage_info *vinfo = handle->voltage_priv;
+
+ if (domain_id >= vinfo->num_domains)
+ return -EINVAL;
+
+ ret = scmi_xfer_get_init(handle, cmd_id,
+ SCMI_PROTOCOL_VOLTAGE,
+ sizeof(__le32), 0, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(domain_id, t->tx.buf);
+ ret = scmi_do_xfer(handle, t);
+ if (!ret)
+ *value = get_unaligned_le32(t->rx.buf);
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_voltage_config_set(const struct scmi_handle *handle,
+ u32 domain_id, u32 config)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct voltage_info *vinfo = handle->voltage_priv;
+ struct scmi_msg_cmd_config_set *cmd;
+
+ if (domain_id >= vinfo->num_domains)
+ return -EINVAL;
+
+ ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET,
+ SCMI_PROTOCOL_VOLTAGE,
+ sizeof(*cmd), 0, &t);
+ if (ret)
+ return ret;
+
+ cmd = t->tx.buf;
+ cmd->domain_id = cpu_to_le32(domain_id);
+ cmd->config = cpu_to_le32(config & GENMASK(3, 0));
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_voltage_config_get(const struct scmi_handle *handle,
+ u32 domain_id, u32 *config)
+{
+ return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET,
+ domain_id, config);
+}
+
+static int scmi_voltage_level_set(const struct scmi_handle *handle,
+ u32 domain_id, u32 flags, s32 volt_uV)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct voltage_info *vinfo = handle->voltage_priv;
+ struct scmi_msg_cmd_level_set *cmd;
+
+ if (domain_id >= vinfo->num_domains)
+ return -EINVAL;
+
+ ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET,
+ SCMI_PROTOCOL_VOLTAGE,
+ sizeof(*cmd), 0, &t);
+ if (ret)
+ return ret;
+
+ cmd = t->tx.buf;
+ cmd->domain_id = cpu_to_le32(domain_id);
+ cmd->flags = cpu_to_le32(flags);
+ cmd->voltage_level = cpu_to_le32(volt_uV);
+
+ ret = scmi_do_xfer(handle, t);
+
+ scmi_xfer_put(handle, t);
+ return ret;
+}
+
+static int scmi_voltage_level_get(const struct scmi_handle *handle,
+ u32 domain_id, s32 *volt_uV)
+{
+ return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET,
+ domain_id, (u32 *)volt_uV);
+}
+
+static const struct scmi_voltage_info * __must_check
+scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id)
+{
+ struct voltage_info *vinfo = handle->voltage_priv;
+
+ if (domain_id >= vinfo->num_domains ||
+ !vinfo->domains[domain_id].num_levels)
+ return NULL;
+
+ return vinfo->domains + domain_id;
+}
+
+static int scmi_voltage_domains_num_get(const struct scmi_handle *handle)
+{
+ struct voltage_info *vinfo = handle->voltage_priv;
+
+ return vinfo->num_domains;
+}
+
+static struct scmi_voltage_ops voltage_ops = {
+ .num_domains_get = scmi_voltage_domains_num_get,
+ .info_get = scmi_voltage_info_get,
+ .config_set = scmi_voltage_config_set,
+ .config_get = scmi_voltage_config_get,
+ .level_set = scmi_voltage_level_set,
+ .level_get = scmi_voltage_level_get,
+};
+
+static int scmi_voltage_protocol_init(struct scmi_handle *handle)
+{
+ int ret;
+ u32 version;
+ struct voltage_info *vinfo;
+
+ ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version);
+ if (ret)
+ return ret;
+
+ dev_dbg(handle->dev, "Voltage Version %d.%d\n",
+ PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+ vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL);
+ if (!vinfo)
+ return -ENOMEM;
+ vinfo->version = version;
+
+ ret = scmi_protocol_attributes_get(handle, vinfo);
+ if (ret)
+ return ret;
+
+ if (vinfo->num_domains) {
+ vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains,
+ sizeof(*vinfo->domains),
+ GFP_KERNEL);
+ if (!vinfo->domains)
+ return -ENOMEM;
+ ret = scmi_voltage_descriptors_get(handle, vinfo);
+ if (ret)
+ return ret;
+ } else {
+ dev_warn(handle->dev, "No Voltage domains found.\n");
+ }
+
+ handle->voltage_ops = &voltage_ops;
+ handle->voltage_priv = vinfo;
+
+ return 0;
+}
+
+DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage)
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 020a00d6696b..53fa84f4d1e1 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP
and Wolfson Microelectronic Arizona codecs
devices.
+config REGULATOR_ARM_SCMI
+ tristate "SCMI based regulator driver"
+ depends on ARM_SCMI_PROTOCOL && OF
+ help
+ This adds the regulator driver support for ARM platforms using SCMI
+ protocol for device voltage management.
+ This driver uses SCMI Message Protocol driver to interact with the
+ firmware providing the device Voltage functionality.
+
config REGULATOR_AS3711
tristate "AS3711 PMIC"
depends on MFD_AS3711
@@ -303,6 +312,26 @@ config REGULATOR_DA9063
This driver can also be built as a module. If so, the module
will be called da9063-regulator.
+config REGULATOR_DA9121
+ tristate "Dialog Semiconductor DA9121/DA9122/DA9220/DA9217/DA9130/DA9131/DA9132 regulator"
+ depends on I2C && OF
+ select REGMAP_I2C
+ help
+ Say y here to support for the Dialog Semiconductor DA9121. The
+ DA9121 is a single channel dual-phase buck converter controlled
+ through an I2C interface.
+
+ DA9121 Single-channel dual-phase 10A buck converter
+ DA9130 Single-channel dual-phase 10A buck converter (Automotive)
+ DA9217 Single-channel dual-phase 6A buck converter
+ DA9122 Dual-channel single-phase 5A buck converter
+ DA9131 Dual-channel single-phase 5A buck converter (Automotive)
+ DA9220 Dual-channel single-phase 3A buck converter
+ DA9132 Dual-channel single-phase 3A buck converter (Automotive)
+
+ This driver can also be built as a module. If so, the module
+ will be called da9121-regulator.
+
config REGULATOR_DA9210
tristate "Dialog Semiconductor DA9210 regulator"
depends on I2C
@@ -791,9 +820,17 @@ config REGULATOR_PCF50633
Say Y here to support the voltage regulators and converters
on PCF50633
+config REGULATOR_PF8X00
+ tristate "NXP PF8100/PF8121A/PF8200 regulator driver"
+ depends on I2C && OF
+ select REGMAP_I2C
+ help
+ Say y here to support the regulators found on the NXP
+ PF8100/PF8121A/PF8200 PMIC.
+
config REGULATOR_PFUZE100
tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
- depends on I2C
+ depends on I2C && OF
select REGMAP_I2C
help
Say y here to support the regulators found on the Freescale
@@ -843,7 +880,7 @@ config REGULATOR_QCOM_RPM
config REGULATOR_QCOM_RPMH
tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
- depends on QCOM_RPMH || COMPILE_TEST
+ depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST)
help
This driver supports control of PMIC regulators via the RPMh hardware
block found on Qualcomm Technologies Inc. SoCs. RPMh regulator
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 6ebae516258e..680e539f6579 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o
obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o
+obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
@@ -38,6 +39,7 @@ obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
+obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o
obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
@@ -100,6 +102,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o
+obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o
diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c
index 33ca197860b3..7bebf9ce6271 100644
--- a/drivers/regulator/as3722-regulator.c
+++ b/drivers/regulator/as3722-regulator.c
@@ -455,7 +455,8 @@ static int as3722_sd_set_mode(struct regulator_dev *rdev,
switch (mode) {
case REGULATOR_MODE_FAST:
val = as3722_reg_lookup[id].mode_mask;
- case REGULATOR_MODE_NORMAL: /* fall down */
+ fallthrough;
+ case REGULATOR_MODE_NORMAL:
break;
default:
return -EINVAL;
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index cd1224182ad7..90cb8445f721 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -594,7 +594,7 @@ static const struct regulator_desc axp22x_regulators[] = {
AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK,
AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK),
AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
- AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK,
+ AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK,
AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK),
AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK,
diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c
index 0774467994fb..e6d5d98c3cea 100644
--- a/drivers/regulator/bd718x7-regulator.c
+++ b/drivers/regulator/bd718x7-regulator.c
@@ -1323,13 +1323,142 @@ static void mark_hw_controlled(struct device *dev, struct device_node *np,
dev_warn(dev, "Bad regulator node\n");
}
-static int get_hw_controlled_regulators(struct device *dev,
- struct bd718xx_regulator_data *reg_data,
- unsigned int num_reg_data, int *info)
+/*
+ * Setups where regulator (especially the buck8) output voltage is scaled
+ * by adding external connection where some other regulator output is connected
+ * to feedback-pin (over suitable resistors) is getting popular amongst users
+ * of BD71837. (This allows for example scaling down the buck8 voltages to suit
+ * lover GPU voltages for projects where buck8 is (ab)used to supply power
+ * for GPU. Additionally some setups do allow DVS for buck8 but as this do
+ * produce voltage spikes the HW must be evaluated to be able to survive this
+ * - hence I keep the DVS disabled for non DVS bucks by default. I don't want
+ * to help you burn your proto board)
+ *
+ * So we allow describing this external connection from DT and scale the
+ * voltages accordingly. This is what the connection should look like:
+ *
+ * |------------|
+ * | buck 8 |-------+----->Vout
+ * | | |
+ * |------------| |
+ * | FB pin |
+ * | |
+ * +-------+--R2---+
+ * |
+ * R1
+ * |
+ * V FB-pull-up
+ *
+ * Here the buck output is sifted according to formula:
+ *
+ * Vout_o = Vo - (Vpu - Vo)*R2/R1
+ * Linear_step = step_orig*(R1+R2)/R1
+ *
+ * where:
+ * Vout_o is adjusted voltage output at vsel reg value 0
+ * Vo is original voltage output at vsel reg value 0
+ * Vpu is the pull-up voltage V FB-pull-up in the picture
+ * R1 and R2 are resistor values.
+ *
+ * As a real world example for buck8 and a specific GPU:
+ * VLDO = 1.6V (used as FB-pull-up)
+ * R1 = 1000ohms
+ * R2 = 150ohms
+ * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V
+ * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV
+ */
+static int setup_feedback_loop(struct device *dev, struct device_node *np,
+ struct bd718xx_regulator_data *reg_data,
+ unsigned int num_reg_data, int fb_uv)
{
+ int i, r1, r2, ret;
+
+ /*
+ * We do adjust the values in the global desc based on DT settings.
+ * This may not be best approach as it can cause problems if more than
+ * one PMIC is controlled from same processor. I don't see such use-case
+ * for BD718x7 now - so we spare some bits.
+ *
+ * If this will point out to be a problem - then we can allocate new
+ * bd718xx_regulator_data array at probe and just use the global
+ * array as a template where we copy initial values. Then we can
+ * use allocated descs for regultor registration and do IC specific
+ * modifications to this copy while leaving other PMICs untouched. But
+ * that means allocating new array for each PMIC - and currently I see
+ * no need for that.
+ */
+
+ for (i = 0; i < num_reg_data; i++) {
+ struct regulator_desc *desc = &reg_data[i].desc;
+ int j;
+
+ if (!of_node_name_eq(np, desc->of_match))
+ continue;
+
+ pr_info("Looking at node '%s'\n", desc->of_match);
+
+ /* The feedback loop connection does not make sense for LDOs */
+ if (desc->id >= BD718XX_LDO1)
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms",
+ &r1);
+ if (ret)
+ return ret;
+
+ if (!r1)
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms",
+ &r2);
+ if (ret)
+ return ret;
+
+ if (desc->n_linear_ranges && desc->linear_ranges) {
+ struct linear_range *new;
+
+ new = devm_kzalloc(dev, desc->n_linear_ranges *
+ sizeof(struct linear_range),
+ GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ for (j = 0; j < desc->n_linear_ranges; j++) {
+ int min = desc->linear_ranges[j].min;
+ int step = desc->linear_ranges[j].step;
+
+ min -= (fb_uv - min)*r2/r1;
+ step = step * (r1 + r2);
+ step /= r1;
+
+ new[j].min = min;
+ new[j].step = step;
+
+ dev_dbg(dev, "%s: old range min %d, step %d\n",
+ desc->name, desc->linear_ranges[j].min,
+ desc->linear_ranges[j].step);
+ dev_dbg(dev, "new range min %d, step %d\n", min,
+ step);
+ }
+ desc->linear_ranges = new;
+ }
+ dev_dbg(dev, "regulator '%s' has FB pull-up configured\n",
+ desc->name);
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int get_special_regulators(struct device *dev,
+ struct bd718xx_regulator_data *reg_data,
+ unsigned int num_reg_data, int *info)
+{
+ int ret;
struct device_node *np;
struct device_node *nproot = dev->of_node;
- const char *prop = "rohm,no-regulator-enable-control";
+ int uv;
*info = 0;
@@ -1338,13 +1467,32 @@ static int get_hw_controlled_regulators(struct device *dev,
dev_err(dev, "failed to find regulators node\n");
return -ENODEV;
}
- for_each_child_of_node(nproot, np)
- if (of_property_read_bool(np, prop))
+ for_each_child_of_node(nproot, np) {
+ if (of_property_read_bool(np, "rohm,no-regulator-enable-control"))
mark_hw_controlled(dev, np, reg_data, num_reg_data,
info);
+ ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt",
+ &uv);
+ if (ret) {
+ if (ret == -EINVAL)
+ continue;
+ else
+ goto err_out;
+ }
+
+ ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv);
+ if (ret)
+ goto err_out;
+ }
of_node_put(nproot);
return 0;
+
+err_out:
+ of_node_put(np);
+ of_node_put(nproot);
+
+ return ret;
}
static int bd718xx_probe(struct platform_device *pdev)
@@ -1432,8 +1580,10 @@ static int bd718xx_probe(struct platform_device *pdev)
* be affected by PMIC state machine - Eg. regulator is likely to stay
* on even in SUSPEND
*/
- get_hw_controlled_regulators(pdev->dev.parent, reg_data, num_reg_data,
+ err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data,
&omit_enable);
+ if (err)
+ return err;
for (i = 0; i < num_reg_data; i++) {
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 42bbd99a36ac..ca03d8e70bd1 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2958,6 +2958,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev,
if (ops->list_voltage) {
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
+ if (selector < rdev->desc->linear_min_sel)
+ return 0;
if (lock)
regulator_lock(rdev);
ret = ops->list_voltage(rdev, selector);
@@ -3109,6 +3111,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator,
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
+ if (selector < rdev->desc->linear_min_sel)
+ return 0;
if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
return -EOPNOTSUPP;
@@ -4030,6 +4034,12 @@ int regulator_set_voltage_time(struct regulator *regulator,
for (i = 0; i < rdev->desc->n_voltages; i++) {
/* We only look for exact voltage matches here */
+ if (i < rdev->desc->linear_min_sel)
+ continue;
+
+ if (old_sel >= 0 && new_sel >= 0)
+ break;
+
voltage = regulator_list_voltage(regulator, i);
if (voltage < 0)
return -EINVAL;
@@ -5305,6 +5315,8 @@ regulator_register(const struct regulator_desc *regulator_desc,
/* FIXME: this currently triggers a chicken-and-egg problem
* when creating -SUPPLY symlink in sysfs to a regulator
* that is just being created */
+ rdev_dbg(rdev, "will resolve supply early: %s\n",
+ rdev->supply_name);
ret = regulator_resolve_supply(rdev);
if (!ret)
ret = set_machine_constraints(rdev);
@@ -5537,7 +5549,7 @@ void regulator_set_drvdata(struct regulator *regulator, void *data)
EXPORT_SYMBOL_GPL(regulator_set_drvdata);
/**
- * regulator_get_id - get regulator ID
+ * rdev_get_id - get regulator ID
* @rdev: regulator
*/
int rdev_get_id(struct regulator_dev *rdev)
diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c
new file mode 100644
index 000000000000..a2ede7d7897e
--- /dev/null
+++ b/drivers/regulator/da9121-regulator.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// DA9121 Single-channel dual-phase 10A buck converter
+//
+// Copyright (C) 2020 Axis Communications AB
+//
+// DA9130 Single-channel dual-phase 10A buck converter (Automotive)
+// DA9217 Single-channel dual-phase 6A buck converter
+// DA9122 Dual-channel single-phase 5A buck converter
+// DA9131 Dual-channel single-phase 5A buck converter (Automotive)
+// DA9220 Dual-channel single-phase 3A buck converter
+// DA9132 Dual-channel single-phase 3A buck converter (Automotive)
+//
+// Copyright (C) 2020 Dialog Semiconductor
+
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/regulator/da9121.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include "da9121-regulator.h"
+
+/* Chip data */
+struct da9121 {
+ struct device *dev;
+ struct delayed_work work;
+ struct da9121_pdata *pdata;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[DA9121_IDX_MAX];
+ unsigned int persistent[2];
+ unsigned int passive_delay;
+ int chip_irq;
+ int variant_id;
+};
+
+/* Define ranges for different variants, enabling translation to/from
+ * registers. Maximums give scope to allow for transients.
+ */
+struct da9121_range {
+ int val_min;
+ int val_max;
+ int val_stp;
+ int reg_min;
+ int reg_max;
+};
+
+static struct da9121_range da9121_10A_2phase_current = {
+ .val_min = 7000000,
+ .val_max = 20000000,
+ .val_stp = 1000000,
+ .reg_min = 1,
+ .reg_max = 14,
+};
+
+static struct da9121_range da9121_6A_2phase_current = {
+ .val_min = 7000000,
+ .val_max = 12000000,
+ .val_stp = 1000000,
+ .reg_min = 1,
+ .reg_max = 6,
+};
+
+static struct da9121_range da9121_5A_1phase_current = {
+ .val_min = 3500000,
+ .val_max = 10000000,
+ .val_stp = 500000,
+ .reg_min = 1,
+ .reg_max = 14,
+};
+
+static struct da9121_range da9121_3A_1phase_current = {
+ .val_min = 3500000,
+ .val_max = 6000000,
+ .val_stp = 500000,
+ .reg_min = 1,
+ .reg_max = 6,
+};
+
+struct da9121_variant_info {
+ int num_bucks;
+ int num_phases;
+ struct da9121_range *current_range;
+};
+
+static const struct da9121_variant_info variant_parameters[] = {
+ { 1, 2, &da9121_10A_2phase_current }, //DA9121_TYPE_DA9121_DA9130
+ { 2, 1, &da9121_3A_1phase_current }, //DA9121_TYPE_DA9220_DA9132
+ { 2, 1, &da9121_5A_1phase_current }, //DA9121_TYPE_DA9122_DA9131
+ { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217
+};
+
+struct da9121_field {
+ unsigned int reg;
+ unsigned int msk;
+};
+
+static const struct da9121_field da9121_current_field[2] = {
+ { DA9121_REG_BUCK_BUCK1_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM },
+ { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM },
+};
+
+static const struct da9121_field da9121_mode_field[2] = {
+ { DA9121_REG_BUCK_BUCK1_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE },
+ { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE },
+};
+
+struct status_event_data {
+ int buck_id; /* 0=core, 1/2-buck */
+ int reg_index; /* index for status/event/mask register selection */
+ int status_bit; /* bit masks... */
+ int event_bit;
+ int mask_bit;
+ unsigned long notification; /* Notification for status inception */
+ char *warn; /* if NULL, notify - otherwise dev_warn this string */
+};
+
+#define DA9121_STATUS(id, bank, name, notification, warning) \
+ { id, bank, \
+ DA9121_MASK_SYS_STATUS_##bank##_##name, \
+ DA9121_MASK_SYS_EVENT_##bank##_E_##name, \
+ DA9121_MASK_SYS_MASK_##bank##_M_##name, \
+ notification, warning }
+
+/* For second buck related event bits that are specific to DA9122, DA9220 variants */
+#define DA9xxx_STATUS(id, bank, name, notification, warning) \
+ { id, bank, \
+ DA9xxx_MASK_SYS_STATUS_##bank##_##name, \
+ DA9xxx_MASK_SYS_EVENT_##bank##_E_##name, \
+ DA9xxx_MASK_SYS_MASK_##bank##_M_##name, \
+ notification, warning }
+
+/* The status signals that may need servicing, depending on device variant.
+ * After assertion, they persist; so event is notified, the IRQ disabled,
+ * and status polled until clear again and IRQ is reenabled.
+ *
+ * SG/PG1/PG2 should be set when device first powers up and should never
+ * re-occur. When this driver starts, it is expected that these will have
+ * self-cleared for when the IRQs are enabled, so these should never be seen.
+ * If seen, the implication is that the device has reset.
+ *
+ * GPIO0/1/2 are not configured for use by default, so should not be seen.
+ */
+static const struct status_event_data status_event_handling[] = {
+ DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"),
+ DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL),
+ DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL),
+ DA9121_STATUS(1, 1, PG1, 0, "Handled E_PG1\n"),
+ DA9121_STATUS(1, 1, OV1, REGULATOR_EVENT_REGULATION_OUT, NULL),
+ DA9121_STATUS(1, 1, UV1, REGULATOR_EVENT_UNDER_VOLTAGE, NULL),
+ DA9121_STATUS(1, 1, OC1, REGULATOR_EVENT_OVER_CURRENT, NULL),
+ DA9xxx_STATUS(2, 1, PG2, 0, "Handled E_PG2\n"),
+ DA9xxx_STATUS(2, 1, OV2, REGULATOR_EVENT_REGULATION_OUT, NULL),
+ DA9xxx_STATUS(2, 1, UV2, REGULATOR_EVENT_UNDER_VOLTAGE, NULL),
+ DA9xxx_STATUS(2, 1, OC2, REGULATOR_EVENT_OVER_CURRENT, NULL),
+ DA9121_STATUS(0, 2, GPIO0, 0, "Handled E_GPIO0\n"),
+ DA9121_STATUS(0, 2, GPIO1, 0, "Handled E_GPIO1\n"),
+ DA9121_STATUS(0, 2, GPIO2, 0, "Handled E_GPIO2\n"),
+};
+
+static int da9121_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9121 *chip = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct da9121_range *range =
+ variant_parameters[chip->variant_id].current_range;
+ unsigned int val = 0;
+ int ret = 0;
+
+ ret = regmap_read(chip->regmap, da9121_current_field[id].reg, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret);
+ goto error;
+ }
+
+ if (val < range->reg_min) {
+ ret = -EACCES;
+ goto error;
+ }
+
+ if (val > range->reg_max) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ return range->val_min + (range->val_stp * (val - range->reg_min));
+error:
+ return ret;
+}
+
+static int da9121_ceiling_selector(struct regulator_dev *rdev,
+ int min, int max,
+ unsigned int *selector)
+{
+ struct da9121 *chip = rdev_get_drvdata(rdev);
+ struct da9121_range *range =
+ variant_parameters[chip->variant_id].current_range;
+ unsigned int level;
+ unsigned int i = 0;
+ unsigned int sel = 0;
+ int ret = 0;
+
+ if (range->val_min > max || range->val_max < min) {
+ dev_err(chip->dev,
+ "Requested current out of regulator capability\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ level = range->val_max;
+ for (i = range->reg_max; i >= range->reg_min; i--) {
+ if (level <= max) {
+ sel = i;
+ break;
+ }
+ level -= range->val_stp;
+ }
+
+ if (level < min) {
+ dev_err(chip->dev,
+ "Best match falls below minimum requested current\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ *selector = sel;
+error:
+ return ret;
+}
+
+static int da9121_set_current_limit(struct regulator_dev *rdev,
+ int min_ua, int max_ua)
+{
+ struct da9121 *chip = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct da9121_range *range =
+ variant_parameters[chip->variant_id].current_range;
+ unsigned int sel = 0;
+ int ret = 0;
+
+ if (min_ua < range->val_min ||
+ max_ua > range->val_max) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = da9121_ceiling_selector(rdev, min_ua, max_ua, &sel);
+ if (ret < 0)
+ goto error;
+
+ ret = regmap_update_bits(chip->regmap,
+ da9121_current_field[id].reg,
+ da9121_current_field[id].msk,
+ (unsigned int)sel);
+ if (ret < 0)
+ dev_err(chip->dev, "Cannot update BUCK current limit, err: %d\n", ret);
+
+error:
+ return ret;
+}
+
+static unsigned int da9121_map_mode(unsigned int mode)
+{
+ switch (mode) {
+ case DA9121_BUCK_MODE_FORCE_PWM:
+ return REGULATOR_MODE_FAST;
+ case DA9121_BUCK_MODE_FORCE_PWM_SHEDDING:
+ return REGULATOR_MODE_NORMAL;
+ case DA9121_BUCK_MODE_AUTO:
+ return REGULATOR_MODE_IDLE;
+ case DA9121_BUCK_MODE_FORCE_PFM:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9121_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct da9121 *chip = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ unsigned int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = DA9121_BUCK_MODE_FORCE_PWM;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = DA9121_BUCK_MODE_FORCE_PWM_SHEDDING;
+ break;
+ case REGULATOR_MODE_IDLE:
+ val = DA9121_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = DA9121_BUCK_MODE_FORCE_PFM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(chip->regmap,
+ da9121_mode_field[id].reg,
+ da9121_mode_field[id].msk,
+ val);
+}
+
+static unsigned int da9121_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9121 *chip = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ unsigned int val;
+ int ret = 0;
+
+ ret = regmap_read(chip->regmap, da9121_mode_field[id].reg, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret);
+ return -EINVAL;
+ }
+
+ return da9121_map_mode(val & da9121_mode_field[id].msk);
+}
+
+static const struct regulator_ops da9121_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_current_limit = da9121_get_current_limit,
+ .set_current_limit = da9121_set_current_limit,
+ .set_mode = da9121_buck_set_mode,
+ .get_mode = da9121_buck_get_mode,
+};
+
+static struct of_regulator_match da9121_matches[] = {
+ [DA9121_IDX_BUCK1] = { .name = "buck1" },
+ [DA9121_IDX_BUCK2] = { .name = "buck2" },
+};
+
+static int da9121_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct da9121 *chip = config->driver_data;
+ struct da9121_pdata *pdata;
+ struct gpio_desc *ena_gpiod;
+
+ if (chip->pdata == NULL) {
+ pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ } else {
+ pdata = chip->pdata;
+ }
+
+ pdata->num_buck++;
+
+ if (pdata->num_buck > variant_parameters[chip->variant_id].num_bucks) {
+ dev_err(chip->dev, "Error: excessive regulators for device\n");
+ return -ENODEV;
+ }
+
+ ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0,
+ GPIOD_OUT_HIGH |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+ "da9121-enable");
+ if (!IS_ERR(ena_gpiod))
+ config->ena_gpiod = ena_gpiod;
+
+ if (variant_parameters[chip->variant_id].num_bucks == 2) {
+ uint32_t ripple_cancel;
+ uint32_t ripple_reg;
+ int ret;
+
+ if (of_property_read_u32(da9121_matches[pdata->num_buck-1].of_node,
+ "dlg,ripple-cancel", &ripple_cancel)) {
+ if (pdata->num_buck > 1)
+ ripple_reg = DA9xxx_REG_BUCK_BUCK2_7;
+ else
+ ripple_reg = DA9121_REG_BUCK_BUCK1_7;
+
+ ret = regmap_update_bits(chip->regmap, ripple_reg,
+ DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL,
+ ripple_cancel);
+ if (ret < 0)
+ dev_err(chip->dev, "Cannot set ripple mode, err: %d\n", ret);
+ }
+ }
+
+ return 0;
+}
+
+#define DA9121_MIN_MV 300
+#define DA9121_MAX_MV 1900
+#define DA9121_STEP_MV 10
+#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV)
+#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \
+ + 1 + DA9121_MIN_SEL)
+
+static const struct regulator_desc da9121_reg = {
+ .id = DA9121_IDX_BUCK1,
+ .name = "da9121",
+ .of_match = "buck1",
+ .of_parse_cb = da9121_of_parse_cb,
+ .owner = THIS_MODULE,
+ .regulators_node = of_match_ptr("regulators"),
+ .of_map_mode = da9121_map_mode,
+ .ops = &da9121_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = DA9121_N_VOLTAGES,
+ .min_uV = DA9121_MIN_MV * 1000,
+ .uV_step = DA9121_STEP_MV * 1000,
+ .linear_min_sel = DA9121_MIN_SEL,
+ .vsel_reg = DA9121_REG_BUCK_BUCK1_5,
+ .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT,
+ .enable_reg = DA9121_REG_BUCK_BUCK1_0,
+ .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN,
+ /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */
+ .ramp_delay = 20000,
+ /* tBUCK_EN */
+ .enable_time = 20,
+};
+
+static const struct regulator_desc da9220_reg[2] = {
+ {
+ .id = DA9121_IDX_BUCK1,
+ .name = "DA9220/DA9132 BUCK1",
+ .of_match = "buck1",
+ .of_parse_cb = da9121_of_parse_cb,
+ .owner = THIS_MODULE,
+ .regulators_node = of_match_ptr("regulators"),
+ .of_map_mode = da9121_map_mode,
+ .ops = &da9121_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = DA9121_N_VOLTAGES,
+ .min_uV = DA9121_MIN_MV * 1000,
+ .uV_step = DA9121_STEP_MV * 1000,
+ .linear_min_sel = DA9121_MIN_SEL,
+ .enable_reg = DA9121_REG_BUCK_BUCK1_0,
+ .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN,
+ .vsel_reg = DA9121_REG_BUCK_BUCK1_5,
+ .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT,
+ },
+ {
+ .id = DA9121_IDX_BUCK2,
+ .name = "DA9220/DA9132 BUCK2",
+ .of_match = "buck2",
+ .of_parse_cb = da9121_of_parse_cb,
+ .owner = THIS_MODULE,
+ .regulators_node = of_match_ptr("regulators"),
+ .of_map_mode = da9121_map_mode,
+ .ops = &da9121_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = DA9121_N_VOLTAGES,
+ .min_uV = DA9121_MIN_MV * 1000,
+ .uV_step = DA9121_STEP_MV * 1000,
+ .linear_min_sel = DA9121_MIN_SEL,
+ .enable_reg = DA9xxx_REG_BUCK_BUCK2_0,
+ .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN,
+ .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5,
+ .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT,
+ }
+};
+
+static const struct regulator_desc da9122_reg[2] = {
+ {
+ .id = DA9121_IDX_BUCK1,
+ .name = "DA9122/DA9131 BUCK1",
+ .of_match = "buck1",
+ .of_parse_cb = da9121_of_parse_cb,
+ .owner = THIS_MODULE,
+ .regulators_node = of_match_ptr("regulators"),
+ .of_map_mode = da9121_map_mode,
+ .ops = &da9121_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = DA9121_N_VOLTAGES,
+ .min_uV = DA9121_MIN_MV * 1000,
+ .uV_step = DA9121_STEP_MV * 1000,
+ .linear_min_sel = DA9121_MIN_SEL,
+ .enable_reg = DA9121_REG_BUCK_BUCK1_0,
+ .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN,
+ .vsel_reg = DA9121_REG_BUCK_BUCK1_5,
+ .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT,
+ },
+ {
+ .id = DA9121_IDX_BUCK2,
+ .name = "DA9122/DA9131 BUCK2",
+ .of_match = "buck2",
+ .of_parse_cb = da9121_of_parse_cb,
+ .owner = THIS_MODULE,
+ .regulators_node = of_match_ptr("regulators"),
+ .of_map_mode = da9121_map_mode,
+ .ops = &da9121_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = DA9121_N_VOLTAGES,
+ .min_uV = DA9121_MIN_MV * 1000,
+ .uV_step = DA9121_STEP_MV * 1000,
+ .linear_min_sel = DA9121_MIN_SEL,
+ .enable_reg = DA9xxx_REG_BUCK_BUCK2_0,
+ .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN,
+ .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5,
+ .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT,
+ }
+};
+
+static const struct regulator_desc da9217_reg = {
+ .id = DA9121_IDX_BUCK1,
+ .name = "DA9217 BUCK1",
+ .of_match = "buck1",
+ .of_parse_cb = da9121_of_parse_cb,
+ .owner = THIS_MODULE,
+ .regulators_node = of_match_ptr("regulators"),
+ .of_map_mode = da9121_map_mode,
+ .ops = &da9121_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = DA9121_N_VOLTAGES,
+ .min_uV = DA9121_MIN_MV * 1000,
+ .uV_step = DA9121_STEP_MV * 1000,
+ .linear_min_sel = DA9121_MIN_SEL,
+ .enable_reg = DA9121_REG_BUCK_BUCK1_0,
+ .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN,
+ .vsel_reg = DA9121_REG_BUCK_BUCK1_5,
+ .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT,
+};
+
+static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = {
+ [DA9121_TYPE_DA9121_DA9130] = { &da9121_reg, NULL },
+ [DA9121_TYPE_DA9220_DA9132] = { &da9220_reg[0], &da9220_reg[1] },
+ [DA9121_TYPE_DA9122_DA9131] = { &da9122_reg[0], &da9122_reg[1] },
+ [DA9121_TYPE_DA9217] = { &da9217_reg, NULL },
+};
+
+static void da9121_status_poll_on(struct work_struct *work)
+{
+ struct da9121 *chip = container_of(work, struct da9121, work.work);
+ int status[3] = {0};
+ int clear[3] = {0};
+ unsigned long delay;
+ int i;
+ int ret;
+
+ ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, status, 2);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to read STATUS registers: %d\n", ret);
+ goto error;
+ }
+
+ /* Possible events are tested to be within range for the variant, potentially
+ * masked by the IRQ handler (not just warned about), as having been masked,
+ * and the respective state cleared - then flagged to unmask for next IRQ.
+ */
+ for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) {
+ const struct status_event_data *item = &status_event_handling[i];
+ int reg_idx = item->reg_index;
+ bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks);
+ bool supported = (item->warn == NULL);
+ bool persisting = (chip->persistent[reg_idx] & item->event_bit);
+ bool now_cleared = !(status[reg_idx] & item->status_bit);
+
+ if (relevant && supported && persisting && now_cleared) {
+ clear[reg_idx] |= item->mask_bit;
+ chip->persistent[reg_idx] &= ~item->event_bit;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (clear[i]) {
+ unsigned int reg = DA9121_REG_SYS_MASK_0 + i;
+ unsigned int mbit = clear[i];
+
+ ret = regmap_update_bits(chip->regmap, reg, mbit, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to unmask 0x%02x %d\n",
+ reg, ret);
+ goto error;
+ }
+ }
+ }
+
+ if (chip->persistent[0] | chip->persistent[1]) {
+ delay = msecs_to_jiffies(chip->passive_delay);
+ queue_delayed_work(system_freezable_wq, &chip->work, delay);
+ }
+
+error:
+ return;
+}
+
+static irqreturn_t da9121_irq_handler(int irq, void *data)
+{
+ struct da9121 *chip = data;
+ struct regulator_dev *rdev;
+ int event[3] = {0};
+ int handled[3] = {0};
+ int mask[3] = {0};
+ int ret = IRQ_NONE;
+ int i;
+ int err;
+
+ err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, event, 3);
+ if (err < 0) {
+ dev_err(chip->dev, "Failed to read EVENT registers %d\n", err);
+ ret = IRQ_NONE;
+ goto error;
+ }
+
+ err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0, mask, 3);
+ if (err < 0) {
+ dev_err(chip->dev,
+ "Failed to read MASK registers: %d\n", ret);
+ ret = IRQ_NONE;
+ goto error;
+ }
+
+ rdev = chip->rdev[DA9121_IDX_BUCK1];
+
+ /* Possible events are tested to be within range for the variant, currently
+ * enabled, and having triggered this IRQ. The event may then be notified,
+ * or a warning given for unexpected events - those from device POR, and
+ * currently unsupported GPIO configurations.
+ */
+ for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) {
+ const struct status_event_data *item = &status_event_handling[i];
+ int reg_idx = item->reg_index;
+ bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks);
+ bool enabled = !(mask[reg_idx] & item->mask_bit);
+ bool active = (event[reg_idx] & item->event_bit);
+ bool notify = (item->warn == NULL);
+
+ if (relevant && enabled && active) {
+ if (notify) {
+ chip->persistent[reg_idx] |= item->event_bit;
+ regulator_notifier_call_chain(rdev, item->notification, NULL);
+ } else {
+ dev_warn(chip->dev, item->warn);
+ handled[reg_idx] |= item->event_bit;
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (event[i] != handled[i]) {
+ dev_warn(chip->dev,
+ "Unhandled event(s) in bank%d 0x%02x\n", i,
+ event[i] ^ handled[i]);
+ }
+ }
+
+ /* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */
+ for (i = 0; i < 2; i++) {
+ if (handled[i]) {
+ unsigned int reg = DA9121_REG_SYS_MASK_0 + i;
+ unsigned int mbit = handled[i];
+
+ err = regmap_update_bits(chip->regmap, reg, mbit, mbit);
+ if (err < 0) {
+ dev_err(chip->dev,
+ "Failed to mask 0x%02x interrupt %d\n",
+ reg, err);
+ ret = IRQ_NONE;
+ goto error;
+ }
+ }
+ }
+
+ /* clear the events */
+ if (handled[0] | handled[1] | handled[2]) {
+ err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, handled, 3);
+ if (err < 0) {
+ dev_err(chip->dev, "Fail to write EVENTs %d\n", err);
+ ret = IRQ_NONE;
+ goto error;
+ }
+ }
+
+ queue_delayed_work(system_freezable_wq, &chip->work, 0);
+error:
+ return ret;
+}
+
+static int da9121_set_regulator_config(struct da9121 *chip)
+{
+ struct regulator_config config = { };
+ unsigned int max_matches = variant_parameters[chip->variant_id].num_bucks;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < max_matches; i++) {
+ const struct regulator_desc *regl_desc =
+ local_da9121_regulators[chip->variant_id][i];
+
+ config.dev = chip->dev;
+ config.driver_data = chip;
+ config.regmap = chip->regmap;
+
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ regl_desc, &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev, "Failed to register regulator %s, %d/%d\n",
+ regl_desc->name, (i+1), max_matches);
+ ret = PTR_ERR(chip->rdev[i]);
+ goto error;
+ }
+ }
+
+error:
+ return ret;
+}
+
+/* DA9121 chip register model */
+static const struct regmap_range da9121_1ch_readable_ranges[] = {
+ regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3),
+ regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3),
+ regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6),
+ regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID),
+};
+
+static const struct regmap_access_table da9121_1ch_readable_table = {
+ .yes_ranges = da9121_1ch_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9121_1ch_readable_ranges),
+};
+
+static const struct regmap_range da9121_2ch_readable_ranges[] = {
+ regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3),
+ regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3),
+ regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_7),
+ regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_7),
+ regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID),
+};
+
+static const struct regmap_access_table da9121_2ch_readable_table = {
+ .yes_ranges = da9121_2ch_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9121_2ch_readable_ranges),
+};
+
+static const struct regmap_range da9121_1ch_writeable_ranges[] = {
+ regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3),
+ regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3),
+ regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_6),
+};
+
+static const struct regmap_access_table da9121_1ch_writeable_table = {
+ .yes_ranges = da9121_1ch_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9121_1ch_writeable_ranges),
+};
+
+static const struct regmap_range da9121_2ch_writeable_ranges[] = {
+ regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3),
+ regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3),
+ regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_7),
+ regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_2),
+ regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_4, DA9xxx_REG_BUCK_BUCK2_7),
+};
+
+static const struct regmap_access_table da9121_2ch_writeable_table = {
+ .yes_ranges = da9121_2ch_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9121_2ch_writeable_ranges),
+};
+
+
+static const struct regmap_range da9121_volatile_ranges[] = {
+ regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_EVENT_2),
+ regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1),
+ regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6),
+};
+
+static const struct regmap_access_table da9121_volatile_table = {
+ .yes_ranges = da9121_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9121_volatile_ranges),
+};
+
+/* DA9121 regmap config for 1 channel variants */
+static struct regmap_config da9121_1ch_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = DA9121_REG_OTP_CONFIG_ID,
+ .rd_table = &da9121_1ch_readable_table,
+ .wr_table = &da9121_1ch_writeable_table,
+ .volatile_table = &da9121_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/* DA9121 regmap config for 2 channel variants */
+static struct regmap_config da9121_2ch_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = DA9121_REG_OTP_CONFIG_ID,
+ .rd_table = &da9121_2ch_readable_table,
+ .wr_table = &da9121_2ch_writeable_table,
+ .volatile_table = &da9121_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip)
+{
+ u32 device_id;
+ u8 chip_id = chip->variant_id;
+ u32 variant_id;
+ u8 variant_mrc, variant_vrc;
+ char *type;
+ bool config_match = false;
+ int ret = 0;
+
+ ret = regmap_read(chip->regmap, DA9121_REG_OTP_DEVICE_ID, &device_id);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read device ID: %d\n", ret);
+ goto error;
+ }
+
+ ret = regmap_read(chip->regmap, DA9121_REG_OTP_VARIANT_ID, &variant_id);
+ if (ret < 0) {
+ dev_err(chip->dev, "Cannot read variant ID: %d\n", ret);
+ goto error;
+ }
+
+ if (device_id != DA9121_DEVICE_ID) {
+ dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id);
+ ret = -ENODEV;
+ goto error;
+ }
+
+ variant_vrc = variant_id & DA9121_MASK_OTP_VARIANT_ID_VRC;
+
+ switch (variant_vrc) {
+ case DA9121_VARIANT_VRC:
+ type = "DA9121/DA9130";
+ config_match = (chip_id == DA9121_TYPE_DA9121_DA9130);
+ break;
+ case DA9220_VARIANT_VRC:
+ type = "DA9220/DA9132";
+ config_match = (chip_id == DA9121_TYPE_DA9220_DA9132);
+ break;
+ case DA9122_VARIANT_VRC:
+ type = "DA9122/DA9131";
+ config_match = (chip_id == DA9121_TYPE_DA9122_DA9131);
+ break;
+ case DA9217_VARIANT_VRC:
+ type = "DA9217";
+ config_match = (chip_id == DA9121_TYPE_DA9217);
+ break;
+ default:
+ type = "Unknown";
+ break;
+ }
+
+ dev_info(chip->dev,
+ "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n",
+ device_id, variant_id, type);
+
+ if (!config_match) {
+ dev_err(chip->dev, "Device tree configuration does not match detected device.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ variant_mrc = (variant_id & DA9121_MASK_OTP_VARIANT_ID_MRC)
+ >> DA9121_SHIFT_OTP_VARIANT_ID_MRC;
+
+ if ((device_id == DA9121_DEVICE_ID) &&
+ (variant_mrc < DA9121_VARIANT_MRC_BASE)) {
+ dev_err(chip->dev,
+ "Cannot support variant MRC: 0x%02X\n", variant_mrc);
+ ret = -EINVAL;
+ }
+error:
+ return ret;
+}
+
+static int da9121_assign_chip_model(struct i2c_client *i2c,
+ struct da9121 *chip)
+{
+ struct regmap_config *regmap;
+ int ret = 0;
+
+ chip->dev = &i2c->dev;
+
+ switch (chip->variant_id) {
+ case DA9121_TYPE_DA9121_DA9130:
+ fallthrough;
+ case DA9121_TYPE_DA9217:
+ regmap = &da9121_1ch_regmap_config;
+ break;
+ case DA9121_TYPE_DA9122_DA9131:
+ fallthrough;
+ case DA9121_TYPE_DA9220_DA9132:
+ regmap = &da9121_2ch_regmap_config;
+ break;
+ }
+
+ /* Set these up for of_regulator_match call which may want .of_map_modes */
+ da9121_matches[0].desc = local_da9121_regulators[chip->variant_id][0];
+ da9121_matches[1].desc = local_da9121_regulators[chip->variant_id][1];
+
+ chip->regmap = devm_regmap_init_i2c(i2c, regmap);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to configure a register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = da9121_check_device_type(i2c, chip);
+
+ return ret;
+}
+
+static int da9121_config_irq(struct i2c_client *i2c,
+ struct da9121 *chip)
+{
+ unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS;
+ const int mask_all[4] = { 0, 0, 0xFF, 0xFF };
+ int ret = 0;
+
+ chip->chip_irq = i2c->irq;
+
+ if (chip->chip_irq != 0) {
+ if (!of_property_read_u32(chip->dev->of_node,
+ "dlg,irq-polling-delay-passive-ms",
+ &p_delay)) {
+ if (p_delay < DA9121_MIN_POLLING_PERIOD_MS ||
+ p_delay > DA9121_MAX_POLLING_PERIOD_MS) {
+ dev_warn(chip->dev,
+ "Out-of-range polling period %d ms\n",
+ p_delay);
+ p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS;
+ }
+ }
+
+ chip->passive_delay = p_delay;
+
+ ret = request_threaded_irq(chip->chip_irq, NULL,
+ da9121_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "da9121", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed IRQ request: %d\n",
+ chip->chip_irq);
+ goto error;
+ }
+
+ ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to set IRQ masks: %d\n",
+ ret);
+ goto regmap_error;
+ }
+
+ INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on);
+ dev_info(chip->dev, "Interrupt polling period set at %d ms\n",
+ chip->passive_delay);
+ }
+error:
+ return ret;
+regmap_error:
+ free_irq(chip->chip_irq, chip);
+ return ret;
+}
+
+static const struct of_device_id da9121_dt_ids[] = {
+ { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 },
+ { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 },
+ { .compatible = "dlg,da9217", .data = (void *) DA9121_TYPE_DA9217 },
+ { .compatible = "dlg,da9122", .data = (void *) DA9121_TYPE_DA9122_DA9131 },
+ { .compatible = "dlg,da9131", .data = (void *) DA9121_TYPE_DA9122_DA9131 },
+ { .compatible = "dlg,da9220", .data = (void *) DA9121_TYPE_DA9220_DA9132 },
+ { .compatible = "dlg,da9132", .data = (void *) DA9121_TYPE_DA9220_DA9132 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da9121_dt_ids);
+
+static inline int da9121_of_get_id(struct device *dev)
+{
+ const struct of_device_id *id = of_match_device(da9121_dt_ids, dev);
+
+ if (!id) {
+ dev_err(dev, "%s: Failed\n", __func__);
+ return -EINVAL;
+ }
+ return (uintptr_t)id->data;
+}
+
+static int da9121_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9121 *chip;
+ const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
+ int ret = 0;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL);
+ if (!chip) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ chip->pdata = i2c->dev.platform_data;
+ chip->variant_id = da9121_of_get_id(&i2c->dev);
+
+ ret = da9121_assign_chip_model(i2c, chip);
+ if (ret < 0)
+ goto error;
+
+ ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret);
+ goto error;
+ }
+
+ ret = da9121_set_regulator_config(chip);
+ if (ret < 0)
+ goto error;
+
+ ret = da9121_config_irq(i2c, chip);
+
+error:
+ return ret;
+}
+
+static int da9121_i2c_remove(struct i2c_client *i2c)
+{
+ struct da9121 *chip = i2c_get_clientdata(i2c);
+ const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
+ int ret = 0;
+
+ free_irq(chip->chip_irq, chip);
+ cancel_delayed_work_sync(&chip->work);
+
+ ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4);
+ if (ret != 0)
+ dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret);
+ return ret;
+}
+
+static const struct i2c_device_id da9121_i2c_id[] = {
+ {"da9121", DA9121_TYPE_DA9121_DA9130},
+ {"da9130", DA9121_TYPE_DA9121_DA9130},
+ {"da9217", DA9121_TYPE_DA9217},
+ {"da9122", DA9121_TYPE_DA9122_DA9131},
+ {"da9131", DA9121_TYPE_DA9122_DA9131},
+ {"da9220", DA9121_TYPE_DA9220_DA9132},
+ {"da9132", DA9121_TYPE_DA9220_DA9132},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, da9121_i2c_id);
+
+static struct i2c_driver da9121_regulator_driver = {
+ .driver = {
+ .name = "da9121",
+ .of_match_table = of_match_ptr(da9121_dt_ids),
+ },
+ .probe = da9121_i2c_probe,
+ .remove = da9121_i2c_remove,
+ .id_table = da9121_i2c_id,
+};
+
+module_i2c_driver(da9121_regulator_driver);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/da9121-regulator.h b/drivers/regulator/da9121-regulator.h
new file mode 100644
index 000000000000..3c34cb889ca8
--- /dev/null
+++ b/drivers/regulator/da9121-regulator.h
@@ -0,0 +1,291 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * DA9121 Single-channel dual-phase 10A buck converter
+ * DA9130 Single-channel dual-phase 10A buck converter (Automotive)
+ * DA9217 Single-channel dual-phase 6A buck converter
+ * DA9122 Dual-channel single-phase 5A buck converter
+ * DA9131 Dual-channel single-phase 5A buck converter (Automotive)
+ * DA9220 Dual-channel single-phase 3A buck converter
+ * DA9132 Dual-channel single-phase 3A buck converter (Automotive)
+ *
+ * Copyright (C) 2020 Dialog Semiconductor
+ *
+ * Authors: Steve Twiss, Dialog Semiconductor
+ * Adam Ward, Dialog Semiconductor
+ */
+
+#ifndef __DA9121_REGISTERS_H__
+#define __DA9121_REGISTERS_H__
+
+/* Values for: DA9121_REG_BUCK_BUCKx_4 registers, fields CHx_y_MODE
+ * DA9121_REG_BUCK_BUCKx_7 registers, fields CHx_RIPPLE_CANCEL
+ */
+#include <dt-bindings/regulator/dlg,da9121-regulator.h>
+
+enum da9121_variant {
+ DA9121_TYPE_DA9121_DA9130,
+ DA9121_TYPE_DA9220_DA9132,
+ DA9121_TYPE_DA9122_DA9131,
+ DA9121_TYPE_DA9217
+};
+
+/* Minimum, maximum and default polling millisecond periods are provided
+ * here as an example. It is expected that any final implementation will
+ * include a modification of these settings to match the required
+ * application.
+ */
+#define DA9121_DEFAULT_POLLING_PERIOD_MS 3000
+#define DA9121_MAX_POLLING_PERIOD_MS 10000
+#define DA9121_MIN_POLLING_PERIOD_MS 1000
+
+/* Registers */
+
+#define DA9121_REG_SYS_STATUS_0 0x01
+#define DA9121_REG_SYS_STATUS_1 0x02
+#define DA9121_REG_SYS_STATUS_2 0x03
+#define DA9121_REG_SYS_EVENT_0 0x04
+#define DA9121_REG_SYS_EVENT_1 0x05
+#define DA9121_REG_SYS_EVENT_2 0x06
+#define DA9121_REG_SYS_MASK_0 0x07
+#define DA9121_REG_SYS_MASK_1 0x08
+#define DA9121_REG_SYS_MASK_2 0x09
+#define DA9121_REG_SYS_MASK_3 0x0A
+#define DA9121_REG_SYS_CONFIG_0 0x0B
+#define DA9121_REG_SYS_CONFIG_1 0x0C
+#define DA9121_REG_SYS_CONFIG_2 0x0D
+#define DA9121_REG_SYS_CONFIG_3 0x0E
+#define DA9121_REG_SYS_GPIO0_0 0x10
+#define DA9121_REG_SYS_GPIO0_1 0x11
+#define DA9121_REG_SYS_GPIO1_0 0x12
+#define DA9121_REG_SYS_GPIO1_1 0x13
+#define DA9121_REG_SYS_GPIO2_0 0x14
+#define DA9121_REG_SYS_GPIO2_1 0x15
+#define DA9121_REG_BUCK_BUCK1_0 0x20
+#define DA9121_REG_BUCK_BUCK1_1 0x21
+#define DA9121_REG_BUCK_BUCK1_2 0x22
+#define DA9121_REG_BUCK_BUCK1_3 0x23
+#define DA9121_REG_BUCK_BUCK1_4 0x24
+#define DA9121_REG_BUCK_BUCK1_5 0x25
+#define DA9121_REG_BUCK_BUCK1_6 0x26
+#define DA9121_REG_BUCK_BUCK1_7 0x27
+#define DA9xxx_REG_BUCK_BUCK2_0 0x28
+#define DA9xxx_REG_BUCK_BUCK2_1 0x29
+#define DA9xxx_REG_BUCK_BUCK2_2 0x2A
+#define DA9xxx_REG_BUCK_BUCK2_3 0x2B
+#define DA9xxx_REG_BUCK_BUCK2_4 0x2C
+#define DA9xxx_REG_BUCK_BUCK2_5 0x2D
+#define DA9xxx_REG_BUCK_BUCK2_6 0x2E
+#define DA9xxx_REG_BUCK_BUCK2_7 0x2F
+#define DA9121_REG_OTP_DEVICE_ID 0x48
+#define DA9121_REG_OTP_VARIANT_ID 0x49
+#define DA9121_REG_OTP_CUSTOMER_ID 0x4A
+#define DA9121_REG_OTP_CONFIG_ID 0x4B
+
+/* Register bits */
+
+/* DA9121_REG_SYS_STATUS_0 */
+
+#define DA9xxx_MASK_SYS_STATUS_0_SG BIT(2)
+#define DA9121_MASK_SYS_STATUS_0_TEMP_CRIT BIT(1)
+#define DA9121_MASK_SYS_STATUS_0_TEMP_WARN BIT(0)
+
+/* DA9121_REG_SYS_STATUS_1 */
+
+#define DA9xxx_MASK_SYS_STATUS_1_PG2 BIT(7)
+#define DA9xxx_MASK_SYS_STATUS_1_OV2 BIT(6)
+#define DA9xxx_MASK_SYS_STATUS_1_UV2 BIT(5)
+#define DA9xxx_MASK_SYS_STATUS_1_OC2 BIT(4)
+#define DA9121_MASK_SYS_STATUS_1_PG1 BIT(3)
+#define DA9121_MASK_SYS_STATUS_1_OV1 BIT(2)
+#define DA9121_MASK_SYS_STATUS_1_UV1 BIT(1)
+#define DA9121_MASK_SYS_STATUS_1_OC1 BIT(0)
+
+/* DA9121_REG_SYS_STATUS_2 */
+
+#define DA9121_MASK_SYS_STATUS_2_GPIO2 BIT(2)
+#define DA9121_MASK_SYS_STATUS_2_GPIO1 BIT(1)
+#define DA9121_MASK_SYS_STATUS_2_GPIO0 BIT(0)
+
+/* DA9121_REG_SYS_EVENT_0 */
+
+#define DA9xxx_MASK_SYS_EVENT_0_E_SG BIT(2)
+#define DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT BIT(1)
+#define DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN BIT(0)
+
+/* DA9121_REG_SYS_EVENT_1 */
+
+#define DA9xxx_MASK_SYS_EVENT_1_E_PG2 BIT(7)
+#define DA9xxx_MASK_SYS_EVENT_1_E_OV2 BIT(6)
+#define DA9xxx_MASK_SYS_EVENT_1_E_UV2 BIT(5)
+#define DA9xxx_MASK_SYS_EVENT_1_E_OC2 BIT(4)
+#define DA9121_MASK_SYS_EVENT_1_E_PG1 BIT(3)
+#define DA9121_MASK_SYS_EVENT_1_E_OV1 BIT(2)
+#define DA9121_MASK_SYS_EVENT_1_E_UV1 BIT(1)
+#define DA9121_MASK_SYS_EVENT_1_E_OC1 BIT(0)
+
+/* DA9121_REG_SYS_EVENT_2 */
+
+#define DA9121_MASK_SYS_EVENT_2_E_GPIO2 BIT(2)
+#define DA9121_MASK_SYS_EVENT_2_E_GPIO1 BIT(1)
+#define DA9121_MASK_SYS_EVENT_2_E_GPIO0 BIT(0)
+
+/* DA9121_REG_SYS_MASK_0 */
+
+#define DA9xxx_MASK_SYS_MASK_0_M_SG BIT(2)
+#define DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT BIT(1)
+#define DA9121_MASK_SYS_MASK_0_M_TEMP_WARN BIT(0)
+
+/* DA9121_REG_SYS_MASK_1 */
+
+#define DA9xxx_MASK_SYS_MASK_1_M_PG2 BIT(7)
+#define DA9xxx_MASK_SYS_MASK_1_M_OV2 BIT(6)
+#define DA9xxx_MASK_SYS_MASK_1_M_UV2 BIT(5)
+#define DA9xxx_MASK_SYS_MASK_1_M_OC2 BIT(4)
+#define DA9121_MASK_SYS_MASK_1_M_PG1 BIT(3)
+#define DA9121_MASK_SYS_MASK_1_M_OV1 BIT(2)
+#define DA9121_MASK_SYS_MASK_1_M_UV1 BIT(1)
+#define DA9121_MASK_SYS_MASK_1_M_OC1 BIT(0)
+
+/* DA9121_REG_SYS_MASK_2 */
+
+#define DA9121_MASK_SYS_MASK_2_M_GPIO2 BIT(2)
+#define DA9121_MASK_SYS_MASK_2_M_GPIO1 BIT(1)
+#define DA9121_MASK_SYS_MASK_2_M_GPIO0 BIT(0)
+
+/* DA9122_REG_SYS_MASK_3 */
+
+#define DA9121_MASK_SYS_MASK_3_M_VR_HOT BIT(3)
+#define DA9xxx_MASK_SYS_MASK_3_M_SG_STAT BIT(2)
+#define DA9xxx_MASK_SYS_MASK_3_M_PG2_STAT BIT(1)
+#define DA9121_MASK_SYS_MASK_3_M_PG1_STAT BIT(0)
+
+/* DA9121_REG_SYS_CONFIG_0 */
+
+#define DA9121_MASK_SYS_CONFIG_0_CH1_DIS_DLY 0xF0
+#define DA9121_MASK_SYS_CONFIG_0_CH1_EN_DLY 0x0F
+
+/* DA9xxx_REG_SYS_CONFIG_1 */
+
+#define DA9xxx_MASK_SYS_CONFIG_1_CH2_DIS_DLY 0xF0
+#define DA9xxx_MASK_SYS_CONFIG_1_CH2_EN_DLY 0x0F
+
+/* DA9121_REG_SYS_CONFIG_2 */
+
+#define DA9121_MASK_SYS_CONFIG_2_OC_LATCHOFF 0x60
+#define DA9121_MASK_SYS_CONFIG_2_OC_DVC_MASK BIT(4)
+#define DA9121_MASK_SYS_CONFIG_2_PG_DVC_MASK 0x0C
+
+/* DA9121_REG_SYS_CONFIG_3 */
+
+#define DA9121_MASK_SYS_CONFIG_3_OSC_TUNE 0X70
+#define DA9121_MASK_SYS_CONFIG_3_I2C_TIMEOUT BIT(1)
+
+/* DA9121_REG_SYS_GPIO0_0 */
+
+#define DA9121_MASK_SYS_GPIO0_0_GPIO0_MODE 0X1E
+#define DA9121_MASK_SYS_GPIO0_0_GPIO0_OBUF BIT(0)
+
+/* DA9121_REG_SYS_GPIO0_1 */
+
+#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_FALL BIT(7)
+#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_RISE BIT(6)
+#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB 0x30
+#define DA9121_MASK_SYS_GPIO0_1_GPIO0_PUPD BIT(3)
+#define DA9121_MASK_SYS_GPIO0_1_GPIO0_POL BIT(2)
+#define DA9121_MASK_SYS_GPIO0_1_GPIO0_TRIG 0x03
+
+/* DA9121_REG_SYS_GPIO1_0 */
+
+#define DA9121_MASK_SYS_GPIO1_0_GPIO1_MODE 0x1E
+#define DA9121_MASK_SYS_GPIO1_0_GPIO1_OBUF BIT(0)
+
+/* DA9121_REG_SYS_GPIO1_1 */
+
+#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_FALL BIT(7)
+#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_RISE BIT(6)
+#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB 0x30
+#define DA9121_MASK_SYS_GPIO1_1_GPIO1_PUPD BIT(3)
+#define DA9121_MASK_SYS_GPIO1_1_GPIO1_POL BIT(2)
+#define DA9121_MASK_SYS_GPIO1_1_GPIO1_TRIG 0x03
+
+/* DA9121_REG_SYS_GPIO2_0 */
+
+#define DA9121_MASK_SYS_GPIO2_0_GPIO2_MODE 0x1E
+#define DA9121_MASK_SYS_GPIO2_0_GPIO2_OBUF BIT(0)
+
+/* DA9121_REG_SYS_GPIO2_1 */
+
+#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_FALL BIT(7)
+#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_RISE BIT(6)
+#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB 0x30
+#define DA9121_MASK_SYS_GPIO2_1_GPIO2_PUPD BIT(3)
+#define DA9121_MASK_SYS_GPIO2_1_GPIO2_POL BIT(2)
+#define DA9121_MASK_SYS_GPIO2_1_GPIO2_TRIG 0x03
+
+/* DA9121_REG_BUCK_BUCK1_0 / DA9xxx_REG_BUCK_BUCK2_0 */
+
+#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_DWN 0x70
+#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_UP 0x0E
+#define DA9121_MASK_BUCK_BUCKx_0_CHx_EN BIT(0)
+
+/* DA9121_REG_BUCK_BUCK1_1 / DA9xxx_REG_BUCK_BUCK2_1 */
+
+#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_SHDN 0x70
+#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_STARTUP 0x0E
+#define DA9121_MASK_BUCK_BUCKx_1_CHx_PD_DIS BIT(0)
+
+/* DA9121_REG_BUCK_BUCK1_2 / DA9xxx_REG_BUCK_BUCK2_2 */
+
+#define DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM 0x0F
+
+/* DA9121_REG_BUCK_BUCK1_3 / DA9xxx_REG_BUCK_BUCK2_3 */
+
+#define DA9121_MASK_BUCK_BUCKx_3_CHx_VMAX 0xFF
+
+/* DA9121_REG_BUCK_BUCK1_4 / DA9xxx_REG_BUCK_BUCK2_4 */
+
+#define DA9121_MASK_BUCK_BUCKx_4_CHx_VSEL BIT(4)
+#define DA9121_MASK_BUCK_BUCKx_4_CHx_B_MODE 0x0C
+#define DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE 0x03
+
+/* DA9121_REG_BUCK_BUCK1_5 / DA9xxx_REG_BUCK_BUCK2_5 */
+
+#define DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT 0xFF
+
+/* DA9121_REG_BUCK_BUCK1_6 / DA9xxx_REG_BUCK_BUCK2_6 */
+
+#define DA9121_MASK_BUCK_BUCKx_6_CHx_B_VOUT 0xFF
+
+/* DA9121_REG_BUCK_BUCK1_7 / DA9xxx_REG_BUCK_BUCK2_7 */
+
+#define DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL 0x03
+
+
+/* DA9121_REG_OTP_DEVICE_ID */
+
+#define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF
+
+#define DA9121_DEVICE_ID 0x05
+
+/* DA9121_REG_OTP_VARIANT_ID */
+
+#define DA9121_SHIFT_OTP_VARIANT_ID_MRC 4
+#define DA9121_MASK_OTP_VARIANT_ID_MRC 0xF0
+#define DA9121_SHIFT_OTP_VARIANT_ID_VRC 0
+#define DA9121_MASK_OTP_VARIANT_ID_VRC 0x0F
+
+#define DA9121_VARIANT_MRC_BASE 0x2
+#define DA9121_VARIANT_VRC 0x1
+#define DA9220_VARIANT_VRC 0x0
+#define DA9122_VARIANT_VRC 0x2
+#define DA9217_VARIANT_VRC 0x7
+
+/* DA9121_REG_OTP_CUSTOMER_ID */
+
+#define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF
+
+/* DA9121_REG_OTP_CONFIG_ID */
+
+#define DA9121_MASK_OTP_CONFIG_ID_CONFIG_REV 0xFF
+
+#endif /* __DA9121_REGISTERS_H__ */
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index 3de7709bdcd4..02ad83153e19 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -18,6 +18,8 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/fixed.h>
#include <linux/gpio/consumer.h>
@@ -34,11 +36,13 @@ struct fixed_voltage_data {
struct regulator_dev *dev;
struct clk *enable_clock;
- unsigned int clk_enable_counter;
+ unsigned int enable_counter;
+ int performance_state;
};
struct fixed_dev_type {
bool has_enable_clock;
+ bool has_performance_state;
};
static int reg_clock_enable(struct regulator_dev *rdev)
@@ -50,7 +54,7 @@ static int reg_clock_enable(struct regulator_dev *rdev)
if (ret)
return ret;
- priv->clk_enable_counter++;
+ priv->enable_counter++;
return ret;
}
@@ -60,16 +64,41 @@ static int reg_clock_disable(struct regulator_dev *rdev)
struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
clk_disable_unprepare(priv->enable_clock);
- priv->clk_enable_counter--;
+ priv->enable_counter--;
return 0;
}
-static int reg_clock_is_enabled(struct regulator_dev *rdev)
+static int reg_domain_enable(struct regulator_dev *rdev)
{
struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
+ struct device *dev = rdev->dev.parent;
+ int ret;
+
+ ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state);
+ if (ret)
+ return ret;
- return priv->clk_enable_counter > 0;
+ priv->enable_counter++;
+
+ return ret;
+}
+
+static int reg_domain_disable(struct regulator_dev *rdev)
+{
+ struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
+ struct device *dev = rdev->dev.parent;
+
+ priv->enable_counter--;
+
+ return dev_pm_genpd_set_performance_state(dev, 0);
+}
+
+static int reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
+
+ return priv->enable_counter > 0;
}
@@ -129,7 +158,13 @@ static const struct regulator_ops fixed_voltage_ops = {
static const struct regulator_ops fixed_voltage_clkenabled_ops = {
.enable = reg_clock_enable,
.disable = reg_clock_disable,
- .is_enabled = reg_clock_is_enabled,
+ .is_enabled = reg_is_enabled,
+};
+
+static const struct regulator_ops fixed_voltage_domain_ops = {
+ .enable = reg_domain_enable,
+ .disable = reg_domain_disable,
+ .is_enabled = reg_is_enabled,
};
static int reg_fixed_voltage_probe(struct platform_device *pdev)
@@ -177,6 +212,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev)
dev_err(dev, "Can't get enable-clock from devicetree\n");
return -ENOENT;
}
+ } else if (drvtype && drvtype->has_performance_state) {
+ drvdata->desc.ops = &fixed_voltage_domain_ops;
+
+ drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0);
+ if (drvdata->performance_state < 0) {
+ dev_err(dev, "Can't get performance state from devicetree\n");
+ return drvdata->performance_state;
+ }
} else {
drvdata->desc.ops = &fixed_voltage_ops;
}
@@ -260,6 +303,10 @@ static const struct fixed_dev_type fixed_clkenable_data = {
.has_enable_clock = true,
};
+static const struct fixed_dev_type fixed_domain_data = {
+ .has_performance_state = true,
+};
+
static const struct of_device_id fixed_of_match[] = {
{
.compatible = "regulator-fixed",
@@ -270,6 +317,10 @@ static const struct of_device_id fixed_of_match[] = {
.data = &fixed_clkenable_data,
},
{
+ .compatible = "regulator-fixed-domain",
+ .data = &fixed_domain_data,
+ },
+ {
},
};
MODULE_DEVICE_TABLE(of, fixed_of_match);
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index e4bb09bbd3fa..f42b394a0c46 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -649,6 +649,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev,
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
+ if (selector < rdev->desc->linear_min_sel)
+ return 0;
return rdev->desc->volt_table[selector];
}
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c
index 303d7e2dc838..e84be29533f4 100644
--- a/drivers/regulator/lp872x.c
+++ b/drivers/regulator/lp872x.c
@@ -892,7 +892,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
struct lp872x *lp;
struct lp872x_platform_data *pdata;
int ret;
- const int lp872x_num_regulators[] = {
+ static const int lp872x_num_regulators[] = {
[LP8720] = LP8720_NUM_REGULATORS,
[LP8725] = LP8725_NUM_REGULATORS,
};
diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c
index e34face736f4..1d78b455cc48 100644
--- a/drivers/regulator/max14577-regulator.c
+++ b/drivers/regulator/max14577-regulator.c
@@ -269,3 +269,5 @@ module_exit(max14577_regulator_exit);
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max14577-regulator");
+MODULE_ALIAS("platform:max77836-regulator");
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index a731e826a037..5221f7a9df91 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -582,8 +582,8 @@ static int mc13892_regulator_probe(struct platform_device *pdev)
/* update mc13892_vcam ops */
memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops,
sizeof(struct regulator_ops));
- mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode,
- mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode,
+ mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode;
+ mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode;
mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops;
mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators,
diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c
index 6d0ad74935b3..74ad92dc664a 100644
--- a/drivers/regulator/mcp16502.c
+++ b/drivers/regulator/mcp16502.c
@@ -22,8 +22,9 @@
#define VDD_LOW_SEL 0x0D
#define VDD_HIGH_SEL 0x3F
-#define MCP16502_FLT BIT(7)
-#define MCP16502_ENS BIT(0)
+#define MCP16502_FLT BIT(7)
+#define MCP16502_DVSR GENMASK(3, 2)
+#define MCP16502_ENS BIT(0)
/*
* The PMIC has four sets of registers corresponding to four power modes:
@@ -54,13 +55,9 @@
* This function is useful for iterating over all regulators and accessing their
* registers in a generic way or accessing a regulator device by its id.
*/
-#define MCP16502_BASE(i) (((i) + 1) << 4)
+#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r)
#define MCP16502_STAT_BASE(i) ((i) + 5)
-#define MCP16502_OFFSET_MODE_A 0
-#define MCP16502_OFFSET_MODE_LPM 1
-#define MCP16502_OFFSET_MODE_HIB 2
-
#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL
#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE
#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY
@@ -75,6 +72,29 @@
#define MCP16502_MIN_REG 0x0
#define MCP16502_MAX_REG 0x65
+/**
+ * enum mcp16502_reg - MCP16502 regulators's registers
+ * @MCP16502_REG_A: active state register
+ * @MCP16502_REG_LPM: low power mode state register
+ * @MCP16502_REG_HIB: hibernate state register
+ * @MCP16502_REG_SEQ: startup sequence register
+ * @MCP16502_REG_CFG: configuration register
+ */
+enum mcp16502_reg {
+ MCP16502_REG_A,
+ MCP16502_REG_LPM,
+ MCP16502_REG_HIB,
+ MCP16502_REG_HPM,
+ MCP16502_REG_SEQ,
+ MCP16502_REG_CFG,
+};
+
+/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */
+static const int mcp16502_ramp_b1l12[] = { 6250, 3125, 2083, 1563 };
+
+/* Ramp delay (uV/us) for buck2, buck3, buck4. */
+static const int mcp16502_ramp_b234[] = { 3125, 1563, 1042, 781 };
+
static unsigned int mcp16502_of_map_mode(unsigned int mode)
{
if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE)
@@ -93,6 +113,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode)
.owner = THIS_MODULE, \
.n_voltages = MCP16502_VSEL + 1, \
.linear_ranges = _ranges, \
+ .linear_min_sel = VDD_LOW_SEL, \
.n_linear_ranges = ARRAY_SIZE(_ranges), \
.of_match = of_match_ptr(_name), \
.of_map_mode = mcp16502_of_map_mode, \
@@ -114,8 +135,6 @@ enum {
/*
* struct mcp16502 - PMIC representation
- * @rdev: the regulators belonging to this chip
- * @rmap: regmap to be used for I2C communication
* @lpm: LPM GPIO descriptor
*/
struct mcp16502 {
@@ -143,22 +162,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode)
}
/*
- * mcp16502_get_reg() - get the PMIC's configuration register for opmode
+ * mcp16502_get_reg() - get the PMIC's state configuration register for opmode
*
* @rdev: the regulator whose register we are searching
* @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate
*/
-static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode)
+static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode)
{
- int reg = MCP16502_BASE(rdev_get_id(rdev));
-
switch (opmode) {
case MCP16502_OPMODE_ACTIVE:
- return reg + MCP16502_OFFSET_MODE_A;
+ return MCP16502_REG_BASE(rdev_get_id(rdev), A);
case MCP16502_OPMODE_LPM:
- return reg + MCP16502_OFFSET_MODE_LPM;
+ return MCP16502_REG_BASE(rdev_get_id(rdev), LPM);
case MCP16502_OPMODE_HIB:
- return reg + MCP16502_OFFSET_MODE_HIB;
+ return MCP16502_REG_BASE(rdev_get_id(rdev), HIB);
default:
return -EINVAL;
}
@@ -178,7 +195,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev)
unsigned int val;
int ret, reg;
- reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE);
+ reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE);
if (reg < 0)
return reg;
@@ -209,7 +226,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode,
int val;
int reg;
- reg = mcp16502_get_reg(rdev, op_mode);
+ reg = mcp16502_get_state_reg(rdev, op_mode);
if (reg < 0)
return reg;
@@ -259,6 +276,80 @@ static int mcp16502_get_status(struct regulator_dev *rdev)
return REGULATOR_STATUS_UNDEFINED;
}
+static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_sel,
+ unsigned int new_sel)
+{
+ static const u8 us_ramp[] = { 8, 16, 24, 32 };
+ int id = rdev_get_id(rdev);
+ unsigned int uV_delta, val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val);
+ if (ret)
+ return ret;
+
+ val = (val & MCP16502_DVSR) >> 2;
+ uV_delta = abs(new_sel * rdev->desc->linear_ranges->step -
+ old_sel * rdev->desc->linear_ranges->step);
+ switch (id) {
+ case BUCK1:
+ case LDO1:
+ case LDO2:
+ ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val],
+ mcp16502_ramp_b1l12[val]);
+ break;
+
+ case BUCK2:
+ case BUCK3:
+ case BUCK4:
+ ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val],
+ mcp16502_ramp_b234[val]);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mcp16502_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ const int *ramp;
+ int id = rdev_get_id(rdev);
+ unsigned int i, size;
+
+ switch (id) {
+ case BUCK1:
+ case LDO1:
+ case LDO2:
+ ramp = mcp16502_ramp_b1l12;
+ size = ARRAY_SIZE(mcp16502_ramp_b1l12);
+ break;
+
+ case BUCK2:
+ case BUCK3:
+ case BUCK4:
+ ramp = mcp16502_ramp_b234;
+ size = ARRAY_SIZE(mcp16502_ramp_b234);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (ramp[i] == ramp_delay)
+ break;
+ }
+ if (i == size)
+ return -EINVAL;
+
+ return regmap_update_bits(rdev->regmap, MCP16502_REG_BASE(id, CFG),
+ MCP16502_DVSR, (i << 2));
+}
+
#ifdef CONFIG_SUSPEND
/*
* mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC
@@ -268,10 +359,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev)
{
switch (pm_suspend_target_state) {
case PM_SUSPEND_STANDBY:
- return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM);
+ return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM);
case PM_SUSPEND_ON:
case PM_SUSPEND_MEM:
- return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB);
+ return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB);
default:
dev_err(&rdev->dev, "invalid suspend target: %d\n",
pm_suspend_target_state);
@@ -353,6 +444,8 @@ static const struct regulator_ops mcp16502_buck_ops = {
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_status = mcp16502_get_status,
+ .set_voltage_time_sel = mcp16502_set_voltage_time_sel,
+ .set_ramp_delay = mcp16502_set_ramp_delay,
.set_mode = mcp16502_set_mode,
.get_mode = mcp16502_get_mode,
@@ -377,6 +470,8 @@ static const struct regulator_ops mcp16502_ldo_ops = {
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_status = mcp16502_get_status,
+ .set_voltage_time_sel = mcp16502_set_voltage_time_sel,
+ .set_ramp_delay = mcp16502_set_ramp_delay,
#ifdef CONFIG_SUSPEND
.set_suspend_voltage = mcp16502_set_suspend_voltage,
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 06c0b15fe4c0..564f928eb1db 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -413,8 +413,12 @@ device_node *regulator_of_get_init_node(struct device *dev,
for_each_available_child_of_node(search, child) {
name = of_get_property(child, "regulator-compatible", NULL);
- if (!name)
- name = child->name;
+ if (!name) {
+ if (!desc->of_match_full_name)
+ name = child->name;
+ else
+ name = child->full_name;
+ }
if (!strcmp(desc->of_match, name)) {
of_node_put(search);
diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c
new file mode 100644
index 000000000000..308c27fa6ea8
--- /dev/null
+++ b/drivers/regulator/pf8x00-regulator.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2019 Boundary Devices
+ * Copyright (C) 2020 Amarula Solutions(India)
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/* registers */
+#define PF8X00_DEVICEID 0x00
+#define PF8X00_REVID 0x01
+#define PF8X00_EMREV 0x02
+#define PF8X00_PROGID 0x03
+#define PF8X00_IMS_INT 0x04
+#define PF8X00_IMS_THERM 0x07
+#define PF8X00_SW_MODE_INT 0x0a
+#define PF8X00_SW_MODE_MASK 0x0b
+#define PF8X00_IMS_SW_ILIM 0x12
+#define PF8X00_IMS_LDO_ILIM 0x15
+#define PF8X00_IMS_SW_UV 0x18
+#define PF8X00_IMS_SW_OV 0x1b
+#define PF8X00_IMS_LDO_UV 0x1e
+#define PF8X00_IMS_LDO_OV 0x21
+#define PF8X00_IMS_PWRON 0x24
+#define PF8X00_SYS_INT 0x27
+#define PF8X00_HARD_FAULT 0x29
+#define PF8X00_FSOB_FLAGS 0x2a
+#define PF8X00_FSOB_SELECT 0x2b
+#define PF8X00_ABIST_OV1 0x2c
+#define PF8X00_ABIST_OV2 0x2d
+#define PF8X00_ABIST_UV1 0x2e
+#define PF8X00_ABIST_UV2 0x2f
+#define PF8X00_TEST_FLAGS 0x30
+#define PF8X00_ABIST_RUN 0x31
+#define PF8X00_RANDOM_GEN 0x33
+#define PF8X00_RANDOM_CHK 0x34
+#define PF8X00_VMONEN1 0x35
+#define PF8X00_VMONEN2 0x36
+#define PF8X00_CTRL1 0x37
+#define PF8X00_CTRL2 0x38
+#define PF8X00_CTRL3 0x39
+#define PF8X00_PWRUP_CTRL 0x3a
+#define PF8X00_RESETBMCU 0x3c
+#define PF8X00_PGOOD 0x3d
+#define PF8X00_PWRDN_DLY1 0x3e
+#define PF8X00_PWRDN_DLY2 0x3f
+#define PF8X00_FREQ_CTRL 0x40
+#define PF8X00_COINCELL_CTRL 0x41
+#define PF8X00_PWRON 0x42
+#define PF8X00_WD_CONFIG 0x43
+#define PF8X00_WD_CLEAR 0x44
+#define PF8X00_WD_EXPIRE 0x45
+#define PF8X00_WD_COUNTER 0x46
+#define PF8X00_FAULT_COUNTER 0x47
+#define PF8X00_FSAFE_COUNTER 0x48
+#define PF8X00_FAULT_TIMER 0x49
+#define PF8X00_AMUX 0x4a
+#define PF8X00_SW1_CONFIG1 0x4d
+#define PF8X00_LDO1_CONFIG1 0x85
+#define PF8X00_VSNVS_CONFIG1 0x9d
+#define PF8X00_PAGE_SELECT 0x9f
+
+/* regulators */
+enum pf8x00_regulators {
+ PF8X00_LDO1,
+ PF8X00_LDO2,
+ PF8X00_LDO3,
+ PF8X00_LDO4,
+ PF8X00_BUCK1,
+ PF8X00_BUCK2,
+ PF8X00_BUCK3,
+ PF8X00_BUCK4,
+ PF8X00_BUCK5,
+ PF8X00_BUCK6,
+ PF8X00_BUCK7,
+ PF8X00_VSNVS,
+
+ PF8X00_MAX_REGULATORS,
+};
+
+enum pf8x00_buck_states {
+ SW_CONFIG1,
+ SW_CONFIG2,
+ SW_PWRUP,
+ SW_MODE1,
+ SW_RUN_VOLT,
+ SW_STBY_VOLT,
+};
+#define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1)
+
+enum pf8x00_ldo_states {
+ LDO_CONFIG1,
+ LDO_CONFIG2,
+ LDO_PWRUP,
+ LDO_RUN_VOLT,
+ LDO_STBY_VOLT,
+};
+#define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1)
+
+enum swxilim_bits {
+ SWXILIM_2100_MA,
+ SWXILIM_2600_MA,
+ SWXILIM_3000_MA,
+ SWXILIM_4500_MA,
+};
+#define PF8X00_SWXILIM_SHIFT 3
+#define PF8X00_SWXILIM_MASK GENMASK(4, 3)
+#define PF8X00_SWXPHASE_MASK GENMASK(2, 0)
+#define PF8X00_SWXPHASE_DEFAULT 0
+#define PF8X00_SWXPHASE_SHIFT 7
+
+enum pf8x00_devid {
+ PF8100 = 0x0,
+ PF8121A = BIT(1),
+ PF8200 = BIT(3),
+};
+#define PF8X00_FAM BIT(6)
+#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4)
+#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0)
+
+struct pf8x00_regulator {
+ struct regulator_desc desc;
+ u8 ilim;
+ u8 phase_shift;
+};
+
+struct pf8x00_chip {
+ struct regmap *regmap;
+ struct device *dev;
+};
+
+static const struct regmap_config pf8x00_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PF8X00_PAGE_SELECT,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/* VLDOx output: 1.5V to 5.0V */
+static const int pf8x00_ldo_voltages[] = {
+ 1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000,
+ 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000,
+};
+
+#define SWV(i) (6250 * i + 400000)
+#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \
+ SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7)
+
+/* Output: 0.4V to 1.8V */
+static const int pf8x00_sw1_to_6_voltages[] = {
+ SWV_LINE(0),
+ SWV_LINE(1),
+ SWV_LINE(2),
+ SWV_LINE(3),
+ SWV_LINE(4),
+ SWV_LINE(5),
+ SWV_LINE(6),
+ SWV_LINE(7),
+ SWV_LINE(8),
+ SWV_LINE(9),
+ SWV_LINE(10),
+ SWV_LINE(11),
+ SWV_LINE(12),
+ SWV_LINE(13),
+ SWV_LINE(14),
+ SWV_LINE(15),
+ SWV_LINE(16),
+ SWV_LINE(17),
+ SWV_LINE(18),
+ SWV_LINE(19),
+ SWV_LINE(20),
+ SWV_LINE(21),
+ 1500000, 1800000,
+};
+
+/* Output: 1.0V to 4.1V */
+static const int pf8x00_sw7_voltages[] = {
+ 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000,
+ 1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000,
+ 2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000,
+ 3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000,
+};
+
+/* Output: 1.8V, 3.0V, or 3.3V */
+static const int pf8x00_vsnvs_voltages[] = {
+ 0, 1800000, 3000000, 3300000,
+};
+
+static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc)
+{
+ return container_of(desc, struct pf8x00_regulator, desc);
+}
+
+static void swxilim_select(const struct regulator_desc *desc, int ilim)
+{
+ struct pf8x00_regulator *data = desc_to_regulator(desc);
+ u8 ilim_sel;
+
+ switch (ilim) {
+ case 2100:
+ ilim_sel = SWXILIM_2100_MA;
+ break;
+ case 2600:
+ ilim_sel = SWXILIM_2600_MA;
+ break;
+ case 3000:
+ ilim_sel = SWXILIM_3000_MA;
+ break;
+ case 4500:
+ ilim_sel = SWXILIM_4500_MA;
+ break;
+ default:
+ ilim_sel = SWXILIM_2100_MA;
+ break;
+ }
+
+ data->ilim = ilim_sel;
+}
+
+static int pf8x00_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct pf8x00_regulator *data = desc_to_regulator(desc);
+ struct pf8x00_chip *chip = config->driver_data;
+ int phase;
+ int val;
+ int ret;
+
+ ret = of_property_read_u32(np, "nxp,ilim-ma", &val);
+ if (ret)
+ dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n",
+ desc->id - PF8X00_LDO4);
+
+ swxilim_select(desc, val);
+
+ ret = of_property_read_u32(np, "nxp,phase-shift", &val);
+ if (ret) {
+ dev_dbg(chip->dev,
+ "unspecified phase-shift for BUCK%d, use 0 degrees\n",
+ desc->id - PF8X00_LDO4);
+ val = PF8X00_SWXPHASE_DEFAULT;
+ }
+
+ phase = val / 45;
+ if ((phase * 45) != val) {
+ dev_warn(config->dev,
+ "invalid phase_shift %d for BUCK%d, use 0 degrees\n",
+ (phase * 45), desc->id - PF8X00_LDO4);
+ phase = PF8X00_SWXPHASE_SHIFT;
+ }
+
+ data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT;
+
+ return 0;
+}
+
+static const struct regulator_ops pf8x00_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static const struct regulator_ops pf8x00_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static const struct regulator_ops pf8x00_vsnvs_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_ascend,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+#define PF8X00LDO(_id, _name, base, voltages) \
+ [PF8X00_LDO ## _id] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = _name, \
+ .regulators_node = "regulators", \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pf8x00_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PF8X00_LDO ## _id, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base) + LDO_RUN_VOLT, \
+ .vsel_mask = 0xff, \
+ .enable_reg = (base) + LDO_CONFIG2, \
+ .enable_val = 0x2, \
+ .disable_val = 0x0, \
+ .enable_mask = 2, \
+ }, \
+ }
+
+#define PF8X00BUCK(_id, _name, base, voltages) \
+ [PF8X00_BUCK ## _id] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = _name, \
+ .regulators_node = "regulators", \
+ .of_parse_cb = pf8x00_of_parse_cb, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pf8x00_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PF8X00_BUCK ## _id, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base) + SW_RUN_VOLT, \
+ .vsel_mask = 0xff, \
+ .enable_reg = (base) + SW_MODE1, \
+ .enable_val = 0x3, \
+ .disable_val = 0x0, \
+ .enable_mask = 0x3, \
+ .enable_time = 500, \
+ }, \
+ }
+
+#define PF8X00VSNVS(_name, base, voltages) \
+ [PF8X00_VSNVS] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = _name, \
+ .regulators_node = "regulators", \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pf8x00_vsnvs_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PF8X00_VSNVS, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base), \
+ .vsel_mask = 0x3, \
+ }, \
+ }
+
+static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = {
+ PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages),
+ PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages),
+ PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages),
+ PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages),
+ PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages),
+ PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages),
+ PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages),
+ PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages),
+ PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages),
+ PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages),
+ PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages),
+ PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages),
+};
+
+static int pf8x00_identify(struct pf8x00_chip *chip)
+{
+ unsigned int value;
+ u8 dev_fam, dev_id;
+ const char *name = NULL;
+ int ret;
+
+ ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value);
+ if (ret) {
+ dev_err(chip->dev, "failed to read chip family\n");
+ return ret;
+ }
+
+ dev_fam = value & PF8X00_DEVICE_FAM_MASK;
+ switch (dev_fam) {
+ case PF8X00_FAM:
+ break;
+ default:
+ dev_err(chip->dev,
+ "Chip 0x%x is not from PF8X00 family\n", dev_fam);
+ return ret;
+ }
+
+ dev_id = value & PF8X00_DEVICE_ID_MASK;
+ switch (dev_id) {
+ case PF8100:
+ name = "PF8100";
+ break;
+ case PF8121A:
+ name = "PF8121A";
+ break;
+ case PF8200:
+ name = "PF8100";
+ break;
+ default:
+ dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id);
+ return -ENODEV;
+ }
+
+ dev_info(chip->dev, "%s PMIC found.\n", name);
+
+ return 0;
+}
+
+static int pf8x00_i2c_probe(struct i2c_client *client)
+{
+ struct regulator_config config = { NULL, };
+ struct pf8x00_chip *chip;
+ int id;
+ int ret;
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->dev = &client->dev;
+
+ chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(&client->dev,
+ "regmap allocation failed with err %d\n", ret);
+ return ret;
+ }
+
+ ret = pf8x00_identify(chip);
+ if (ret)
+ return ret;
+
+ for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) {
+ struct pf8x00_regulator *data = &pf8x00_regulators_data[id];
+ struct regulator_dev *rdev;
+
+ config.dev = chip->dev;
+ config.driver_data = chip;
+ config.regmap = chip->regmap;
+
+ rdev = devm_regulator_register(&client->dev, &data->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&client->dev,
+ "failed to register %s regulator\n", data->desc.name);
+ return PTR_ERR(rdev);
+ }
+
+ if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) {
+ u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
+
+ regmap_update_bits(chip->regmap, reg,
+ PF8X00_SWXPHASE_MASK,
+ data->phase_shift);
+
+ regmap_update_bits(chip->regmap, reg,
+ PF8X00_SWXILIM_MASK,
+ data->ilim << PF8X00_SWXILIM_SHIFT);
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id pf8x00_dt_ids[] = {
+ { .compatible = "nxp,pf8x00",},
+ { }
+};
+MODULE_DEVICE_TABLE(of, pf8x00_dt_ids);
+
+static const struct i2c_device_id pf8x00_i2c_id[] = {
+ { "pf8x00", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id);
+
+static struct i2c_driver pf8x00_regulator_driver = {
+ .id_table = pf8x00_i2c_id,
+ .driver = {
+ .name = "pf8x00",
+ .of_match_table = pf8x00_dt_ids,
+ },
+ .probe_new = pf8x00_i2c_probe,
+};
+module_i2c_driver(pf8x00_regulator_driver);
+
+MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
+MODULE_AUTHOR("Troy Kisky <troy.kisky@boundarydevices.com>");
+MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c
index 01a12cfcea7c..d60d7d1b7fa2 100644
--- a/drivers/regulator/pfuze100-regulator.c
+++ b/drivers/regulator/pfuze100-regulator.c
@@ -105,15 +105,6 @@ static const int pfuze3000_sw2hi[] = {
2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000,
};
-static const struct i2c_device_id pfuze_device_id[] = {
- {.name = "pfuze100", .driver_data = PFUZE100},
- {.name = "pfuze200", .driver_data = PFUZE200},
- {.name = "pfuze3000", .driver_data = PFUZE3000},
- {.name = "pfuze3001", .driver_data = PFUZE3001},
- { }
-};
-MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
-
static const struct of_device_id pfuze_dt_ids[] = {
{ .compatible = "fsl,pfuze100", .data = (void *)PFUZE100},
{ .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
@@ -440,7 +431,6 @@ static struct pfuze_regulator pfuze3001_regulators[] = {
PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
};
-#ifdef CONFIG_OF
/* PFUZE100 */
static struct of_regulator_match pfuze100_matches[] = {
{ .name = "sw1ab", },
@@ -578,22 +568,6 @@ static inline struct device_node *match_of_node(int index)
{
return pfuze_matches[index].of_node;
}
-#else
-static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
-{
- return 0;
-}
-
-static inline struct regulator_init_data *match_init_data(int index)
-{
- return NULL;
-}
-
-static inline struct device_node *match_of_node(int index)
-{
- return NULL;
-}
-#endif
static struct pfuze_chip *syspm_pfuze_chip;
@@ -708,8 +682,6 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pfuze_chip *pfuze_chip;
- struct pfuze_regulator_platform_data *pdata =
- dev_get_platdata(&client->dev);
struct regulator_config config = { };
int i, ret;
const struct of_device_id *match;
@@ -802,10 +774,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
desc = &pfuze_chip->regulator_descs[i].desc;
- if (pdata)
- init_data = pdata->init_data[i];
- else
- init_data = match_init_data(i);
+ init_data = match_init_data(i);
/* SW2~SW4 high bit check and modify the voltage value table */
if (i >= sw_check_start && i <= sw_check_end) {
@@ -879,7 +848,6 @@ static int pfuze100_regulator_remove(struct i2c_client *client)
}
static struct i2c_driver pfuze_driver = {
- .id_table = pfuze_device_id,
.driver = {
.name = "pfuze100-regulator",
.of_match_table = pfuze_dt_ids,
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
index d488325499a9..fe030ec4b7db 100644
--- a/drivers/regulator/qcom-rpmh-regulator.c
+++ b/drivers/regulator/qcom-rpmh-regulator.c
@@ -865,6 +865,60 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = {
{},
};
+static const struct rpmh_vreg_init_data pm8350_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"),
+ RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"),
+ RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
+ RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"),
+ RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"),
+ RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"),
+ RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"),
+ RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"),
+ RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"),
+ RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"),
+ RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"),
+ RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"),
+ RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"),
+ RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"),
+ RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"),
+ RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"),
+ RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"),
+ RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"),
+ RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"),
+ {},
+};
+
+static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"),
+ RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"),
+ RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
+ RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"),
+ RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"),
+ RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"),
+ RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"),
+ RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"),
+ RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"),
+ RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
+ RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
+ RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
+ RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"),
+ RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
+ RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"),
+ RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"),
+ RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"),
+ RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"),
+ RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"),
+ RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
+ RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"),
+ {},
+};
+
static const struct rpmh_vreg_init_data pm8009_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"),
@@ -930,6 +984,33 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = {
{},
};
+static const struct rpmh_vreg_init_data pmx55_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"),
+ RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"),
+ RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
+ RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"),
+ RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"),
+ RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"),
+ RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"),
+ RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"),
+ RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"),
+ RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"),
+ RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"),
+ RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"),
+ RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"),
+ RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"),
+ RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"),
+ RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"),
+ RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"),
+ RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"),
+ RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"),
+ RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"),
+ {},
+};
+
static int rpmh_regulator_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -985,6 +1066,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
.data = pm8150l_vreg_data,
},
{
+ .compatible = "qcom,pm8350-rpmh-regulators",
+ .data = pm8350_vreg_data,
+ },
+ {
+ .compatible = "qcom,pm8350c-rpmh-regulators",
+ .data = pm8350c_vreg_data,
+ },
+ {
.compatible = "qcom,pm8998-rpmh-regulators",
.data = pm8998_vreg_data,
},
@@ -1000,6 +1089,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
.compatible = "qcom,pm6150l-rpmh-regulators",
.data = pm6150l_vreg_data,
},
+ {
+ .compatible = "qcom,pmx55-rpmh-regulators",
+ .data = pmx55_vreg_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
new file mode 100644
index 000000000000..0e8b3caa8146
--- /dev/null
+++ b/drivers/regulator/scmi-regulator.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// System Control and Management Interface (SCMI) based regulator driver
+//
+// Copyright (C) 2020 ARM Ltd.
+//
+// Implements a regulator driver on top of the SCMI Voltage Protocol.
+//
+// The ARM SCMI Protocol aims in general to hide as much as possible all the
+// underlying operational details while providing an abstracted interface for
+// its users to operate upon: as a consequence the resulting operational
+// capabilities and configurability of this regulator device are much more
+// limited than the ones usually available on a standard physical regulator.
+//
+// The supported SCMI regulator ops are restricted to the bare minimum:
+//
+// - 'status_ops': enable/disable/is_enabled
+// - 'voltage_ops': get_voltage_sel/set_voltage_sel
+// list_voltage/map_voltage
+//
+// Each SCMI regulator instance is associated, through the means of a proper DT
+// entry description, to a specific SCMI Voltage Domain.
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/linear_range.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct scmi_regulator {
+ u32 id;
+ struct scmi_device *sdev;
+ struct regulator_dev *rdev;
+ struct device_node *of_node;
+ struct regulator_desc desc;
+ struct regulator_config conf;
+};
+
+struct scmi_regulator_info {
+ int num_doms;
+ struct scmi_regulator **sregv;
+};
+
+static int scmi_reg_enable(struct regulator_dev *rdev)
+{
+ struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
+ const struct scmi_handle *handle = sreg->sdev->handle;
+
+ return handle->voltage_ops->config_set(handle, sreg->id,
+ SCMI_VOLTAGE_ARCH_STATE_ON);
+}
+
+static int scmi_reg_disable(struct regulator_dev *rdev)
+{
+ struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
+ const struct scmi_handle *handle = sreg->sdev->handle;
+
+ return handle->voltage_ops->config_set(handle, sreg->id,
+ SCMI_VOLTAGE_ARCH_STATE_OFF);
+}
+
+static int scmi_reg_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ u32 config;
+ struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
+ const struct scmi_handle *handle = sreg->sdev->handle;
+
+ ret = handle->voltage_ops->config_get(handle, sreg->id,
+ &config);
+ if (ret) {
+ dev_err(&sreg->sdev->dev,
+ "Error %d reading regulator %s status.\n",
+ ret, sreg->desc.name);
+ return ret;
+ }
+
+ return config & SCMI_VOLTAGE_ARCH_STATE_ON;
+}
+
+static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev)
+{
+ int ret;
+ s32 volt_uV;
+ struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
+ const struct scmi_handle *handle = sreg->sdev->handle;
+
+ ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV);
+ if (ret)
+ return ret;
+
+ return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV);
+}
+
+static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ s32 volt_uV;
+ struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
+ const struct scmi_handle *handle = sreg->sdev->handle;
+
+ volt_uV = sreg->desc.ops->list_voltage(rdev, selector);
+ if (volt_uV <= 0)
+ return -EINVAL;
+
+ return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV);
+}
+
+static const struct regulator_ops scmi_reg_fixed_ops = {
+ .enable = scmi_reg_enable,
+ .disable = scmi_reg_disable,
+ .is_enabled = scmi_reg_is_enabled,
+};
+
+static const struct regulator_ops scmi_reg_linear_ops = {
+ .enable = scmi_reg_enable,
+ .disable = scmi_reg_disable,
+ .is_enabled = scmi_reg_is_enabled,
+ .get_voltage_sel = scmi_reg_get_voltage_sel,
+ .set_voltage_sel = scmi_reg_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+};
+
+static const struct regulator_ops scmi_reg_discrete_ops = {
+ .enable = scmi_reg_enable,
+ .disable = scmi_reg_disable,
+ .is_enabled = scmi_reg_is_enabled,
+ .get_voltage_sel = scmi_reg_get_voltage_sel,
+ .set_voltage_sel = scmi_reg_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+};
+
+static int
+scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg,
+ const struct scmi_voltage_info *vinfo)
+{
+ s32 delta_uV;
+
+ /*
+ * Note that SCMI voltage domains describable by linear ranges
+ * (segments) {low, high, step} are guaranteed to come in one single
+ * triplet by the SCMI Voltage Domain protocol support itself.
+ */
+
+ delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] -
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]);
+
+ /* Rule out buggy negative-intervals answers from fw */
+ if (delta_uV < 0) {
+ dev_err(&sreg->sdev->dev,
+ "Invalid volt-range %d-%duV for domain %d\n",
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW],
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH],
+ sreg->id);
+ return -EINVAL;
+ }
+
+ if (!delta_uV) {
+ /* Just one fixed voltage exposed by SCMI */
+ sreg->desc.fixed_uV =
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW];
+ sreg->desc.n_voltages = 1;
+ sreg->desc.ops = &scmi_reg_fixed_ops;
+ } else {
+ /* One simple linear mapping. */
+ sreg->desc.min_uV =
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW];
+ sreg->desc.uV_step =
+ vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP];
+ sreg->desc.linear_min_sel = 0;
+ sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step;
+ sreg->desc.ops = &scmi_reg_linear_ops;
+ }
+
+ return 0;
+}
+
+static int
+scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg,
+ const struct scmi_voltage_info *vinfo)
+{
+ /* Discrete non linear levels are mapped to volt_table */
+ sreg->desc.n_voltages = vinfo->num_levels;
+
+ if (sreg->desc.n_voltages > 1) {
+ sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv;
+ sreg->desc.ops = &scmi_reg_discrete_ops;
+ } else {
+ sreg->desc.fixed_uV = vinfo->levels_uv[0];
+ sreg->desc.ops = &scmi_reg_fixed_ops;
+ }
+
+ return 0;
+}
+
+static int scmi_regulator_common_init(struct scmi_regulator *sreg)
+{
+ int ret;
+ const struct scmi_handle *handle = sreg->sdev->handle;
+ struct device *dev = &sreg->sdev->dev;
+ const struct scmi_voltage_info *vinfo;
+
+ vinfo = handle->voltage_ops->info_get(handle, sreg->id);
+ if (!vinfo) {
+ dev_warn(dev, "Failure to get voltage domain %d\n",
+ sreg->id);
+ return -ENODEV;
+ }
+
+ /*
+ * Regulator framework does not fully support negative voltages
+ * so we discard any voltage domain reported as supporting negative
+ * voltages: as a consequence each levels_uv entry is guaranteed to
+ * be non-negative from here on.
+ */
+ if (vinfo->negative_volts_allowed) {
+ dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
+ sreg->of_node->full_name);
+ return -EOPNOTSUPP;
+ }
+
+ sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name);
+ if (!sreg->desc.name)
+ return -ENOMEM;
+
+ sreg->desc.id = sreg->id;
+ sreg->desc.type = REGULATOR_VOLTAGE;
+ sreg->desc.owner = THIS_MODULE;
+ sreg->desc.of_match_full_name = true;
+ sreg->desc.of_match = sreg->of_node->full_name;
+ sreg->desc.regulators_node = "regulators";
+ if (vinfo->segmented)
+ ret = scmi_config_linear_regulator_mappings(sreg, vinfo);
+ else
+ ret = scmi_config_discrete_regulator_mappings(sreg, vinfo);
+ if (ret)
+ return ret;
+
+ /*
+ * Using the scmi device here to have DT searched from Voltage
+ * protocol node down.
+ */
+ sreg->conf.dev = dev;
+
+ /* Store for later retrieval via rdev_get_drvdata() */
+ sreg->conf.driver_data = sreg;
+
+ return 0;
+}
+
+static int process_scmi_regulator_of_node(struct scmi_device *sdev,
+ struct device_node *np,
+ struct scmi_regulator_info *rinfo)
+{
+ u32 dom, ret;
+
+ ret = of_property_read_u32(np, "reg", &dom);
+ if (ret)
+ return ret;
+
+ if (dom >= rinfo->num_doms)
+ return -ENODEV;
+
+ if (rinfo->sregv[dom]) {
+ dev_err(&sdev->dev,
+ "SCMI Voltage Domain %d already in use. Skipping: %s\n",
+ dom, np->full_name);
+ return -EINVAL;
+ }
+
+ rinfo->sregv[dom] = devm_kzalloc(&sdev->dev,
+ sizeof(struct scmi_regulator),
+ GFP_KERNEL);
+ if (!rinfo->sregv[dom])
+ return -ENOMEM;
+
+ rinfo->sregv[dom]->id = dom;
+ rinfo->sregv[dom]->sdev = sdev;
+
+ /* get hold of good nodes */
+ of_node_get(np);
+ rinfo->sregv[dom]->of_node = np;
+
+ dev_dbg(&sdev->dev,
+ "Found SCMI Regulator entry -- OF node [%d] -> %s\n",
+ dom, np->full_name);
+
+ return 0;
+}
+
+static int scmi_regulator_probe(struct scmi_device *sdev)
+{
+ int d, ret, num_doms;
+ struct device_node *np, *child;
+ const struct scmi_handle *handle = sdev->handle;
+ struct scmi_regulator_info *rinfo;
+
+ if (!handle || !handle->voltage_ops)
+ return -ENODEV;
+
+ num_doms = handle->voltage_ops->num_domains_get(handle);
+ if (num_doms <= 0) {
+ if (!num_doms) {
+ dev_err(&sdev->dev,
+ "number of voltage domains invalid\n");
+ num_doms = -EINVAL;
+ } else {
+ dev_err(&sdev->dev,
+ "failed to get voltage domains - err:%d\n",
+ num_doms);
+ }
+
+ return num_doms;
+ }
+
+ rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL);
+ if (!rinfo)
+ return -ENOMEM;
+
+ /* Allocate pointers array for all possible domains */
+ rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms,
+ sizeof(void *), GFP_KERNEL);
+ if (!rinfo->sregv)
+ return -ENOMEM;
+
+ rinfo->num_doms = num_doms;
+
+ /*
+ * Start collecting into rinfo->sregv possibly good SCMI Regulators as
+ * described by a well-formed DT entry and associated with an existing
+ * plausible SCMI Voltage Domain number, all belonging to this SCMI
+ * platform instance node (handle->dev->of_node).
+ */
+ np = of_find_node_by_name(handle->dev->of_node, "regulators");
+ for_each_child_of_node(np, child) {
+ ret = process_scmi_regulator_of_node(sdev, child, rinfo);
+ /* abort on any mem issue */
+ if (ret == -ENOMEM)
+ return ret;
+ }
+
+ /*
+ * Register a regulator for each valid regulator-DT-entry that we
+ * can successfully reach via SCMI and has a valid associated voltage
+ * domain.
+ */
+ for (d = 0; d < num_doms; d++) {
+ struct scmi_regulator *sreg = rinfo->sregv[d];
+
+ /* Skip empty slots */
+ if (!sreg)
+ continue;
+
+ ret = scmi_regulator_common_init(sreg);
+ /* Skip invalid voltage domains */
+ if (ret)
+ continue;
+
+ sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc,
+ &sreg->conf);
+ if (IS_ERR(sreg->rdev)) {
+ sreg->rdev = NULL;
+ continue;
+ }
+
+ dev_info(&sdev->dev,
+ "Regulator %s registered for domain [%d]\n",
+ sreg->desc.name, sreg->id);
+ }
+
+ dev_set_drvdata(&sdev->dev, rinfo);
+
+ return 0;
+}
+
+static void scmi_regulator_remove(struct scmi_device *sdev)
+{
+ int d;
+ struct scmi_regulator_info *rinfo;
+
+ rinfo = dev_get_drvdata(&sdev->dev);
+ if (!rinfo)
+ return;
+
+ for (d = 0; d < rinfo->num_doms; d++) {
+ if (!rinfo->sregv[d])
+ continue;
+ of_node_put(rinfo->sregv[d]->of_node);
+ }
+}
+
+static const struct scmi_device_id scmi_regulator_id_table[] = {
+ { SCMI_PROTOCOL_VOLTAGE, "regulator" },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table);
+
+static struct scmi_driver scmi_drv = {
+ .name = "scmi-regulator",
+ .probe = scmi_regulator_probe,
+ .remove = scmi_regulator_remove,
+ .id_table = scmi_regulator_id_table,
+};
+
+module_scmi_driver(scmi_drv);
+
+MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/regulator/dlg,da9121-regulator.h b/include/dt-bindings/regulator/dlg,da9121-regulator.h
new file mode 100644
index 000000000000..954edf633ce7
--- /dev/null
+++ b/include/dt-bindings/regulator/dlg,da9121-regulator.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _DT_BINDINGS_REGULATOR_DLG_DA9121_H
+#define _DT_BINDINGS_REGULATOR_DLG_DA9121_H
+
+/*
+ * These buck mode constants may be used to specify values in device tree
+ * properties (e.g. regulator-initial-mode).
+ * A description of the following modes is in the manufacturers datasheet.
+ */
+
+#define DA9121_BUCK_MODE_FORCE_PFM 0
+#define DA9121_BUCK_MODE_FORCE_PWM 1
+#define DA9121_BUCK_MODE_FORCE_PWM_SHEDDING 2
+#define DA9121_BUCK_MODE_AUTO 3
+
+#define DA9121_BUCK_RIPPLE_CANCEL_NONE 0
+#define DA9121_BUCK_RIPPLE_CANCEL_SMALL 1
+#define DA9121_BUCK_RIPPLE_CANCEL_MID 2
+#define DA9121_BUCK_RIPPLE_CANCEL_LARGE 3
+
+#endif
diff --git a/include/linux/regulator/da9121.h b/include/linux/regulator/da9121.h
new file mode 100644
index 000000000000..62d9d257dc25
--- /dev/null
+++ b/include/linux/regulator/da9121.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * DA9121 Single-channel dual-phase 10A buck converter
+ * DA9130 Single-channel dual-phase 10A buck converter (Automotive)
+ * DA9217 Single-channel dual-phase 6A buck converter
+ * DA9122 Dual-channel single-phase 5A buck converter
+ * DA9131 Dual-channel single-phase 5A buck converter (Automotive)
+ * DA9220 Dual-channel single-phase 3A buck converter
+ * DA9132 Dual-channel single-phase 3A buck converter (Automotive)
+ *
+ * Copyright (C) 2020 Dialog Semiconductor
+ *
+ * Authors: Adam Ward, Dialog Semiconductor
+ */
+
+#ifndef __LINUX_REGULATOR_DA9121_H
+#define __LINUX_REGULATOR_DA9121_H
+
+#include <linux/regulator/machine.h>
+
+struct gpio_desc;
+
+enum {
+ DA9121_IDX_BUCK1,
+ DA9121_IDX_BUCK2,
+ DA9121_IDX_MAX
+};
+
+struct da9121_pdata {
+ int num_buck;
+ struct gpio_desc *gpiod_ren[DA9121_IDX_MAX];
+ struct device_node *reg_node[DA9121_IDX_MAX];
+ struct regulator_init_data *init_data[DA9121_IDX_MAX];
+};
+
+#endif
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 11cade73726c..d7c77ee370f3 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -223,6 +223,8 @@ enum regulator_type {
* @name: Identifying name for the regulator.
* @supply_name: Identifying the regulator supply
* @of_match: Name used to identify regulator in DT.
+ * @of_match_full_name: A flag to indicate that the of_match string, if
+ * present, should be matched against the node full_name.
* @regulators_node: Name of node containing regulator definitions in DT.
* @of_parse_cb: Optional callback called only if of_match is present.
* Will be called for each regulator parsed from DT, during
@@ -314,6 +316,7 @@ struct regulator_desc {
const char *name;
const char *supply_name;
const char *of_match;
+ bool of_match_full_name;
const char *regulators_node;
int (*of_parse_cb)(struct device_node *,
const struct regulator_desc *,
diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h
index d47e668d9ca8..c964fe8ab698 100644
--- a/include/linux/regulator/pfuze100.h
+++ b/include/linux/regulator/pfuze100.h
@@ -63,10 +63,4 @@
#define PFUZE3001_VLDO3 8
#define PFUZE3001_VLDO4 9
-struct regulator_init_data;
-
-struct pfuze_regulator_platform_data {
- struct regulator_init_data *init_data[PFUZE100_MAX_REGULATOR];
-};
-
#endif /* __LINUX_REG_PFUZE100_H */
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 9cd312a1ff92..b11ca01e2393 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -210,6 +210,64 @@ struct scmi_reset_ops {
};
/**
+ * struct scmi_voltage_info - describe one available SCMI Voltage Domain
+ *
+ * @id: the domain ID as advertised by the platform
+ * @segmented: defines the layout of the entries of array @levels_uv.
+ * - when True the entries are to be interpreted as triplets,
+ * each defining a segment representing a range of equally
+ * space voltages: <lowest_volts>, <highest_volt>, <step_uV>
+ * - when False the entries simply represent a single discrete
+ * supported voltage level
+ * @negative_volts_allowed: True if any of the entries of @levels_uv represent
+ * a negative voltage.
+ * @attributes: represents Voltage Domain advertised attributes
+ * @name: name assigned to the Voltage Domain by platform
+ * @num_levels: number of total entries in @levels_uv.
+ * @levels_uv: array of entries describing the available voltage levels for
+ * this domain.
+ */
+struct scmi_voltage_info {
+ unsigned int id;
+ bool segmented;
+ bool negative_volts_allowed;
+ unsigned int attributes;
+ char name[SCMI_MAX_STR_SIZE];
+ unsigned int num_levels;
+#define SCMI_VOLTAGE_SEGMENT_LOW 0
+#define SCMI_VOLTAGE_SEGMENT_HIGH 1
+#define SCMI_VOLTAGE_SEGMENT_STEP 2
+ int *levels_uv;
+};
+
+/**
+ * struct scmi_voltage_ops - represents the various operations provided
+ * by SCMI Voltage Protocol
+ *
+ * @num_domains_get: get the count of voltage domains provided by SCMI
+ * @info_get: get the information of the specified domain
+ * @config_set: set the config for the specified domain
+ * @config_get: get the config of the specified domain
+ * @level_set: set the voltage level for the specified domain
+ * @level_get: get the voltage level of the specified domain
+ */
+struct scmi_voltage_ops {
+ int (*num_domains_get)(const struct scmi_handle *handle);
+ const struct scmi_voltage_info __must_check *(*info_get)
+ (const struct scmi_handle *handle, u32 domain_id);
+ int (*config_set)(const struct scmi_handle *handle, u32 domain_id,
+ u32 config);
+#define SCMI_VOLTAGE_ARCH_STATE_OFF 0x0
+#define SCMI_VOLTAGE_ARCH_STATE_ON 0x7
+ int (*config_get)(const struct scmi_handle *handle, u32 domain_id,
+ u32 *config);
+ int (*level_set)(const struct scmi_handle *handle, u32 domain_id,
+ u32 flags, s32 volt_uV);
+ int (*level_get)(const struct scmi_handle *handle, u32 domain_id,
+ s32 *volt_uV);
+};
+
+/**
* struct scmi_notify_ops - represents notifications' operations provided by
* SCMI core
* @register_event_notifier: Register a notifier_block for the requested event
@@ -262,6 +320,7 @@ struct scmi_notify_ops {
* @clk_ops: pointer to set of clock protocol operations
* @sensor_ops: pointer to set of sensor protocol operations
* @reset_ops: pointer to set of reset protocol operations
+ * @voltage_ops: pointer to set of voltage protocol operations
* @notify_ops: pointer to set of notifications related operations
* @perf_priv: pointer to private data structure specific to performance
* protocol(for internal use only)
@@ -273,6 +332,8 @@ struct scmi_notify_ops {
* protocol(for internal use only)
* @reset_priv: pointer to private data structure specific to reset
* protocol(for internal use only)
+ * @voltage_priv: pointer to private data structure specific to voltage
+ * protocol(for internal use only)
* @notify_priv: pointer to private data structure specific to notifications
* (for internal use only)
*/
@@ -284,6 +345,7 @@ struct scmi_handle {
const struct scmi_power_ops *power_ops;
const struct scmi_sensor_ops *sensor_ops;
const struct scmi_reset_ops *reset_ops;
+ const struct scmi_voltage_ops *voltage_ops;
const struct scmi_notify_ops *notify_ops;
/* for protocol internal use */
void *perf_priv;
@@ -291,6 +353,7 @@ struct scmi_handle {
void *power_priv;
void *sensor_priv;
void *reset_priv;
+ void *voltage_priv;
void *notify_priv;
void *system_priv;
};
@@ -303,6 +366,7 @@ enum scmi_std_protocol {
SCMI_PROTOCOL_CLOCK = 0x14,
SCMI_PROTOCOL_SENSOR = 0x15,
SCMI_PROTOCOL_RESET = 0x16,
+ SCMI_PROTOCOL_VOLTAGE = 0x17,
};
enum scmi_system_events {