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