Merge tag 'regulator-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator

Pull regulator updates from Mark Brown:
 "This is a very quiet release for regulator, almost all the changes are
  new drivers but we do also have some improvements for the Rust
  bindings.

   - Additional APIs added to the Rust bindings

   - Support for Maxim MAX77838, NXP PF0900 and PF5300, Richtek RT5133
     and SpacemiT P1"

* tag 'regulator-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (28 commits)
  regulator: dt-bindings: qcom,sdm845-refgen-regulator: document more platforms
  regulator: Fix MAX77838 selection
  regulator: spacemit: support SpacemiT P1 regulators
  regulator: max77838: add max77838 regulator driver
  dt-bindings: regulator: document max77838 pmic
  rust: regulator: add devm_enable and devm_enable_optional
  rust: regulator: remove Regulator<Dynamic>
  regulator: dt-bindings: rpi-panel: Split 7" Raspberry Pi 720x1280 v2 binding
  regulator: pf530x: Add a driver for the NXP PF5300 Regulator
  regulator: dt-bindings: nxp,pf530x: Add NXP PF5300/PF5301/PF5302 PMICs
  regulator: scmi: Use int type to store negative error codes
  regulator: core: Remove redundant ternary operators
  rust: regulator: use `to_result` for error handling
  regulator: consumer.rst: document bulk operations
  regulator: rt5133: Fix IS_ERR() vs NULL bug in rt5133_validate_vendor_info()
  regulator: bd718x7: Use kcalloc() instead of kzalloc()
  regulator: rt5133: Fix spelling mistake "regualtor" -> "regulator"
  regulator: remove unneeded 'fast_io' parameter in regmap_config
  regulator: rt5133: Add RT5133 PMIC regulator Support
  regulator: dt-bindings: Add Richtek RT5133 Support
  ...
This commit is contained in:
Linus Torvalds
2025-10-01 11:43:54 -07:00
29 changed files with 3354 additions and 278 deletions

View File

@@ -1,82 +0,0 @@
Device-Tree bindings for Active-semi ACT8945A MFD driver
Required properties:
- compatible: "active-semi,act8945a".
- reg: the I2C slave address for the ACT8945A chip
The chip exposes two subdevices:
- a regulators: see ../regulator/act8945a-regulator.txt
- a charger: see ../power/act8945a-charger.txt
Example:
pmic@5b {
compatible = "active-semi,act8945a";
reg = <0x5b>;
active-semi,vsel-high;
regulators {
vdd_1v35_reg: REG_DCDC1 {
regulator-name = "VDD_1V35";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
};
vdd_1v2_reg: REG_DCDC2 {
regulator-name = "VDD_1V2";
regulator-min-microvolt = <1100000>;
regulator-max-microvolt = <1300000>;
regulator-always-on;
};
vdd_3v3_reg: REG_DCDC3 {
regulator-name = "VDD_3V3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
vdd_fuse_reg: REG_LDO1 {
regulator-name = "VDD_FUSE";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
regulator-always-on;
};
vdd_3v3_lp_reg: REG_LDO2 {
regulator-name = "VDD_3V3_LP";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
vdd_led_reg: REG_LDO3 {
regulator-name = "VDD_LED";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
vdd_sdhc_1v8_reg: REG_LDO4 {
regulator-name = "VDD_SDHC_1V8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
};
charger {
compatible = "active-semi,act8945a-charger";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_charger_chglev &pinctrl_charger_lbo &pinctrl_charger_irq>;
interrupt-parent = <&pioA>;
interrupts = <45 IRQ_TYPE_LEVEL_LOW>;
active-semi,chglev-gpios = <&pioA 12 GPIO_ACTIVE_HIGH>;
active-semi,lbo-gpios = <&pioA 72 GPIO_ACTIVE_LOW>;
active-semi,input-voltage-threshold-microvolt = <6600>;
active-semi,precondition-timeout = <40>;
active-semi,total-timeout = <3>;
};
};

View File

@@ -1,76 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/supply/active-semi,act8945a-charger.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Active-semi ACT8945A Charger Function
maintainers:
- Sebastian Reichel <sre@kernel.org>
allOf:
- $ref: power-supply.yaml#
properties:
compatible:
const: active-semi,act8945a-charger
interrupts:
maxItems: 1
active-semi,chglev-gpios:
maxItems: 1
description: charge current level GPIO
active-semi,lbo-gpios:
maxItems: 1
description: low battery voltage detect GPIO
active-semi,input-voltage-threshold-microvolt:
description: |
Specifies the charger's input over-voltage threshold value.
Despite the name, specified values are in millivolt (mV).
Defaults to 6.6 V
enum: [ 6600, 7000, 7500, 8000 ]
active-semi,precondition-timeout:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Specifies the charger's PRECONDITION safety timer setting value in minutes.
If 0, it means to disable this timer.
Defaults to 40 minutes.
enum: [ 0, 40, 60, 80 ]
active-semi,total-timeout:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Specifies the charger's total safety timer setting value in hours;
If 0, it means to disable this timer;
Defaults to 3 hours.
enum: [ 0, 3, 4, 5 ]
required:
- compatible
- interrupts
- active-semi,chglev-gpios
- active-semi,lbo-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
pmic {
charger {
compatible = "active-semi,act8945a-charger";
interrupt-parent = <&pioA>;
interrupts = <45 IRQ_TYPE_LEVEL_LOW>;
active-semi,chglev-gpios = <&pioA 12 GPIO_ACTIVE_HIGH>;
active-semi,lbo-gpios = <&pioA 72 GPIO_ACTIVE_LOW>;
active-semi,input-voltage-threshold-microvolt = <6600>;
active-semi,precondition-timeout = <40>;
active-semi,total-timeout = <3>;
};
};

View File

@@ -91,28 +91,41 @@ properties:
maxItems: 1
active-semi,chglev-gpios:
description: CGHLEV GPIO
description: charge current level GPIO
maxItems: 1
active-semi,lbo-gpios:
description: LBO GPIO
description: low battery voltage detect GPIO
maxItems: 1
active-semi,input-voltage-threshold-microvolt:
description: Input voltage threshold
maxItems: 1
description:
Specifies the charger's input over-voltage threshold value. Despite
the name, specified values are in millivolt (mV).
enum: [ 6600, 7000, 7500, 8000 ]
default: 6600
active-semi,precondition-timeout:
description: Precondition timeout
description:
Specifies the charger's PRECONDITION safety timer setting value in
minutes. If 0, it means to disable this timer.
enum: [ 0, 40, 60, 80 ]
default: 40
$ref: /schemas/types.yaml#/definitions/uint32
active-semi,total-timeout:
description: Total timeout
description:
Specifies the charger's total safety timer setting value in hours; If
0, it means to disable this timer;
enum: [ 0, 3, 4, 5 ]
default: 3
$ref: /schemas/types.yaml#/definitions/uint32
required:
- compatible
- interrupts
- active-semi,chglev-gpios
- active-semi,lbo-gpios
additionalProperties: false

View File

@@ -0,0 +1,68 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/maxim,max77838.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim Integrated MAX77838 PMIC
maintainers:
- Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
properties:
$nodename:
pattern: "pmic@[0-9a-f]{1,2}"
compatible:
enum:
- maxim,max77838
reg:
maxItems: 1
regulators:
type: object
$ref: regulator.yaml#
description: |
list of regulators provided by this controller, must be named
after their hardware counterparts ldo[1-4] and buck
properties:
buck:
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
patternProperties:
"^ldo([1-4])$":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@60 {
compatible = "maxim,max77838";
reg = <0x60>;
regulators {
ldo2 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
};
};
};
...

View File

@@ -0,0 +1,163 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/nxp,pf0900.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PF0900 Power Management Integrated Circuit regulators
maintainers:
- Joy Zou <joy.zou@nxp.com>
description:
The PF0900 is a power management integrated circuit (PMIC) optimized
for high performance i.MX9x based applications. It features five high
efficiency buck converters, three linear and one vaon regulators. It
provides low quiescent current in Standby and low power off Modes.
properties:
compatible:
enum:
- nxp,pf0900
reg:
maxItems: 1
interrupts:
maxItems: 1
regulators:
type: object
additionalProperties: false
properties:
vaon:
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
patternProperties:
"^ldo[1-3]$":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
"^sw[1-5]$":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
nxp,i2c-crc-enable:
type: boolean
description:
The CRC enabled during register read/write. Controlled by customer
unviewable fuse bits OTP_I2C_CRC_EN. Check chip part number.
required:
- compatible
- reg
- interrupts
- regulators
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@8 {
compatible = "nxp,pf0900";
reg = <0x08>;
interrupt-parent = <&pcal6524>;
interrupts = <89 IRQ_TYPE_LEVEL_LOW>;
nxp,i2c-crc-enable;
regulators {
vaon {
regulator-name = "VAON";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
sw1 {
regulator-name = "SW1";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-ramp-delay = <1950>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-max-microvolt = <650000>;
regulator-suspend-min-microvolt = <650000>;
};
};
sw2 {
regulator-name = "SW2";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-ramp-delay = <1950>;
};
sw3 {
regulator-name = "SW3";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-ramp-delay = <1950>;
};
sw4 {
regulator-name = "SW4";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-ramp-delay = <1950>;
};
sw5 {
regulator-name = "SW5";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
regulator-ramp-delay = <1950>;
};
ldo1 {
regulator-name = "LDO1";
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo2 {
regulator-name = "LDO2";
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo3 {
regulator-name = "LDO3";
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
};
};
};

View File

@@ -0,0 +1,54 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/nxp,pf5300.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PF5300/PF5301/PF5302 PMIC regulators
maintainers:
- Woodrow Douglass <wdouglass@carnegierobotics.com>
description: |
The PF5300, PF5301, and PF5302 integrate high-performance buck converters,
12 A, 8 A, and 15 A, respectively, to power high-end automotive and industrial
processors. With adaptive voltage positioning and a high-bandwidth loop, they
offer transient regulation to minimize capacitor requirements.
allOf:
- $ref: regulator.yaml#
properties:
compatible:
oneOf:
- const: nxp,pf5300
- items:
- enum:
- nxp,pf5301
- nxp,pf5302
- const: nxp,pf5300
reg:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@28 {
compatible = "nxp,pf5302", "nxp,pf5300";
reg = <0x28>;
regulator-always-on;
regulator-boot-on;
regulator-max-microvolt = <1200000>;
regulator-min-microvolt = <500000>;
};
};

View File

@@ -23,11 +23,14 @@ properties:
- enum:
- qcom,sc7180-refgen-regulator
- qcom,sc8180x-refgen-regulator
- qcom,sdm670-refgen-regulator
- qcom,sm8150-refgen-regulator
- const: qcom,sdm845-refgen-regulator
- items:
- enum:
- qcom,qcs8300-refgen-regulator
- qcom,sa8775p-refgen-regulator
- qcom,sc7280-refgen-regulator
- qcom,sc8280xp-refgen-regulator
- qcom,sm6350-refgen-regulator

View File

@@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/raspberrypi,7inch-touchscreen-panel-regulator-v2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RaspberryPi 5" and 7" display V2 MCU-based regulator/backlight controller
maintainers:
- Marek Vasut <marek.vasut+renesas@mailbox.org>
description: |
The RaspberryPi 5" and 7" display 2 has an MCU-based regulator, PWM
backlight and GPIO controller on the PCB, which is used to turn the
display unit on/off and control the backlight.
allOf:
- $ref: regulator.yaml#
properties:
compatible:
const: raspberrypi,touchscreen-panel-regulator-v2
reg:
maxItems: 1
gpio-controller: true
"#gpio-cells":
const: 2
description:
The first cell is the pin number, and the second cell is used to
specify the gpio polarity (GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW).
"#pwm-cells":
const: 3
description: See ../../pwm/pwm.yaml for description of the cell formats.
additionalProperties: false
required:
- compatible
- reg
- gpio-controller
- "#gpio-cells"
- "#pwm-cells"
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@45 {
compatible = "raspberrypi,touchscreen-panel-regulator-v2";
reg = <0x45>;
gpio-controller;
#gpio-cells = <2>;
#pwm-cells = <3>;
};
};
...

View File

@@ -12,17 +12,14 @@ maintainers:
description: |
The RaspberryPi 7" display has an ATTINY88-based regulator/backlight
controller on the PCB, which is used to turn the display unit on/off
and control the backlight. The V2 supports 5" and 7" panels and also
offers PWM backlight control.
and control the backlight.
allOf:
- $ref: regulator.yaml#
properties:
compatible:
enum:
- raspberrypi,7inch-touchscreen-panel-regulator
- raspberrypi,touchscreen-panel-regulator-v2
const: raspberrypi,7inch-touchscreen-panel-regulator
reg:
maxItems: 1

View File

@@ -0,0 +1,178 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/richtek,rt5133.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Richtek RT5133 PMIC Regulator
maintainers:
- ShihChia Chang <jeff_chang@richtek.com>
description:
The RT5133 is an integrated Power Management IC for portable devices,
featuring 8 LDOs and 3 GPOs. It allows programmable output voltages,
soft-start times, and protections via I2C. GPO operation depends on LDO1
voltage.
properties:
compatible:
enum:
- richtek,rt5133
reg:
maxItems: 1
enable-gpios:
maxItems: 1
wakeup-source: true
interrupts:
maxItems: 1
gpio-controller: true
"#gpio-cells":
const: 2
richtek,oc-shutdown-all:
type: boolean
description:
Controls the behavior when any LDO (Low Dropout Regulator) enters an
Over Current state.
If set to true, all LDO channels will be shut down.
If set to false, only the affected LDO channel will shut down itself.
richtek,pgb-shutdown-all:
type: boolean
description:
Controls the behavior when any LDO enters a Power Good Bad state.
If set to true, all LDO channels will be shut down.
If set to false, only the affected LDO channel will shut down itself.
regulators:
type: object
additionalProperties: false
properties:
base:
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
description:
Properties for the base regulator, which is the top-level supply for
LDO1 to LDO6. It functions merely as an on/off switch rather than
regulating voltages. If none of LDO1 to LDO6 are in use, switching
off the base will reduce the quiescent current.
required:
- regulator-name
patternProperties:
"^ldo([1-6])$":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
description:
Properties for single LDO regulator
required:
- regulator-name
"^ldo([7-8])$":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
description:
Properties for single LDO regulator
properties:
vin-supply: true
required:
- regulator-name
- vin-supply
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@18 {
compatible = "richtek,rt5133";
reg = <0x18>;
wakeup-source;
interrupts-extended = <&gpio 0 IRQ_TYPE_EDGE_FALLING>;
enable-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
gpio-controller;
#gpio-cells = <2>;
richtek,oc-shutdown-all;
richtek,pgb-shutdown-all;
regulators {
base {
regulator-name = "base";
};
pvin78: ldo1 {
regulator-name = "ldo1";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3199998>;
regulator-active-discharge = <1>;
};
ldo2 {
regulator-name = "ldo2";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3200000>;
regulator-active-discharge = <1>;
};
ldo3 {
regulator-name = "ldo3";
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <3000000>;
regulator-active-discharge = <1>;
};
ldo4 {
regulator-name = "ldo4";
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <3000000>;
regulator-active-discharge = <1>;
};
ldo5 {
regulator-name = "ldo5";
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <3000000>;
regulator-active-discharge = <1>;
};
ldo6 {
regulator-name = "ldo6";
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <3000000>;
regulator-active-discharge = <1>;
};
ldo7 {
regulator-name = "ldo7";
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1200000>;
regulator-active-discharge = <1>;
vin-supply = <&pvin78>;
};
ldo8 {
regulator-name = "ldo8";
regulator-min-microvolt = <855000>;
regulator-max-microvolt = <1200000>;
regulator-active-discharge = <1>;
vin-supply = <&pvin78>;
};
};
};
};

View File

@@ -23,10 +23,18 @@ To release the regulator the consumer driver should call ::
regulator_put(regulator);
Consumers can be supplied by more than one regulator e.g. codec consumer with
analog and digital supplies ::
analog and digital supplies by means of bulk operations ::
struct regulator_bulk_data supplies[2];
supplies[0].supply = "Vcc"; /* digital core */
supplies[1].supply = "Avdd"; /* analog */
ret = regulator_bulk_get(dev, ARRAY_SIZE(supplies), supplies);
// convenience helper to call regulator_put() on multiple regulators
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
digital = regulator_get(dev, "Vcc"); /* digital core */
analog = regulator_get(dev, "Avdd"); /* analog */
The regulator access functions regulator_get() and regulator_put() will
usually be called in your device drivers probe() and remove() respectively.
@@ -51,11 +59,21 @@ A consumer can determine if a regulator is enabled by calling::
This will return > zero when the regulator is enabled.
A set of regulators can be enabled with a single bulk operation ::
int regulator_bulk_enable(int num_consumers,
struct regulator_bulk_data *consumers);
A consumer can disable its supply when no longer needed by calling::
int regulator_disable(regulator);
Or a number of them ::
int regulator_bulk_disable(int num_consumers,
struct regulator_bulk_data *consumers);
NOTE:
This may not disable the supply if it's shared with other consumers. The
regulator will only be disabled when the enabled reference count is zero.
@@ -64,11 +82,15 @@ Finally, a regulator can be forcefully disabled in the case of an emergency::
int regulator_force_disable(regulator);
This operation is also supported for multiple regulators ::
int regulator_bulk_force_disable(int num_consumers,
struct regulator_bulk_data *consumers);
NOTE:
this will immediately and forcefully shutdown the regulator output. All
consumers will be powered off.
3. Regulator Voltage Control & Status (dynamic drivers)
=======================================================

View File

@@ -15117,6 +15117,13 @@ F: Documentation/devicetree/bindings/*/*max77802.yaml
F: drivers/regulator/max77802-regulator.c
F: include/dt-bindings/*/*max77802.h
MAXIM MAX77838 PMIC REGULATOR DEVICE DRIVER
M: Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/regulator/maxim,max77838.yaml
F: drivers/regulator/max77838-regulator.c
MAXIM MAX77976 BATTERY CHARGER
M: Luca Ceresoli <luca@lucaceresoli.net>
S: Supported
@@ -18369,6 +18376,12 @@ F: Documentation/devicetree/bindings/clock/*imx*
F: drivers/clk/imx/
F: include/dt-bindings/clock/*imx*
NXP PF5300/PF5301/PF5302 PMIC REGULATOR DEVICE DRIVER
M: Woodrow Douglass <wdouglass@carnegierobotics.com>
S: Maintained
F: Documentation/devicetree/bindings/regulator/nxp,pf5300.yaml
F: drivers/regulator/pf530x-regulator.c
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
@@ -22496,7 +22509,7 @@ F: Documentation/devicetree/bindings/regulator/samsung,s2m*.yaml
F: Documentation/devicetree/bindings/regulator/samsung,s5m*.yaml
F: drivers/clk/clk-s2mps11.c
F: drivers/mfd/sec*.[ch]
F: drivers/regulator/s2m*.c
F: drivers/regulator/s2*.c
F: drivers/regulator/s5m*.c
F: drivers/rtc/rtc-s5m.c
F: include/linux/mfd/samsung/

View File

@@ -777,6 +777,15 @@ config REGULATOR_MAX77826
It includes support for control of output voltage. This
regulator is found on the Samsung Galaxy S5 (klte) smartphone.
config REGULATOR_MAX77838
tristate "Maxim 77838 regulator"
depends on I2C
select REGMAP_I2C
help
This driver controls a Maxim 77838 regulator via I2C bus.
The regulator include 4 LDOs and a BUCK regulator. It's
present on the Samsung Galaxy S7 lineup of smartphones.
config REGULATOR_MC13XXX_CORE
tristate
@@ -1006,6 +1015,26 @@ config REGULATOR_PCAP
This driver provides support for the voltage regulators of the
PCAP2 PMIC.
config REGULATOR_PF0900
tristate "NXP PF0900/PF0901/PF09XX regulator driver"
depends on I2C
select REGMAP_I2C
help
Say y here to support the NXP PF0900/PF0901/PF09XX PMIC
regulator driver.
config REGULATOR_PF530X
tristate "NXP PF5300/PF5301/PF5302 regulator driver"
depends on I2C && OF
select REGMAP_I2C
help
Say y here to support the regulators found on the NXP
PF5300/PF5301/PF5302 PMIC.
Say M here if you want to support for the regulators found
on the NXP PF5300/PF5301/PF5302 PMIC. The module will be named
"pf530x-regulator".
config REGULATOR_PF8X00
tristate "NXP PF8100/PF8121A/PF8200 regulator driver"
depends on I2C && OF
@@ -1240,6 +1269,18 @@ config REGULATOR_RT5120
600mV to 1395mV, per step 6.250mV. The others are all fixed voltage
by external hardware circuit.
config REGULATOR_RT5133
tristate "Richtek RT5133 PMIC Regulators"
depends on I2C && GPIOLIB && OF
select REGMAP
select CRC8
select OF_GPIO
help
This driver adds support for RT5133 PMIC regulators.
RT5133 is an integrated chip. It includes 8 LDOs and 3 GPOs that
can be used to drive output high/low purpose. The dependency of the
GPO block is internally LDO1 Voltage.
config REGULATOR_RT5190A
tristate "Richtek RT5190A PMIC"
depends on I2C
@@ -1344,6 +1385,14 @@ config REGULATOR_RTQ2208
and two ldos. It features wide output voltage range from 0.4V to 2.05V
and the capability to configure the corresponding power stages.
config REGULATOR_S2DOS05
tristate "Samsung S2DOS05 voltage regulator"
depends on MFD_SEC_CORE || COMPILE_TEST
help
This driver provides support for the voltage regulators of the S2DOS05.
The S2DOS05 is a companion power management IC for the smart phones.
The S2DOS05 has 4 LDOs and 1 BUCK outputs.
config REGULATOR_S2MPA01
tristate "Samsung S2MPA01 voltage regulator"
depends on MFD_SEC_CORE || COMPILE_TEST
@@ -1395,6 +1444,19 @@ config REGULATOR_SLG51000
The SLG51000 is seven compact and customizable low dropout
regulators.
config REGULATOR_SPACEMIT_P1
tristate "SpacemiT P1 regulators"
depends on ARCH_SPACEMIT || COMPILE_TEST
depends on I2C
select MFD_SPACEMIT_P1
default ARCH_SPACEMIT
help
Enable support for regulators implemented by the SpacemiT P1
power controller. The P1 implements 6 high-efficiency buck
converters and 12 programmable LDO regulators. To compile this
driver as a module, choose M here. The module will be called
"spacemit-pmic".
config REGULATOR_STM32_BOOSTER
tristate "STMicroelectronics STM32 BOOSTER"
depends on ARCH_STM32 || COMPILE_TEST

View File

@@ -92,6 +92,7 @@ obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
obj-$(CONFIG_REGULATOR_MAX77826) += max77826-regulator.o
obj-$(CONFIG_REGULATOR_MAX77838) += max77838-regulator.o
obj-$(CONFIG_REGULATOR_MAX77857) += max77857-regulator.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
@@ -124,7 +125,9 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o
obj-$(CONFIG_REGULATOR_PF0900) += pf0900-regulator.o
obj-$(CONFIG_REGULATOR_PF9453) += pf9453-regulator.o
obj-$(CONFIG_REGULATOR_PF530X) += pf530x-regulator.o
obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
@@ -146,6 +149,7 @@ obj-$(CONFIG_REGULATOR_RT4803) += rt4803.o
obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o
obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
obj-$(CONFIG_REGULATOR_RT5120) += rt5120-regulator.o
obj-$(CONFIG_REGULATOR_RT5133) += rt5133-regulator.o
obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o
obj-$(CONFIG_REGULATOR_RT5739) += rt5739.o
obj-$(CONFIG_REGULATOR_RT5759) += rt5759-regulator.o
@@ -156,12 +160,14 @@ obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o
obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o
obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o
obj-$(CONFIG_REGULATOR_RTQ2208) += rtq2208-regulator.o
obj-$(CONFIG_REGULATOR_S2DOS05) += s2dos05-regulator.o
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o
obj-$(CONFIG_REGULATOR_SPACEMIT_P1) += spacemit-p1.o
obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o
obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o

View File

@@ -1598,7 +1598,7 @@ static int setup_feedback_loop(struct device *dev, struct device_node *np,
if (desc->n_linear_ranges && desc->linear_ranges) {
struct linear_range *new;
new = devm_kzalloc(dev, desc->n_linear_ranges *
new = devm_kcalloc(dev, desc->n_linear_ranges,
sizeof(struct linear_range),
GFP_KERNEL);
if (!new)

View File

@@ -1586,8 +1586,8 @@ static int set_machine_constraints(struct regulator_dev *rdev)
}
if (rdev->constraints->active_discharge && ops->set_active_discharge) {
bool ad_state = (rdev->constraints->active_discharge ==
REGULATOR_ACTIVE_DISCHARGE_ENABLE) ? true : false;
bool ad_state = rdev->constraints->active_discharge ==
REGULATOR_ACTIVE_DISCHARGE_ENABLE;
ret = ops->set_active_discharge(rdev, ad_state);
if (ret < 0) {

View File

@@ -0,0 +1,221 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// regulator driver for Maxim MAX77838
//
// based on max77826-regulator.c
//
// Copyright (c) 2025, Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
enum max77838_registers {
MAX77838_REG_DEVICE_ID = 0x00,
MAX77838_REG_TOPSYS_STAT,
MAX77838_REG_STAT,
MAX77838_REG_EN,
MAX77838_REG_GPIO_PD_CTRL,
MAX77838_REG_UVLO_CFG1,
/* 0x06 - 0x0B: reserved */
MAX77838_REG_I2C_CFG = 0x0C,
/* 0x0D - 0x0F: reserved */
MAX77838_REG_LDO1_CFG = 0x10,
MAX77838_REG_LDO2_CFG,
MAX77838_REG_LDO3_CFG,
MAX77838_REG_LDO4_CFG,
/* 0x14 - 0x1F: reserved */
MAX77838_REG_BUCK_CFG1 = 0x20,
MAX77838_REG_BUCK_VOUT,
};
enum max77838_regulators {
MAX77838_LDO1 = 0,
MAX77838_LDO2,
MAX77838_LDO3,
MAX77838_LDO4,
MAX77838_BUCK,
MAX77838_MAX_REGULATORS,
};
#define MAX77838_MASK_LDO 0x7f
#define MAX77838_MASK_BUCK 0xff
#define MAX77838_LDO1_EN BIT(0)
#define MAX77838_LDO2_EN BIT(1)
#define MAX77838_LDO3_EN BIT(2)
#define MAX77838_LDO4_EN BIT(3)
#define MAX77838_BUCK_EN BIT(4)
#define MAX77838_BUCK_AD BIT(3)
#define MAX77838_LDO_AD BIT(7)
#define MAX77838_LDO_VOLT_MIN 600000
#define MAX77838_LDO_VOLT_MAX 3775000
#define MAX77838_LDO_VOLT_STEP 25000
#define MAX77838_BUCK_VOLT_MIN 500000
#define MAX77838_BUCK_VOLT_MAX 2093750
#define MAX77838_BUCK_VOLT_STEP 6250
#define MAX77838_VOLT_RANGE(_type) \
((MAX77838_ ## _type ## _VOLT_MAX - \
MAX77838_ ## _type ## _VOLT_MIN) / \
MAX77838_ ## _type ## _VOLT_STEP + 1)
#define MAX77838_LDO(_id) \
[MAX77838_LDO ## _id] = { \
.id = MAX77838_LDO ## _id, \
.name = "ldo"#_id, \
.of_match = of_match_ptr("ldo"#_id), \
.regulators_node = "regulators", \
.ops = &max77838_regulator_ops, \
.min_uV = MAX77838_LDO_VOLT_MIN, \
.uV_step = MAX77838_LDO_VOLT_STEP, \
.n_voltages = MAX77838_VOLT_RANGE(LDO), \
.enable_reg = MAX77838_REG_EN, \
.enable_mask = MAX77838_LDO ## _id ## _EN, \
.vsel_reg = MAX77838_REG_LDO ## _id ## _CFG, \
.vsel_mask = MAX77838_MASK_LDO, \
.active_discharge_off = 0, \
.active_discharge_on = MAX77838_LDO_AD, \
.active_discharge_mask = MAX77838_LDO_AD, \
.active_discharge_reg = MAX77838_REG_LDO ## _id ## _CFG, \
.owner = THIS_MODULE, \
}
#define MAX77838_BUCK_DESC \
[MAX77838_BUCK] = { \
.id = MAX77838_BUCK, \
.name = "buck", \
.of_match = of_match_ptr("buck"), \
.regulators_node = "regulators", \
.ops = &max77838_regulator_ops, \
.min_uV = MAX77838_BUCK_VOLT_MIN, \
.uV_step = MAX77838_BUCK_VOLT_STEP, \
.n_voltages = MAX77838_VOLT_RANGE(BUCK), \
.enable_reg = MAX77838_REG_EN, \
.enable_mask = MAX77838_BUCK_EN, \
.vsel_reg = MAX77838_REG_BUCK_VOUT, \
.vsel_mask = MAX77838_MASK_BUCK, \
.active_discharge_off = 0, \
.active_discharge_on = MAX77838_BUCK_AD, \
.active_discharge_mask = MAX77838_BUCK_AD, \
.active_discharge_reg = MAX77838_REG_BUCK_CFG1, \
.owner = THIS_MODULE, \
}
struct max77838_regulator_info {
struct regmap *regmap;
};
static const struct regmap_config max77838_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77838_REG_BUCK_VOUT,
};
static const struct regulator_ops max77838_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_active_discharge = regulator_set_active_discharge_regmap,
};
static const struct regulator_desc max77838_regulators_desc[] = {
MAX77838_LDO(1),
MAX77838_LDO(2),
MAX77838_LDO(3),
MAX77838_LDO(4),
MAX77838_BUCK_DESC,
};
static int max77838_read_device_id(struct regmap *regmap, struct device *dev)
{
unsigned int device_id;
int ret;
ret = regmap_read(regmap, MAX77838_REG_DEVICE_ID, &device_id);
if (!ret)
dev_dbg(dev, "DEVICE_ID: 0x%x\n", device_id);
return ret;
}
static int max77838_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct max77838_regulator_info *info;
struct regulator_config config = {};
struct regulator_dev *rdev;
struct regmap *regmap;
int i;
info = devm_kzalloc(dev, sizeof(struct max77838_regulator_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &max77838_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to allocate regmap!\n");
return PTR_ERR(regmap);
}
info->regmap = regmap;
i2c_set_clientdata(client, info);
config.dev = dev;
config.regmap = regmap;
config.driver_data = info;
for (i = 0; i < MAX77838_MAX_REGULATORS; i++) {
rdev = devm_regulator_register(dev,
&max77838_regulators_desc[i],
&config);
if (IS_ERR(rdev)) {
dev_err(dev, "Failed to register regulator!\n");
return PTR_ERR(rdev);
}
}
return max77838_read_device_id(regmap, dev);
}
static const struct of_device_id __maybe_unused max77838_of_match[] = {
{ .compatible = "maxim,max77838" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, max77838_of_match);
static const struct i2c_device_id max77838_id[] = {
{ "max77838-regulator" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, max77838_id);
static struct i2c_driver max77838_regulator_driver = {
.driver = {
.name = "max77838",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(max77838_of_match),
},
.probe = max77838_i2c_probe,
.id_table = max77838_id,
};
module_i2c_driver(max77838_regulator_driver);
MODULE_AUTHOR("Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>");
MODULE_DESCRIPTION("MAX77838 PMIC regulator driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,975 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright 2025 NXP.
// NXP PF0900 pmic driver
#include <linux/bitfield.h>
#include <linux/crc8.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
enum pf0900_regulators {
PF0900_SW1 = 0,
PF0900_SW2,
PF0900_SW3,
PF0900_SW4,
PF0900_SW5,
PF0900_LDO1,
PF0900_LDO2,
PF0900_LDO3,
PF0900_VAON,
PF0900_REGULATOR_CNT,
};
enum {
PF0900_DVS_LEVEL_RUN = 0,
PF0900_DVS_LEVEL_STANDBY,
PF0900_DVS_LEVEL_MAX,
};
#define PF0900_VAON_VOLTAGE_NUM 0x03
#define PF0900_SW_VOLTAGE_NUM 0x100
#define PF0900_LDO_VOLTAGE_NUM 0x20
#define REGU_SW_CNT 0x5
#define REGU_LDO_VAON_CNT 0x4
enum {
PF0900_REG_DEV_ID = 0x00,
PF0900_REG_DEV_FAM = 0x01,
PF0900_REG_REV_ID = 0x02,
PF0900_REG_PROG_ID1 = 0x03,
PF0900_REG_PROG_ID2 = 0x04,
PF0900_REG_SYSTEM_INT = 0x05,
PF0900_REG_STATUS1_INT = 0x06,
PF0900_REG_STATUS1_MSK = 0x07,
PF0900_REG_STATUS1_SNS = 0x08,
PF0900_REG_STATUS2_INT = 0x09,
PF0900_REG_STATUS2_MSK = 0x0A,
PF0900_REG_STATUS2_SNS = 0x0B,
PF0900_REG_STATUS3_INT = 0x0C,
PF0900_REG_STATUS3_MSK = 0x0D,
PF0900_REG_SW_MODE_INT = 0x0E,
PF0900_REG_SW_MODE_MSK = 0x0F,
PF0900_REG_SW_ILIM_INT = 0x10,
PF0900_REG_SW_ILIM_MSK = 0x11,
PF0900_REG_SW_ILIM_SNS = 0x12,
PF0900_REG_LDO_ILIM_INT = 0x13,
PF0900_REG_LDO_ILIM_MSK = 0x14,
PF0900_REG_LDO_ILIM_SNS = 0x15,
PF0900_REG_SW_UV_INT = 0x16,
PF0900_REG_SW_UV_MSK = 0x17,
PF0900_REG_SW_UV_SNS = 0x18,
PF0900_REG_SW_OV_INT = 0x19,
PF0900_REG_SW_OV_MSK = 0x1A,
PF0900_REG_SW_OV_SNS = 0x1B,
PF0900_REG_LDO_UV_INT = 0x1C,
PF0900_REG_LDO_UV_MSK = 0x1D,
PF0900_REG_LDO_UV_SNS = 0x1E,
PF0900_REG_LDO_OV_INT = 0x1F,
PF0900_REG_LDO_OV_MSK = 0x20,
PF0900_REG_LDO_OV_SNS = 0x21,
PF0900_REG_PWRON_INT = 0x22,
PF0900_REG_IO_INT = 0x24,
PF0900_REG_IO_MSK = 0x25,
PF0900_REG_IO_SNS = 0x26,
PF0900_REG_IOSHORT_SNS = 0x27,
PF0900_REG_ABIST_OV1 = 0x28,
PF0900_REG_ABIST_OV2 = 0x29,
PF0900_REG_ABIST_UV1 = 0x2A,
PF0900_REG_ABIST_UV2 = 0x2B,
PF0900_REG_ABIST_IO = 0x2C,
PF0900_REG_TEST_FLAGS = 0x2D,
PF0900_REG_HFAULT_FLAGS = 0x2E,
PF0900_REG_FAULT_FLAGS = 0x2F,
PF0900_REG_FS0B_CFG = 0x30,
PF0900_REG_FCCU_CFG = 0x31,
PF0900_REG_RSTB_CFG1 = 0x32,
PF0900_REG_SYSTEM_CMD = 0x33,
PF0900_REG_FS0B_CMD = 0x34,
PF0900_REG_SECURE_WR1 = 0x35,
PF0900_REG_SECURE_WR2 = 0x36,
PF0900_REG_VMON_CFG1 = 0x37,
PF0900_REG_SYS_CFG1 = 0x38,
PF0900_REG_GPO_CFG = 0x39,
PF0900_REG_GPO_CTRL = 0x3A,
PF0900_REG_PWRUP_CFG = 0x3B,
PF0900_REG_RSTB_PWRUP = 0x3C,
PF0900_REG_GPIO1_PWRUP = 0x3D,
PF0900_REG_GPIO2_PWRUP = 0x3E,
PF0900_REG_GPIO3_PWRUP = 0x3F,
PF0900_REG_GPIO4_PWRUP = 0x40,
PF0900_REG_VMON1_PWRUP = 0x41,
PF0900_REG_VMON2_PWRUP = 0x42,
PF0900_REG_SW1_PWRUP = 0x43,
PF0900_REG_SW2_PWRUP = 0x44,
PF0900_REG_SW3_PWRUP = 0x45,
PF0900_REG_SW4_PWRUP = 0x46,
PF0900_REG_SW5_PWRUP = 0x47,
PF0900_REG_LDO1_PWRUP = 0x48,
PF0900_REG_LDO2_PWRUP = 0x49,
PF0900_REG_LDO3_PWRUP = 0x4A,
PF0900_REG_VAON_PWRUP = 0x4B,
PF0900_REG_FREQ_CTRL = 0x4C,
PF0900_REG_PWRON_CFG = 0x4D,
PF0900_REG_WD_CTRL1 = 0x4E,
PF0900_REG_WD_CTRL2 = 0x4F,
PF0900_REG_WD_CFG1 = 0x50,
PF0900_REG_WD_CFG2 = 0x51,
PF0900_REG_WD_CNT1 = 0x52,
PF0900_REG_WD_CNT2 = 0x53,
PF0900_REG_FAULT_CFG = 0x54,
PF0900_REG_FAULT_CNT = 0x55,
PF0900_REG_DFS_CNT = 0x56,
PF0900_REG_AMUX_CFG = 0x57,
PF0900_REG_VMON1_RUN_CFG = 0x58,
PF0900_REG_VMON1_STBY_CFG = 0x59,
PF0900_REG_VMON1_CTRL = 0x5A,
PF0900_REG_VMON2_RUN_CFG = 0x5B,
PF0900_REG_VMON2_STBY_CFG = 0x5C,
PF0900_REG_VMON2_CTRL = 0x5D,
PF0900_REG_SW1_VRUN = 0x5E,
PF0900_REG_SW1_VSTBY = 0x5F,
PF0900_REG_SW1_MODE = 0x60,
PF0900_REG_SW1_CFG1 = 0x61,
PF0900_REG_SW1_CFG2 = 0x62,
PF0900_REG_SW2_VRUN = 0x63,
PF0900_REG_SW2_VSTBY = 0x64,
PF0900_REG_SW2_MODE = 0x65,
PF0900_REG_SW2_CFG1 = 0x66,
PF0900_REG_SW2_CFG2 = 0x67,
PF0900_REG_SW3_VRUN = 0x68,
PF0900_REG_SW3_VSTBY = 0x69,
PF0900_REG_SW3_MODE = 0x6A,
PF0900_REG_SW3_CFG1 = 0x6B,
PF0900_REG_SW3_CFG2 = 0x6C,
PF0900_REG_SW4_VRUN = 0x6D,
PF0900_REG_SW4_VSTBY = 0x6E,
PF0900_REG_SW4_MODE = 0x6F,
PF0900_REG_SW4_CFG1 = 0x70,
PF0900_REG_SW4_CFG2 = 0x71,
PF0900_REG_SW5_VRUN = 0x72,
PF0900_REG_SW5_VSTBY = 0x73,
PF0900_REG_SW5_MODE = 0x74,
PF0900_REG_SW5_CFG1 = 0x75,
PF0900_REG_SW5_CFG2 = 0x76,
PF0900_REG_LDO1_RUN = 0x77,
PF0900_REG_LDO1_STBY = 0x78,
PF0900_REG_LDO1_CFG2 = 0x79,
PF0900_REG_LDO2_RUN = 0x7A,
PF0900_REG_LDO2_STBY = 0x7B,
PF0900_REG_LDO2_CFG2 = 0x7C,
PF0900_REG_LDO3_RUN = 0x7D,
PF0900_REG_LDO3_STBY = 0x7E,
PF0900_REG_LDO3_CFG2 = 0x7F,
PF0900_REG_VAON_CFG1 = 0x80,
PF0900_REG_VAON_CFG2 = 0x81,
PF0900_REG_SYS_DIAG = 0x82,
PF0900_MAX_REGISTER,
};
/* PF0900 SW MODE */
#define SW_RUN_MODE_OFF 0x00
#define SW_RUN_MODE_PWM 0x01
#define SW_RUN_MODE_PFM 0x02
#define SW_STBY_MODE_OFF 0x00
#define SW_STBY_MODE_PWM 0x04
#define SW_STBY_MODE_PFM 0x08
/* PF0900 SW MODE MASK */
#define SW_RUN_MODE_MASK GENMASK(1, 0)
#define SW_STBY_MODE_MASK GENMASK(3, 2)
/* PF0900 SW VRUN/VSTBY MASK */
#define PF0900_SW_VOL_MASK GENMASK(7, 0)
/* PF0900_REG_VAON_CFG1 bits */
#define PF0900_VAON_1P8V 0x01
#define PF0900_VAON_MASK GENMASK(1, 0)
/* PF0900_REG_SWX_CFG1 MASK */
#define PF0900_SW_DVS_MASK GENMASK(4, 3)
/* PF0900_REG_LDO_RUN MASK */
#define VLDO_RUN_MASK GENMASK(4, 0)
#define LDO_RUN_EN_MASK BIT(5)
/* PF0900_REG_STATUS1_INT bits */
#define PF0900_IRQ_PWRUP BIT(3)
/* PF0900_REG_ILIM_INT bits */
#define PF0900_IRQ_SW1_IL BIT(0)
#define PF0900_IRQ_SW2_IL BIT(1)
#define PF0900_IRQ_SW3_IL BIT(2)
#define PF0900_IRQ_SW4_IL BIT(3)
#define PF0900_IRQ_SW5_IL BIT(4)
#define PF0900_IRQ_LDO1_IL BIT(0)
#define PF0900_IRQ_LDO2_IL BIT(1)
#define PF0900_IRQ_LDO3_IL BIT(2)
/* PF0900_REG_UV_INT bits */
#define PF0900_IRQ_SW1_UV BIT(0)
#define PF0900_IRQ_SW2_UV BIT(1)
#define PF0900_IRQ_SW3_UV BIT(2)
#define PF0900_IRQ_SW4_UV BIT(3)
#define PF0900_IRQ_SW5_UV BIT(4)
#define PF0900_IRQ_LDO1_UV BIT(0)
#define PF0900_IRQ_LDO2_UV BIT(1)
#define PF0900_IRQ_LDO3_UV BIT(2)
#define PF0900_IRQ_VAON_UV BIT(3)
/* PF0900_REG_OV_INT bits */
#define PF0900_IRQ_SW1_OV BIT(0)
#define PF0900_IRQ_SW2_OV BIT(1)
#define PF0900_IRQ_SW3_OV BIT(2)
#define PF0900_IRQ_SW4_OV BIT(3)
#define PF0900_IRQ_SW5_OV BIT(4)
#define PF0900_IRQ_LDO1_OV BIT(0)
#define PF0900_IRQ_LDO2_OV BIT(1)
#define PF0900_IRQ_LDO3_OV BIT(2)
#define PF0900_IRQ_VAON_OV BIT(3)
struct pf0900_regulator_desc {
struct regulator_desc desc;
unsigned int suspend_enable_mask;
unsigned int suspend_voltage_reg;
unsigned int suspend_voltage_cache;
};
struct pf0900_drvdata {
const struct pf0900_regulator_desc *desc;
unsigned int rcnt;
};
struct pf0900 {
struct device *dev;
struct regmap *regmap;
const struct pf0900_drvdata *drvdata;
struct regulator_dev *rdevs[PF0900_REGULATOR_CNT];
int irq;
unsigned short addr;
bool crc_en;
};
enum pf0900_regulator_type {
PF0900_SW = 0,
PF0900_LDO,
};
#define PF0900_REGU_IRQ(_reg, _type, _event) \
{ \
.reg = _reg, \
.type = _type, \
.event = _event, \
}
struct pf0900_regulator_irq {
unsigned int reg;
unsigned int type;
unsigned int event;
};
static const struct regmap_range pf0900_range = {
.range_min = PF0900_REG_DEV_ID,
.range_max = PF0900_REG_SYS_DIAG,
};
static const struct regmap_access_table pf0900_volatile_regs = {
.yes_ranges = &pf0900_range,
.n_yes_ranges = 1,
};
static const struct regmap_config pf0900_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &pf0900_volatile_regs,
.max_register = PF0900_MAX_REGISTER - 1,
.cache_type = REGCACHE_MAPLE,
};
static uint8_t crc8_j1850(unsigned short addr, unsigned int reg,
unsigned int val)
{
uint8_t crcBuf[3];
uint8_t t_crc;
uint8_t i, j;
crcBuf[0] = addr;
crcBuf[1] = reg;
crcBuf[2] = val;
t_crc = 0xFF;
/*
* The CRC calculation is based on the standard CRC-8-SAE as
* defined in the SAE-J1850 specification with the following
* characteristics.
* Polynomial = 0x1D
* Initial Value = 0xFF
* The CRC byte is calculated by shifting 24-bit data through
* the CRC polynomial.The 24-bits package is built as follows:
* DEVICE_ADDR[b8] + REGISTER_ADDR [b8] +DATA[b8]
* The DEVICE_ADDR is calculated as the 7-bit slave address
* shifted left one space plus the corresponding read/write bit.
* (7Bit Address [b7] << 1 ) + R/W = DEVICE_ADDR[b8]
*/
for (i = 0; i < sizeof(crcBuf); i++) {
t_crc ^= crcBuf[i];
for (j = 0; j < 8; j++) {
if ((t_crc & 0x80) != 0) {
t_crc <<= 1;
t_crc ^= 0x1D;
} else {
t_crc <<= 1;
}
}
}
return t_crc;
}
static int pf0900_regmap_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct pf0900 *pf0900 = dev_get_drvdata(dev);
int ret;
u8 crc;
if (!pf0900 || !pf0900->dev)
return -EINVAL;
if (reg >= PF0900_MAX_REGISTER) {
dev_err(pf0900->dev, "Invalid register address: 0x%x\n", reg);
return -EINVAL;
}
if (pf0900->crc_en) {
ret = i2c_smbus_read_word_data(i2c, reg);
if (ret < 0) {
dev_err(pf0900->dev, "Read error at reg=0x%x: %d\n", reg, ret);
return ret;
}
*val = (u16)ret;
crc = crc8_j1850(pf0900->addr << 1 | 0x1, reg, FIELD_GET(GENMASK(7, 0), *val));
if (crc != FIELD_GET(GENMASK(15, 8), *val)) {
dev_err(pf0900->dev, "Crc check error!\n");
return -EINVAL;
}
*val = FIELD_GET(GENMASK(7, 0), *val);
} else {
ret = i2c_smbus_read_byte_data(i2c, reg);
if (ret < 0) {
dev_err(pf0900->dev, "Read error at reg=0x%x: %d\n", reg, ret);
return ret;
}
*val = ret;
}
return 0;
}
static int pf0900_regmap_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct pf0900 *pf0900 = dev_get_drvdata(dev);
uint8_t data[2];
int ret;
if (!pf0900 || !pf0900->dev)
return -EINVAL;
if (reg >= PF0900_MAX_REGISTER) {
dev_err(pf0900->dev, "Invalid register address: 0x%x\n", reg);
return -EINVAL;
}
data[0] = val;
if (pf0900->crc_en) {
/* Get CRC */
data[1] = crc8_j1850(pf0900->addr << 1, reg, data[0]);
val = FIELD_PREP(GENMASK(15, 8), data[1]) | data[0];
ret = i2c_smbus_write_word_data(i2c, reg, val);
} else {
ret = i2c_smbus_write_byte_data(i2c, reg, data[0]);
}
if (ret) {
dev_err(pf0900->dev, "Write reg=0x%x error!\n", reg);
return ret;
}
return 0;
}
static int pf0900_suspend_enable(struct regulator_dev *rdev)
{
struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev);
struct regmap *rmap = rdev_get_regmap(rdev);
return regmap_update_bits(rmap, rdata->desc.enable_reg,
rdata->suspend_enable_mask, SW_STBY_MODE_PFM);
}
static int pf0900_suspend_disable(struct regulator_dev *rdev)
{
struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev);
struct regmap *rmap = rdev_get_regmap(rdev);
return regmap_update_bits(rmap, rdata->desc.enable_reg,
rdata->suspend_enable_mask, SW_STBY_MODE_OFF);
}
static int pf0900_set_suspend_voltage(struct regulator_dev *rdev, int uV)
{
struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev);
struct regmap *rmap = rdev_get_regmap(rdev);
int ret;
if (rdata->suspend_voltage_cache == uV)
return 0;
ret = regulator_map_voltage_iterate(rdev, uV, uV);
if (ret < 0) {
dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV);
return ret;
}
dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n",
uV, rdata->suspend_voltage_reg, rdata->desc.vsel_mask, ret);
ret = regmap_update_bits(rmap, rdata->suspend_voltage_reg,
rdata->desc.vsel_mask, ret);
if (ret < 0) {
dev_err(rdev_get_dev(rdev), "failed to set %i uV\n", uV);
return ret;
}
rdata->suspend_voltage_cache = uV;
return 0;
}
static const struct regmap_bus pf0900_regmap_bus = {
.reg_read = pf0900_regmap_read,
.reg_write = pf0900_regmap_write,
};
static const struct regulator_ops pf0900_avon_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
static const struct regulator_ops pf0900_dvs_sw_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = regulator_set_ramp_delay_regmap,
.set_suspend_enable = pf0900_suspend_enable,
.set_suspend_disable = pf0900_suspend_disable,
.set_suspend_voltage = pf0900_set_suspend_voltage,
};
static const struct regulator_ops pf0900_ldo_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
/*
* SW1/2/3/4/5
* SW1_DVS[1:0] SW1 DVS ramp rate setting
* 00: 15.6mV/8usec
* 01: 15.6mV/4usec
* 10: 15.6mV/2usec
* 11: 15.6mV/1usec
*/
static const unsigned int pf0900_dvs_sw_ramp_table[] = {
1950, 3900, 7800, 15600
};
/* VAON 1.8V, 3.0V, or 3.3V */
static const int pf0900_vaon_voltages[] = {
0, 1800000, 3000000, 3300000,
};
/*
* SW1 0.5V to 3.3V
* 0.5V to 1.35V (6.25mV step)
* 1.8V to 2.5V (125mV step)
* 2.8V to 3.3V (250mV step)
*/
static const struct linear_range pf0900_dvs_sw1_volts[] = {
REGULATOR_LINEAR_RANGE(0, 0x00, 0x08, 0),
REGULATOR_LINEAR_RANGE(500000, 0x09, 0x91, 6250),
REGULATOR_LINEAR_RANGE(0, 0x92, 0x9E, 0),
REGULATOR_LINEAR_RANGE(1500000, 0x9F, 0x9F, 0),
REGULATOR_LINEAR_RANGE(1800000, 0xA0, 0xD8, 12500),
REGULATOR_LINEAR_RANGE(0, 0xD9, 0xDF, 0),
REGULATOR_LINEAR_RANGE(2800000, 0xE0, 0xF4, 25000),
REGULATOR_LINEAR_RANGE(0, 0xF5, 0xFF, 0),
};
/*
* SW2/3/4/5 0.3V to 3.3V
* 0.45V to 1.35V (6.25mV step)
* 1.8V to 2.5V (125mV step)
* 2.8V to 3.3V (250mV step)
*/
static const struct linear_range pf0900_dvs_sw2345_volts[] = {
REGULATOR_LINEAR_RANGE(300000, 0x00, 0x00, 0),
REGULATOR_LINEAR_RANGE(450000, 0x01, 0x91, 6250),
REGULATOR_LINEAR_RANGE(0, 0x92, 0x9E, 0),
REGULATOR_LINEAR_RANGE(1500000, 0x9F, 0x9F, 0),
REGULATOR_LINEAR_RANGE(1800000, 0xA0, 0xD8, 12500),
REGULATOR_LINEAR_RANGE(0, 0xD9, 0xDF, 0),
REGULATOR_LINEAR_RANGE(2800000, 0xE0, 0xF4, 25000),
REGULATOR_LINEAR_RANGE(0, 0xF5, 0xFF, 0),
};
/*
* LDO1
* 0.75V to 3.3V
*/
static const struct linear_range pf0900_ldo1_volts[] = {
REGULATOR_LINEAR_RANGE(750000, 0x00, 0x0F, 50000),
REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1F, 100000),
};
/*
* LDO2/3
* 0.65V to 3.3V (50mV step)
*/
static const struct linear_range pf0900_ldo23_volts[] = {
REGULATOR_LINEAR_RANGE(650000, 0x00, 0x0D, 50000),
REGULATOR_LINEAR_RANGE(1400000, 0x0E, 0x0F, 100000),
REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1F, 100000),
};
static const struct pf0900_regulator_desc pf0900_regulators[] = {
{
.desc = {
.name = "sw1",
.of_match = of_match_ptr("sw1"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_SW1,
.ops = &pf0900_dvs_sw_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_SW_VOLTAGE_NUM,
.linear_ranges = pf0900_dvs_sw1_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw1_volts),
.vsel_reg = PF0900_REG_SW1_VRUN,
.vsel_mask = PF0900_SW_VOL_MASK,
.enable_reg = PF0900_REG_SW1_MODE,
.enable_mask = SW_RUN_MODE_MASK,
.enable_val = SW_RUN_MODE_PWM,
.ramp_reg = PF0900_REG_SW1_CFG1,
.ramp_mask = PF0900_SW_DVS_MASK,
.ramp_delay_table = pf0900_dvs_sw_ramp_table,
.n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
.owner = THIS_MODULE,
},
.suspend_enable_mask = SW_STBY_MODE_MASK,
.suspend_voltage_reg = PF0900_REG_SW1_VSTBY,
},
{
.desc = {
.name = "sw2",
.of_match = of_match_ptr("sw2"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_SW2,
.ops = &pf0900_dvs_sw_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_SW_VOLTAGE_NUM,
.linear_ranges = pf0900_dvs_sw2345_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
.vsel_reg = PF0900_REG_SW2_VRUN,
.vsel_mask = PF0900_SW_VOL_MASK,
.enable_reg = PF0900_REG_SW2_MODE,
.enable_mask = SW_RUN_MODE_MASK,
.enable_val = SW_RUN_MODE_PWM,
.ramp_reg = PF0900_REG_SW2_CFG1,
.ramp_mask = PF0900_SW_DVS_MASK,
.ramp_delay_table = pf0900_dvs_sw_ramp_table,
.n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
.owner = THIS_MODULE,
},
.suspend_enable_mask = SW_STBY_MODE_MASK,
.suspend_voltage_reg = PF0900_REG_SW2_VSTBY,
},
{
.desc = {
.name = "sw3",
.of_match = of_match_ptr("sw3"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_SW3,
.ops = &pf0900_dvs_sw_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_SW_VOLTAGE_NUM,
.linear_ranges = pf0900_dvs_sw2345_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
.vsel_reg = PF0900_REG_SW3_VRUN,
.vsel_mask = PF0900_SW_VOL_MASK,
.enable_reg = PF0900_REG_SW3_MODE,
.enable_mask = SW_RUN_MODE_MASK,
.enable_val = SW_RUN_MODE_PWM,
.ramp_reg = PF0900_REG_SW3_CFG1,
.ramp_mask = PF0900_SW_DVS_MASK,
.ramp_delay_table = pf0900_dvs_sw_ramp_table,
.n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
.owner = THIS_MODULE,
},
.suspend_enable_mask = SW_STBY_MODE_MASK,
.suspend_voltage_reg = PF0900_REG_SW3_VSTBY,
},
{
.desc = {
.name = "sw4",
.of_match = of_match_ptr("sw4"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_SW4,
.ops = &pf0900_dvs_sw_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_SW_VOLTAGE_NUM,
.linear_ranges = pf0900_dvs_sw2345_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
.vsel_reg = PF0900_REG_SW4_VRUN,
.vsel_mask = PF0900_SW_VOL_MASK,
.enable_reg = PF0900_REG_SW4_MODE,
.enable_mask = SW_RUN_MODE_MASK,
.enable_val = SW_RUN_MODE_PWM,
.ramp_reg = PF0900_REG_SW4_CFG1,
.ramp_mask = PF0900_SW_DVS_MASK,
.ramp_delay_table = pf0900_dvs_sw_ramp_table,
.n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
.owner = THIS_MODULE,
},
.suspend_enable_mask = SW_STBY_MODE_MASK,
.suspend_voltage_reg = PF0900_REG_SW4_VSTBY,
},
{
.desc = {
.name = "sw5",
.of_match = of_match_ptr("sw5"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_SW5,
.ops = &pf0900_dvs_sw_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_SW_VOLTAGE_NUM,
.linear_ranges = pf0900_dvs_sw2345_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts),
.vsel_reg = PF0900_REG_SW5_VRUN,
.vsel_mask = PF0900_SW_VOL_MASK,
.enable_reg = PF0900_REG_SW5_MODE,
.enable_mask = SW_RUN_MODE_MASK,
.enable_val = SW_RUN_MODE_PWM,
.ramp_reg = PF0900_REG_SW5_CFG1,
.ramp_mask = PF0900_SW_DVS_MASK,
.ramp_delay_table = pf0900_dvs_sw_ramp_table,
.n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table),
.owner = THIS_MODULE,
},
.suspend_enable_mask = SW_STBY_MODE_MASK,
.suspend_voltage_reg = PF0900_REG_SW5_VSTBY,
},
{
.desc = {
.name = "ldo1",
.of_match = of_match_ptr("ldo1"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_LDO1,
.ops = &pf0900_ldo_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_LDO_VOLTAGE_NUM,
.linear_ranges = pf0900_ldo1_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_ldo1_volts),
.vsel_reg = PF0900_REG_LDO1_RUN,
.vsel_mask = VLDO_RUN_MASK,
.enable_reg = PF0900_REG_LDO1_RUN,
.enable_mask = LDO_RUN_EN_MASK,
.owner = THIS_MODULE,
},
},
{
.desc = {
.name = "ldo2",
.of_match = of_match_ptr("ldo2"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_LDO2,
.ops = &pf0900_ldo_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_LDO_VOLTAGE_NUM,
.linear_ranges = pf0900_ldo23_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_ldo23_volts),
.vsel_reg = PF0900_REG_LDO2_RUN,
.vsel_mask = VLDO_RUN_MASK,
.enable_reg = PF0900_REG_LDO2_RUN,
.enable_mask = LDO_RUN_EN_MASK,
.owner = THIS_MODULE,
},
},
{
.desc = {
.name = "ldo3",
.of_match = of_match_ptr("ldo3"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_LDO3,
.ops = &pf0900_ldo_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_LDO_VOLTAGE_NUM,
.linear_ranges = pf0900_ldo23_volts,
.n_linear_ranges = ARRAY_SIZE(pf0900_ldo23_volts),
.vsel_reg = PF0900_REG_LDO3_RUN,
.vsel_mask = VLDO_RUN_MASK,
.enable_reg = PF0900_REG_LDO3_RUN,
.enable_mask = LDO_RUN_EN_MASK,
.owner = THIS_MODULE,
},
},
{
.desc = {
.name = "vaon",
.of_match = of_match_ptr("vaon"),
.regulators_node = of_match_ptr("regulators"),
.id = PF0900_VAON,
.ops = &pf0900_avon_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PF0900_VAON_VOLTAGE_NUM,
.volt_table = pf0900_vaon_voltages,
.enable_reg = PF0900_REG_VAON_CFG1,
.enable_mask = PF0900_VAON_MASK,
.enable_val = PF0900_VAON_1P8V,
.vsel_reg = PF0900_REG_VAON_CFG1,
.vsel_mask = PF0900_VAON_MASK,
.owner = THIS_MODULE,
},
},
};
struct pf0900_regulator_irq regu_irqs[] = {
PF0900_REGU_IRQ(PF0900_REG_SW_ILIM_INT, PF0900_SW, REGULATOR_ERROR_OVER_CURRENT_WARN),
PF0900_REGU_IRQ(PF0900_REG_LDO_ILIM_INT, PF0900_LDO, REGULATOR_ERROR_OVER_CURRENT_WARN),
PF0900_REGU_IRQ(PF0900_REG_SW_UV_INT, PF0900_SW, REGULATOR_ERROR_UNDER_VOLTAGE_WARN),
PF0900_REGU_IRQ(PF0900_REG_LDO_UV_INT, PF0900_LDO, REGULATOR_ERROR_UNDER_VOLTAGE_WARN),
PF0900_REGU_IRQ(PF0900_REG_SW_OV_INT, PF0900_SW, REGULATOR_ERROR_OVER_VOLTAGE_WARN),
PF0900_REGU_IRQ(PF0900_REG_LDO_OV_INT, PF0900_LDO, REGULATOR_ERROR_OVER_VOLTAGE_WARN),
};
static irqreturn_t pf0900_irq_handler(int irq, void *data)
{
unsigned int val, regu, i, index;
struct pf0900 *pf0900 = data;
int ret;
for (i = 0; i < ARRAY_SIZE(regu_irqs); i++) {
ret = regmap_read(pf0900->regmap, regu_irqs[i].reg, &val);
if (ret < 0) {
dev_err(pf0900->dev, "Failed to read %d\n", ret);
return IRQ_NONE;
}
if (val) {
ret = regmap_write_bits(pf0900->regmap, regu_irqs[i].reg, val, val);
if (ret < 0) {
dev_err(pf0900->dev, "Failed to update %d\n", ret);
return IRQ_NONE;
}
if (regu_irqs[i].type == PF0900_SW) {
for (index = 0; index < REGU_SW_CNT; index++) {
if (val & BIT(index)) {
regu = (enum pf0900_regulators)index;
regulator_notifier_call_chain(pf0900->rdevs[regu],
regu_irqs[i].event,
NULL);
}
}
} else if (regu_irqs[i].type == PF0900_LDO) {
for (index = 0; index < REGU_LDO_VAON_CNT; index++) {
if (val & BIT(index)) {
regu = (enum pf0900_regulators)index + PF0900_LDO1;
regulator_notifier_call_chain(pf0900->rdevs[regu],
regu_irqs[i].event,
NULL);
}
}
}
}
}
return IRQ_HANDLED;
}
static int pf0900_i2c_probe(struct i2c_client *i2c)
{
const struct pf0900_regulator_desc *regulator_desc;
const struct pf0900_drvdata *drvdata = NULL;
struct device_node *np = i2c->dev.of_node;
unsigned int device_id, device_fam, i;
struct regulator_config config = { };
struct pf0900 *pf0900;
int ret;
if (!i2c->irq)
return dev_err_probe(&i2c->dev, -EINVAL, "No IRQ configured?\n");
pf0900 = devm_kzalloc(&i2c->dev, sizeof(struct pf0900), GFP_KERNEL);
if (!pf0900)
return -ENOMEM;
drvdata = device_get_match_data(&i2c->dev);
if (!drvdata)
return dev_err_probe(&i2c->dev, -EINVAL, "unable to find driver data\n");
regulator_desc = drvdata->desc;
pf0900->drvdata = drvdata;
pf0900->crc_en = of_property_read_bool(np, "nxp,i2c-crc-enable");
pf0900->irq = i2c->irq;
pf0900->dev = &i2c->dev;
pf0900->addr = i2c->addr;
dev_set_drvdata(&i2c->dev, pf0900);
pf0900->regmap = devm_regmap_init(&i2c->dev, &pf0900_regmap_bus, &i2c->dev,
&pf0900_regmap_config);
if (IS_ERR(pf0900->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(pf0900->regmap),
"regmap initialization failed\n");
ret = regmap_read(pf0900->regmap, PF0900_REG_DEV_ID, &device_id);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Read device id error\n");
ret = regmap_read(pf0900->regmap, PF0900_REG_DEV_FAM, &device_fam);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Read device fam error\n");
/* Check your board and dts for match the right pmic */
if (device_fam == 0x09 && (device_id & 0x1F) != 0x0)
return dev_err_probe(&i2c->dev, -EINVAL, "Device id(%x) mismatched\n",
device_id >> 4);
for (i = 0; i < drvdata->rcnt; i++) {
const struct regulator_desc *desc;
const struct pf0900_regulator_desc *r;
r = &regulator_desc[i];
desc = &r->desc;
config.regmap = pf0900->regmap;
config.driver_data = (void *)r;
config.dev = pf0900->dev;
pf0900->rdevs[i] = devm_regulator_register(pf0900->dev, desc, &config);
if (IS_ERR(pf0900->rdevs[i]))
return dev_err_probe(pf0900->dev, PTR_ERR(pf0900->rdevs[i]),
"Failed to register regulator(%s)\n", desc->name);
}
ret = devm_request_threaded_irq(pf0900->dev, pf0900->irq, NULL,
pf0900_irq_handler,
(IRQF_TRIGGER_FALLING | IRQF_ONESHOT),
"pf0900-irq", pf0900);
if (ret != 0)
return dev_err_probe(pf0900->dev, ret, "Failed to request IRQ: %d\n",
pf0900->irq);
/*
* The PWRUP_M is unmasked by default. When the device enter in RUN state,
* it will assert the PWRUP_I interrupt and assert the INTB pin to inform
* the MCU that it has finished the power up sequence properly.
*/
ret = regmap_write_bits(pf0900->regmap, PF0900_REG_STATUS1_INT, PF0900_IRQ_PWRUP,
PF0900_IRQ_PWRUP);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Clean PWRUP_I error\n");
/* mask interrupt PWRUP */
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_STATUS1_MSK, PF0900_IRQ_PWRUP,
PF0900_IRQ_PWRUP);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_ILIM_MSK, PF0900_IRQ_SW1_IL |
PF0900_IRQ_SW2_IL | PF0900_IRQ_SW3_IL | PF0900_IRQ_SW4_IL |
PF0900_IRQ_SW5_IL, 0);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_UV_MSK, PF0900_IRQ_SW1_UV |
PF0900_IRQ_SW2_UV | PF0900_IRQ_SW3_UV | PF0900_IRQ_SW4_UV |
PF0900_IRQ_SW5_UV, 0);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_OV_MSK, PF0900_IRQ_SW1_OV |
PF0900_IRQ_SW2_OV | PF0900_IRQ_SW3_OV | PF0900_IRQ_SW4_OV |
PF0900_IRQ_SW5_OV, 0);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_ILIM_MSK, PF0900_IRQ_LDO1_IL |
PF0900_IRQ_LDO2_IL | PF0900_IRQ_LDO3_IL, 0);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_UV_MSK, PF0900_IRQ_LDO1_UV |
PF0900_IRQ_LDO2_UV | PF0900_IRQ_LDO3_UV | PF0900_IRQ_VAON_UV, 0);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_OV_MSK, PF0900_IRQ_LDO1_OV |
PF0900_IRQ_LDO2_OV | PF0900_IRQ_LDO3_OV | PF0900_IRQ_VAON_OV, 0);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n");
return 0;
}
static struct pf0900_drvdata pf0900_drvdata = {
.desc = pf0900_regulators,
.rcnt = ARRAY_SIZE(pf0900_regulators),
};
static const struct of_device_id pf0900_of_match[] = {
{ .compatible = "nxp,pf0900", .data = &pf0900_drvdata},
{ }
};
MODULE_DEVICE_TABLE(of, pf0900_of_match);
static struct i2c_driver pf0900_i2c_driver = {
.driver = {
.name = "nxp-pf0900",
.of_match_table = pf0900_of_match,
},
.probe = pf0900_i2c_probe,
};
module_i2c_driver(pf0900_i2c_driver);
MODULE_AUTHOR("Joy Zou <joy.zou@nxp.com>");
MODULE_DESCRIPTION("NXP PF0900 Power Management IC driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,375 @@
// SPDX-License-Identifier: GPL-2.0+
// documentation of this device is available at
// https://www.nxp.com/docs/en/data-sheet/PF5300.pdf
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
/* registers */
#define PF530X_DEVICEID 0x00
#define PF530X_REV 0x01
#define PF530X_EMREV 0x02
#define PF530X_PROGID 0x03
#define PF530X_CONFIG1 0x04
#define PF530X_INT_STATUS1 0x05
#define PF530X_INT_SENSE1 0x06
#define PF530X_INT_STATUS2 0x07
#define PF530X_INT_SENSE2 0x08
#define PF530X_BIST_STAT1 0x09
#define PF530X_BIST_CTRL 0x0a
#define PF530X_STATE 0x0b
#define PF530X_STATE_CTRL 0x0c
#define PF530X_SW1_VOLT 0x0d
#define PF530X_SW1_STBY_VOLT 0x0e
#define PF530X_SW1_CTRL1 0x0f
#define PF530X_SW1_CTRL2 0x10
#define PF530X_CLK_CTRL 0x11
#define PF530X_SEQ_CTRL1 0x12
#define PF530X_SEQ_CTRL2 0x13
#define PF530X_RANDOM_CHK 0x14
#define PF530X_RANDOM_GEN 0x15
#define PF530X_WD_CTRL1 0x16
#define PF530X_WD_SEED 0x17
#define PF530X_WD_ANSWER 0x18
#define PF530X_FLT_CNT1 0x19
#define PF530X_FLT_CNT2 0x1a
#define PF530X_OTP_MODE 0x2f
enum pf530x_states {
PF530X_STATE_POF,
PF530X_STATE_FUSE_LOAD,
PF530X_STATE_LP_OFF,
PF530X_STATE_SELF_TEST,
PF530X_STATE_POWER_UP,
PF530X_STATE_INIT,
PF530X_STATE_IO_RELEASE,
PF530X_STATE_RUN,
PF530X_STATE_STANDBY,
PF530X_STATE_FAULT,
PF530X_STATE_FAILSAFE,
PF530X_STATE_POWER_DOWN,
PF530X_STATE_2MS_SELFTEST_RETRY,
PF530X_STATE_OFF_DLY,
};
#define PF530_FAM 0x50
enum pf530x_devid {
PF5300 = 0x3,
PF5301 = 0x4,
PF5302 = 0x5,
};
#define PF530x_FAM 0x50
#define PF530x_DEVICE_FAM_MASK GENMASK(7, 4)
#define PF530x_DEVICE_ID_MASK GENMASK(3, 0)
#define PF530x_STATE_MASK GENMASK(3, 0)
#define PF530x_STATE_RUN 0x07
#define PF530x_STATE_STANDBY 0x08
#define PF530x_STATE_LP_OFF 0x02
#define PF530X_OTP_STBY_MODE GENMASK(3, 2)
#define PF530X_OTP_RUN_MODE GENMASK(1, 0)
#define PF530X_INT_STATUS_OV BIT(1)
#define PF530X_INT_STATUS_UV BIT(2)
#define PF530X_INT_STATUS_ILIM BIT(3)
#define SW1_ILIM_S BIT(0)
#define VMON_UV_S BIT(1)
#define VMON_OV_S BIT(2)
#define VIN_OVLO_S BIT(3)
#define BG_ERR_S BIT(6)
#define THERM_155_S BIT(3)
#define THERM_140_S BIT(2)
#define THERM_125_S BIT(1)
#define THERM_110_S BIT(0)
struct pf530x_chip {
struct regmap *regmap;
struct device *dev;
};
static const struct regmap_config pf530x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = PF530X_OTP_MODE,
.cache_type = REGCACHE_MAPLE,
};
static int pf530x_get_status(struct regulator_dev *rdev)
{
unsigned int state;
int ret;
ret = regmap_read(rdev->regmap, PF530X_INT_SENSE1, &state);
if (ret != 0)
return ret;
if ((state & (BG_ERR_S | SW1_ILIM_S | VMON_UV_S | VMON_OV_S | VIN_OVLO_S))
!= 0)
return REGULATOR_STATUS_ERROR;
// no errors, check if what non-error state we're in
ret = regmap_read(rdev->regmap, PF530X_STATE, &state);
if (ret != 0)
return ret;
state &= PF530x_STATE_MASK;
switch (state) {
case PF530x_STATE_RUN:
ret = REGULATOR_STATUS_NORMAL;
break;
case PF530x_STATE_STANDBY:
ret = REGULATOR_STATUS_STANDBY;
break;
case PF530x_STATE_LP_OFF:
ret = REGULATOR_STATUS_OFF;
break;
default:
ret = REGULATOR_STATUS_ERROR;
break;
}
return ret;
}
static int pf530x_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
{
unsigned int status;
int ret;
ret = regmap_read(rdev->regmap, PF530X_INT_STATUS1, &status);
if (ret != 0)
return ret;
*flags = 0;
if (status & PF530X_INT_STATUS_OV)
*flags |= REGULATOR_ERROR_OVER_VOLTAGE_WARN;
if (status & PF530X_INT_STATUS_UV)
*flags |= REGULATOR_ERROR_UNDER_VOLTAGE;
if (status & PF530X_INT_STATUS_ILIM)
*flags |= REGULATOR_ERROR_OVER_CURRENT;
ret = regmap_read(rdev->regmap, PF530X_INT_SENSE2, &status);
if (ret != 0)
return ret;
if ((status & (THERM_155_S |
THERM_140_S |
THERM_125_S |
THERM_110_S)) != 0)
*flags |= REGULATOR_ERROR_OVER_TEMP_WARN;
return 0;
}
static const struct regulator_ops pf530x_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.map_voltage = regulator_map_voltage_linear_range,
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.get_status = pf530x_get_status,
.get_error_flags = pf530x_get_error_flags,
.set_bypass = regulator_set_bypass_regmap,
.get_bypass = regulator_get_bypass_regmap,
};
static const struct linear_range vrange = REGULATOR_LINEAR_RANGE(500000, 0, 140, 5000);
static const struct regulator_desc pf530x_reg_desc = {
.name = "SW1",
.ops = &pf530x_regulator_ops,
.linear_ranges = &vrange,
.n_linear_ranges = 1,
.type = REGULATOR_VOLTAGE,
.id = 0,
.owner = THIS_MODULE,
.vsel_reg = PF530X_SW1_VOLT,
.vsel_mask = 0xFF,
.bypass_reg = PF530X_SW1_CTRL2,
.bypass_mask = 0x07,
.bypass_val_on = 0x07,
.bypass_val_off = 0x00,
.enable_reg = PF530X_SW1_CTRL1,
.enable_mask = GENMASK(5, 2),
.enable_val = GENMASK(5, 2),
.disable_val = 0,
};
static int pf530x_identify(struct pf530x_chip *chip)
{
unsigned int value;
u8 dev_fam, dev_id, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev;
const char *name = NULL;
int ret;
ret = regmap_read(chip->regmap, PF530X_DEVICEID, &value);
if (ret) {
dev_err(chip->dev, "failed to read chip family\n");
return ret;
}
dev_fam = value & PF530x_DEVICE_FAM_MASK;
switch (dev_fam) {
case PF530x_FAM:
break;
default:
dev_err(chip->dev,
"Chip 0x%x is not from PF530X family\n", dev_fam);
return ret;
}
dev_id = value & PF530x_DEVICE_ID_MASK;
switch (dev_id) {
case PF5300:
name = "PF5300";
break;
case PF5301:
name = "PF5301";
break;
case PF5302:
name = "PF5302";
break;
default:
dev_err(chip->dev, "Unknown pf530x device id 0x%x\n", dev_id);
return -ENODEV;
}
ret = regmap_read(chip->regmap, PF530X_REV, &value);
if (ret) {
dev_err(chip->dev, "failed to read chip rev\n");
return ret;
}
full_layer_rev = ((value & 0xF0) == 0) ? '0' : ((((value & 0xF0) >> 4) - 1) + 'A');
metal_layer_rev = value & 0xF;
ret = regmap_read(chip->regmap, PF530X_EMREV, &value);
if (ret) {
dev_err(chip->dev, "failed to read chip emrev register\n");
return ret;
}
prog_idh = (value >> 4) + 'A';
// prog_idh skips 'O', per page 96 of the datasheet
if (prog_idh >= 'O')
prog_idh += 1;
emrev = value & 0x7;
ret = regmap_read(chip->regmap, PF530X_PROGID, &value);
if (ret) {
dev_err(chip->dev, "failed to read chip progid register\n");
return ret;
}
if (value >= 0x22) {
dev_err(chip->dev, "invalid value for progid register\n");
return -ENODEV;
} else if (value < 10) {
prog_idl = value + '0';
} else {
prog_idl = (value - 10) + 'A';
// prog_idh skips 'O', per page 97 of the datasheet
if (prog_idl >= 'O')
prog_idl += 1;
}
dev_info(chip->dev, "%s Regulator found (Rev %c%d ProgID %c%c EMREV %x).\n",
name, full_layer_rev, metal_layer_rev, prog_idh, prog_idl, emrev);
return 0;
}
static int pf530x_i2c_probe(struct i2c_client *client)
{
struct regulator_config config = { NULL, };
struct pf530x_chip *chip;
int ret;
struct regulator_dev *rdev;
struct regulator_init_data *init_data;
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
i2c_set_clientdata(client, chip);
chip->dev = &client->dev;
chip->regmap = devm_regmap_init_i2c(client, &pf530x_regmap_config);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
dev_err(&client->dev,
"regmap allocation failed with err %d\n", ret);
return ret;
}
ret = pf530x_identify(chip);
if (ret)
return ret;
init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node, &pf530x_reg_desc);
if (!init_data)
return -ENODATA;
config.dev = chip->dev;
config.of_node = chip->dev->of_node;
config.regmap = chip->regmap;
config.init_data = init_data;
// the config parameter gets copied, it's ok to pass a pointer on the stack here
rdev = devm_regulator_register(&client->dev, &pf530x_reg_desc, &config);
if (IS_ERR(rdev)) {
dev_err(&client->dev, "failed to register %s regulator\n", pf530x_reg_desc.name);
return PTR_ERR(rdev);
}
return 0;
}
static const struct of_device_id pf530x_dt_ids[] = {
{ .compatible = "nxp,pf5300",},
{ }
};
MODULE_DEVICE_TABLE(of, pf530x_dt_ids);
static const struct i2c_device_id pf530x_i2c_id[] = {
{ "pf5300", 0 },
{ "pf5301", 0 },
{ "pf5302", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, pf530x_i2c_id);
static struct i2c_driver pf530x_regulator_driver = {
.id_table = pf530x_i2c_id,
.driver = {
.name = "pf530x",
.of_match_table = pf530x_dt_ids,
},
.probe = pf530x_i2c_probe,
};
module_i2c_driver(pf530x_regulator_driver);
MODULE_AUTHOR("Woodrow Douglass <wdouglass@carnegierobotics.com>");
MODULE_DESCRIPTION("Regulator Driver for NXP's PF5300/PF5301/PF5302 PMIC");
MODULE_LICENSE("GPL");

View File

@@ -94,7 +94,6 @@ static const struct regmap_config qcom_refgen_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
};
static int qcom_refgen_probe(struct platform_device *pdev)

View File

@@ -0,0 +1,642 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Richtek Technology Corp.
// Author: ChiYuan Huang <cy_huang@richtek.com>
// Author: ShihChia Chang <jeff_chang@richtek.com>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#define RT5133_REG_CHIP_INFO 0x00
#define RT5133_REG_RST_CTRL 0x06
#define RT5133_REG_BASE_CTRL 0x09
#define RT5133_REG_GPIO_CTRL 0x0B
#define RT5133_REG_BASE_EVT 0x10
#define RT5133_REG_LDO_PGB_STAT 0x15
#define RT5133_REG_BASE_MASK 0x16
#define RT5133_REG_LDO_SHDN 0x19
#define RT5133_REG_LDO_ON 0x1A
#define RT5133_REG_LDO_OFF 0x1B
#define RT5133_REG_LDO1_CTRL1 0x20
#define RT5133_REG_LDO1_CTRL2 0x21
#define RT5133_REG_LDO1_CTRL3 0x22
#define RT5133_REG_LDO2_CTRL1 0x24
#define RT5133_REG_LDO2_CTRL2 0x25
#define RT5133_REG_LDO2_CTRL3 0x26
#define RT5133_REG_LDO3_CTRL1 0x28
#define RT5133_REG_LDO3_CTRL2 0x29
#define RT5133_REG_LDO3_CTRL3 0x2A
#define RT5133_REG_LDO4_CTRL1 0x2C
#define RT5133_REG_LDO4_CTRL2 0x2D
#define RT5133_REG_LDO4_CTRL3 0x2E
#define RT5133_REG_LDO5_CTRL1 0x30
#define RT5133_REG_LDO5_CTRL2 0x31
#define RT5133_REG_LDO5_CTRL3 0x32
#define RT5133_REG_LDO6_CTRL1 0x34
#define RT5133_REG_LDO6_CTRL2 0x35
#define RT5133_REG_LDO6_CTRL3 0x36
#define RT5133_REG_LDO7_CTRL1 0x38
#define RT5133_REG_LDO7_CTRL2 0x39
#define RT5133_REG_LDO7_CTRL3 0x3A
#define RT5133_REG_LDO8_CTRL1 0x3C
#define RT5133_REG_LDO8_CTRL2 0x3D
#define RT5133_REG_LDO8_CTRL3 0x3E
#define RT5133_REG_LDO8_CTRL4 0x3F
#define RT5133_LDO_REG_BASE(_id) (0x20 + ((_id) - 1) * 4)
#define RT5133_VENDOR_ID_MASK GENMASK(7, 4)
#define RT5133_RESET_CODE 0xB1
#define RT5133_FOFF_BASE_MASK BIT(1)
#define RT5133_OCSHDN_ALL_MASK BIT(7)
#define RT5133_OCSHDN_ALL_SHIFT (7)
#define RT5133_PGBSHDN_ALL_MASK BIT(6)
#define RT5133_PGBSHDN_ALL_SHIFT (6)
#define RT5133_OCPTSEL_MASK BIT(5)
#define RT5133_PGBPTSEL_MASK BIT(4)
#define RT5133_STBTDSEL_MASK GENMASK(1, 0)
#define RT5133_LDO_ENABLE_MASK BIT(7)
#define RT5133_LDO_VSEL_MASK GENMASK(7, 5)
#define RT5133_LDO_AD_MASK BIT(2)
#define RT5133_LDO_SOFT_START_MASK GENMASK(1, 0)
#define RT5133_GPIO_NR 3
#define RT5133_LDO_PGB_EVT_MASK GENMASK(23, 16)
#define RT5133_LDO_PGB_EVT_SHIFT 16
#define RT5133_LDO_OC_EVT_MASK GENMASK(15, 8)
#define RT5133_LDO_OC_EVT_SHIFT 8
#define RT5133_VREF_EVT_MASK BIT(6)
#define RT5133_BASE_EVT_MASK GENMASK(7, 0)
#define RT5133_INTR_CLR_MASK GENMASK(23, 0)
#define RT5133_INTR_BYTE_NR 3
#define RT5133_MAX_I2C_BLOCK_SIZE 1
#define RT5133_CRC8_POLYNOMIAL 0x7
#define RT5133_I2C_ADDR_LEN 1
#define RT5133_PREDATA_LEN 2
#define RT5133_I2C_CRC_LEN 1
#define RT5133_REG_ADDR_LEN 1
#define RT5133_I2C_DUMMY_LEN 1
#define I2C_ADDR_XLATE_8BIT(_addr, _rw) ((((_addr) & 0x7F) << 1) | (_rw))
enum {
RT5133_REGULATOR_BASE = 0,
RT5133_REGULATOR_LDO1,
RT5133_REGULATOR_LDO2,
RT5133_REGULATOR_LDO3,
RT5133_REGULATOR_LDO4,
RT5133_REGULATOR_LDO5,
RT5133_REGULATOR_LDO6,
RT5133_REGULATOR_LDO7,
RT5133_REGULATOR_LDO8,
RT5133_REGULATOR_MAX
};
struct chip_data {
const struct regulator_desc *regulators;
const u8 vendor_id;
};
struct rt5133_priv {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *enable_gpio;
struct regulator_dev *rdev[RT5133_REGULATOR_MAX];
struct gpio_chip gc;
const struct chip_data *cdata;
unsigned int gpio_output_flag;
u8 crc8_tbls[CRC8_TABLE_SIZE];
};
static const unsigned int vout_type1_tables[] = {
1800000, 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000
};
static const unsigned int vout_type2_tables[] = {
1700000, 1800000, 1900000, 2500000, 2700000, 2800000, 2900000, 3000000
};
static const unsigned int vout_type3_tables[] = {
900000, 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1800000
};
static const unsigned int vout_type4_tables[] = {
855000, 900000, 950000, 1000000, 1040000, 1090000, 1140000, 1710000
};
static const struct regulator_ops rt5133_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_active_discharge = regulator_set_active_discharge_regmap,
};
static const struct regulator_ops rt5133_base_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
#define RT5133_REGULATOR_DESC(_name, _node_name, vtables, _supply) \
{\
.name = #_name,\
.id = RT5133_REGULATOR_##_name,\
.of_match = of_match_ptr(#_node_name),\
.regulators_node = of_match_ptr("regulators"),\
.supply_name = _supply,\
.type = REGULATOR_VOLTAGE,\
.owner = THIS_MODULE,\
.ops = &rt5133_regulator_ops,\
.n_voltages = ARRAY_SIZE(vtables),\
.volt_table = vtables,\
.enable_reg = RT5133_REG_##_name##_CTRL1,\
.enable_mask = RT5133_LDO_ENABLE_MASK,\
.vsel_reg = RT5133_REG_##_name##_CTRL2,\
.vsel_mask = RT5133_LDO_VSEL_MASK,\
.active_discharge_reg = RT5133_REG_##_name##_CTRL3,\
.active_discharge_mask = RT5133_LDO_AD_MASK,\
}
static const struct regulator_desc rt5133_regulators[] = {
/* For digital part, base current control */
{
.name = "base",
.id = RT5133_REGULATOR_BASE,
.of_match = of_match_ptr("base"),
.regulators_node = of_match_ptr("regulators"),
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &rt5133_base_regulator_ops,
.enable_reg = RT5133_REG_BASE_CTRL,
.enable_mask = RT5133_FOFF_BASE_MASK,
.enable_is_inverted = true,
},
RT5133_REGULATOR_DESC(LDO1, ldo1, vout_type1_tables, "base"),
RT5133_REGULATOR_DESC(LDO2, ldo2, vout_type1_tables, "base"),
RT5133_REGULATOR_DESC(LDO3, ldo3, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO4, ldo4, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO5, ldo5, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO6, ldo6, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO7, ldo7, vout_type3_tables, "vin"),
RT5133_REGULATOR_DESC(LDO8, ldo8, vout_type3_tables, "vin"),
};
static const struct regulator_desc rt5133a_regulators[] = {
/* For digital part, base current control */
{
.name = "base",
.id = RT5133_REGULATOR_BASE,
.of_match = of_match_ptr("base"),
.regulators_node = of_match_ptr("regulators"),
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &rt5133_base_regulator_ops,
.enable_reg = RT5133_REG_BASE_CTRL,
.enable_mask = RT5133_FOFF_BASE_MASK,
.enable_is_inverted = true,
},
RT5133_REGULATOR_DESC(LDO1, ldo1, vout_type1_tables, "base"),
RT5133_REGULATOR_DESC(LDO2, ldo2, vout_type1_tables, "base"),
RT5133_REGULATOR_DESC(LDO3, ldo3, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO4, ldo4, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO5, ldo5, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO6, ldo6, vout_type2_tables, "base"),
RT5133_REGULATOR_DESC(LDO7, ldo7, vout_type3_tables, "vin"),
RT5133_REGULATOR_DESC(LDO8, ldo8, vout_type4_tables, "vin"),
};
static const struct chip_data regulator_data[] = {
{ rt5133_regulators, 0x70},
{ rt5133a_regulators, 0x80},
};
static int rt5133_gpio_direction_output(struct gpio_chip *gpio,
unsigned int offset, int value)
{
struct rt5133_priv *priv = gpiochip_get_data(gpio);
if (offset >= RT5133_GPIO_NR)
return -EINVAL;
return regmap_update_bits(priv->regmap, RT5133_REG_GPIO_CTRL,
BIT(7 - offset) | BIT(3 - offset),
value ? BIT(7 - offset) | BIT(3 - offset) : 0);
}
static int rt5133_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct rt5133_priv *priv = gpiochip_get_data(chip);
return !!(priv->gpio_output_flag & BIT(offset));
}
static int rt5133_get_gpioen_mask(unsigned int offset, unsigned int *mask)
{
if (offset >= RT5133_GPIO_NR)
return -EINVAL;
*mask = (BIT(7 - offset) | BIT(3 - offset));
return 0;
}
static int rt5133_gpio_set(struct gpio_chip *chip, unsigned int offset, int set_val)
{
struct rt5133_priv *priv = gpiochip_get_data(chip);
unsigned int mask = 0, val = 0, next_flag = priv->gpio_output_flag;
int ret = 0;
ret = rt5133_get_gpioen_mask(offset, &mask);
if (ret) {
dev_err(priv->dev, "%s get gpion en mask failed, offset(%d)\n", __func__, offset);
return ret;
}
val = set_val ? mask : 0;
if (set_val)
next_flag |= BIT(offset);
else
next_flag &= ~BIT(offset);
ret = regmap_update_bits(priv->regmap, RT5133_REG_GPIO_CTRL, mask, val);
if (ret) {
dev_err(priv->dev, "Failed to set gpio [%d] val %d\n", offset,
set_val);
return ret;
}
priv->gpio_output_flag = next_flag;
return 0;
}
static irqreturn_t rt5133_intr_handler(int irq_number, void *data)
{
struct rt5133_priv *priv = data;
u32 intr_evts = 0, handle_evts;
int i, ret;
ret = regmap_bulk_read(priv->regmap, RT5133_REG_BASE_EVT, &intr_evts,
RT5133_INTR_BYTE_NR);
if (ret) {
dev_err(priv->dev, "%s, read event failed\n", __func__);
return IRQ_NONE;
}
handle_evts = intr_evts & RT5133_BASE_EVT_MASK;
/*
* VREF_EVT is a special case, if base off
* this event will also be trigger. Skip it
*/
if (handle_evts & ~RT5133_VREF_EVT_MASK)
dev_dbg(priv->dev, "base event occurred [0x%02x]\n",
handle_evts);
handle_evts = (intr_evts & RT5133_LDO_OC_EVT_MASK) >>
RT5133_LDO_OC_EVT_SHIFT;
for (i = RT5133_REGULATOR_LDO1; i < RT5133_REGULATOR_MAX && handle_evts; i++) {
if (!(handle_evts & BIT(i - 1)))
continue;
regulator_notifier_call_chain(priv->rdev[i],
REGULATOR_EVENT_OVER_CURRENT,
&i);
}
handle_evts = (intr_evts & RT5133_LDO_PGB_EVT_MASK) >>
RT5133_LDO_PGB_EVT_SHIFT;
for (i = RT5133_REGULATOR_LDO1; i < RT5133_REGULATOR_MAX && handle_evts; i++) {
if (!(handle_evts & BIT(i - 1)))
continue;
regulator_notifier_call_chain(priv->rdev[i],
REGULATOR_EVENT_FAIL, &i);
}
ret = regmap_bulk_write(priv->regmap, RT5133_REG_BASE_EVT, &intr_evts,
RT5133_INTR_BYTE_NR);
if (ret)
dev_err(priv->dev, "%s, clear event failed\n", __func__);
return IRQ_HANDLED;
}
static int rt5133_enable_interrupts(int irq_no, struct rt5133_priv *priv)
{
u32 mask = RT5133_INTR_CLR_MASK;
int ret;
/* Force to write clear all events */
ret = regmap_bulk_write(priv->regmap, RT5133_REG_BASE_EVT, &mask,
RT5133_INTR_BYTE_NR);
if (ret) {
dev_err(priv->dev, "Failed to clear all interrupts\n");
return ret;
}
/* Unmask all interrupts */
mask = 0;
ret = regmap_bulk_write(priv->regmap, RT5133_REG_BASE_MASK, &mask,
RT5133_INTR_BYTE_NR);
if (ret) {
dev_err(priv->dev, "Failed to unmask all interrupts\n");
return ret;
}
return devm_request_threaded_irq(priv->dev, irq_no, NULL,
rt5133_intr_handler, IRQF_ONESHOT,
dev_name(priv->dev), priv);
}
static int rt5133_regmap_hw_read(void *context, const void *reg_buf,
size_t reg_size, void *val_buf,
size_t val_size)
{
struct rt5133_priv *priv = context;
struct i2c_client *client = to_i2c_client(priv->dev);
u8 reg = *(u8 *)reg_buf, crc;
u8 *buf;
int buf_len = RT5133_PREDATA_LEN + val_size + RT5133_I2C_CRC_LEN;
int read_len, ret;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = I2C_ADDR_XLATE_8BIT(client->addr, I2C_SMBUS_READ);
buf[1] = reg;
read_len = val_size + RT5133_I2C_CRC_LEN;
ret = i2c_smbus_read_i2c_block_data(client, reg, read_len,
buf + RT5133_PREDATA_LEN);
if (ret < 0)
goto out_read_err;
if (ret != read_len) {
ret = -EIO;
goto out_read_err;
}
crc = crc8(priv->crc8_tbls, buf, RT5133_PREDATA_LEN + val_size, 0);
if (crc != buf[RT5133_PREDATA_LEN + val_size]) {
ret = -EIO;
goto out_read_err;
}
memcpy(val_buf, buf + RT5133_PREDATA_LEN, val_size);
dev_dbg(priv->dev, "%s, reg = 0x%02x, data = 0x%02x\n", __func__, reg, *(u8 *)val_buf);
out_read_err:
kfree(buf);
return (ret < 0) ? ret : 0;
}
static int rt5133_regmap_hw_write(void *context, const void *data, size_t count)
{
struct rt5133_priv *priv = context;
struct i2c_client *client = to_i2c_client(priv->dev);
u8 reg = *(u8 *)data, crc;
u8 *buf;
int buf_len = RT5133_I2C_ADDR_LEN + count + RT5133_I2C_CRC_LEN +
RT5133_I2C_DUMMY_LEN;
int write_len, ret;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = I2C_ADDR_XLATE_8BIT(client->addr, I2C_SMBUS_WRITE);
buf[1] = reg;
memcpy(buf + RT5133_PREDATA_LEN, data + RT5133_REG_ADDR_LEN,
count - RT5133_REG_ADDR_LEN);
crc = crc8(priv->crc8_tbls, buf, RT5133_I2C_ADDR_LEN + count, 0);
buf[RT5133_I2C_ADDR_LEN + count] = crc;
write_len = count - RT5133_REG_ADDR_LEN + RT5133_I2C_CRC_LEN +
RT5133_I2C_DUMMY_LEN;
ret = i2c_smbus_write_i2c_block_data(client, reg, write_len,
buf + RT5133_PREDATA_LEN);
dev_dbg(priv->dev, "%s, reg = 0x%02x, data = 0x%02x\n", __func__, reg,
*(u8 *)(buf + RT5133_PREDATA_LEN));
kfree(buf);
return ret;
}
static const struct regmap_bus rt5133_regmap_bus = {
.read = rt5133_regmap_hw_read,
.write = rt5133_regmap_hw_write,
/* Due to crc, the block read/write length has the limit */
.max_raw_read = RT5133_MAX_I2C_BLOCK_SIZE,
.max_raw_write = RT5133_MAX_I2C_BLOCK_SIZE,
};
static bool rt5133_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case RT5133_REG_CHIP_INFO:
case RT5133_REG_BASE_EVT...RT5133_REG_LDO_PGB_STAT:
case RT5133_REG_LDO_ON...RT5133_REG_LDO_OFF:
case RT5133_REG_LDO1_CTRL1:
case RT5133_REG_LDO2_CTRL1:
case RT5133_REG_LDO3_CTRL1:
case RT5133_REG_LDO4_CTRL1:
case RT5133_REG_LDO5_CTRL1:
case RT5133_REG_LDO6_CTRL1:
case RT5133_REG_LDO7_CTRL1:
case RT5133_REG_LDO8_CTRL1:
return true;
default:
return false;
};
}
static const struct regmap_config rt5133_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RT5133_REG_LDO8_CTRL4,
.cache_type = REGCACHE_FLAT,
.num_reg_defaults_raw = RT5133_REG_LDO8_CTRL4 + 1,
.volatile_reg = rt5133_is_volatile_reg,
};
static int rt5133_chip_reset(struct rt5133_priv *priv)
{
int ret;
ret = regmap_write(priv->regmap, RT5133_REG_RST_CTRL,
RT5133_RESET_CODE);
if (ret)
return ret;
/* Wait for register reset to take effect */
udelay(2);
return 0;
}
static int rt5133_validate_vendor_info(struct rt5133_priv *priv)
{
unsigned int val = 0;
int i, ret;
ret = regmap_read(priv->regmap, RT5133_REG_CHIP_INFO, &val);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
if ((val & RT5133_VENDOR_ID_MASK) ==
regulator_data[i].vendor_id){
priv->cdata = &regulator_data[i];
break;
}
}
if (!priv->cdata) {
dev_err(priv->dev, "Failed to find regulator match version\n");
return -ENODEV;
}
return 0;
}
static int rt5133_parse_dt(struct rt5133_priv *priv)
{
unsigned int val = 0;
int ret = 0;
if (!device_property_read_bool(priv->dev, "richtek,oc-shutdown-all"))
val = 0;
else
val = 1 << RT5133_OCSHDN_ALL_SHIFT;
ret = regmap_update_bits(priv->regmap, RT5133_REG_LDO_SHDN,
RT5133_OCSHDN_ALL_MASK, val);
if (ret)
return ret;
if (!device_property_read_bool(priv->dev, "richtek,pgb-shutdown-all"))
val = 0;
else
val = 1 << RT5133_PGBSHDN_ALL_SHIFT;
return regmap_update_bits(priv->regmap, RT5133_REG_LDO_SHDN,
RT5133_PGBSHDN_ALL_MASK, val);
}
static int rt5133_probe(struct i2c_client *i2c)
{
struct rt5133_priv *priv;
struct regulator_config config = {0};
int i, ret;
priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &i2c->dev;
crc8_populate_msb(priv->crc8_tbls, RT5133_CRC8_POLYNOMIAL);
priv->enable_gpio = devm_gpiod_get_optional(&i2c->dev, "enable",
GPIOD_OUT_HIGH);
if (IS_ERR(priv->enable_gpio))
dev_err(&i2c->dev, "Failed to request HWEN gpio, check if default en=high\n");
priv->regmap = devm_regmap_init(&i2c->dev, &rt5133_regmap_bus, priv,
&rt5133_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(&i2c->dev, "Failed to register regmap\n");
return PTR_ERR(priv->regmap);
}
ret = rt5133_validate_vendor_info(priv);
if (ret) {
dev_err(&i2c->dev, "Failed to check vendor info [%d]\n", ret);
return ret;
}
ret = rt5133_chip_reset(priv);
if (ret) {
dev_err(&i2c->dev, "Failed to execute sw reset\n");
return ret;
}
config.dev = &i2c->dev;
config.driver_data = priv;
config.regmap = priv->regmap;
for (i = 0; i < RT5133_REGULATOR_MAX; i++) {
priv->rdev[i] = devm_regulator_register(&i2c->dev,
priv->cdata->regulators + i,
&config);
if (IS_ERR(priv->rdev[i])) {
dev_err(&i2c->dev,
"Failed to register [%d] regulator\n", i);
return PTR_ERR(priv->rdev[i]);
}
}
ret = rt5133_parse_dt(priv);
if (ret) {
dev_err(&i2c->dev, "%s, Failed to parse dt\n", __func__);
return ret;
}
priv->gc.label = dev_name(&i2c->dev);
priv->gc.parent = &i2c->dev;
priv->gc.base = -1;
priv->gc.ngpio = RT5133_GPIO_NR;
priv->gc.set = rt5133_gpio_set;
priv->gc.get = rt5133_gpio_get;
priv->gc.direction_output = rt5133_gpio_direction_output;
priv->gc.can_sleep = true;
ret = devm_gpiochip_add_data(&i2c->dev, &priv->gc, priv);
if (ret)
return ret;
ret = rt5133_enable_interrupts(i2c->irq, priv);
if (ret) {
dev_err(&i2c->dev, "enable interrupt failed\n");
return ret;
}
i2c_set_clientdata(i2c, priv);
return ret;
}
static const struct of_device_id __maybe_unused rt5133_of_match_table[] = {
{ .compatible = "richtek,rt5133", },
{ }
};
MODULE_DEVICE_TABLE(of, rt5133_of_match_table);
static struct i2c_driver rt5133_driver = {
.driver = {
.name = "rt5133",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = rt5133_of_match_table,
},
.probe = rt5133_probe,
};
module_i2c_driver(rt5133_driver);
MODULE_DESCRIPTION("RT5133 Regulator Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0+
//
// s2dos05.c - Regulator driver for the Samsung s2dos05
//
// Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/samsung/core.h>
#include <linux/regulator/s2dos05.h>
#include <linux/i2c.h>
struct s2dos05_data {
struct regmap *regmap;
struct device *dev;
};
#define _BUCK(macro) S2DOS05_BUCK##macro
#define _buck_ops(num) s2dos05_ops##num
#define _LDO(macro) S2DOS05_LDO##macro
#define _REG(ctrl) S2DOS05_REG##ctrl
#define _ldo_ops(num) s2dos05_ops##num
#define _MASK(macro) S2DOS05_ENABLE_MASK##macro
#define _TIME(macro) S2DOS05_ENABLE_TIME##macro
#define BUCK_DESC(_name, _id, _ops, m, s, v, e, em, t, a) { \
.name = _name, \
.id = _id, \
.ops = _ops, \
.of_match = of_match_ptr(_name), \
.of_match_full_name = true, \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.min_uV = m, \
.uV_step = s, \
.n_voltages = S2DOS05_BUCK_N_VOLTAGES, \
.vsel_reg = v, \
.vsel_mask = S2DOS05_BUCK_VSEL_MASK, \
.enable_reg = e, \
.enable_mask = em, \
.enable_time = t, \
.active_discharge_off = 0, \
.active_discharge_on = S2DOS05_BUCK_FD_MASK, \
.active_discharge_reg = a, \
.active_discharge_mask = S2DOS05_BUCK_FD_MASK \
}
#define LDO_DESC(_name, _id, _ops, m, s, v, e, em, t, a) { \
.name = _name, \
.id = _id, \
.ops = _ops, \
.of_match = of_match_ptr(_name), \
.of_match_full_name = true, \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.min_uV = m, \
.uV_step = s, \
.n_voltages = S2DOS05_LDO_N_VOLTAGES, \
.vsel_reg = v, \
.vsel_mask = S2DOS05_LDO_VSEL_MASK, \
.enable_reg = e, \
.enable_mask = em, \
.enable_time = t, \
.active_discharge_off = 0, \
.active_discharge_on = S2DOS05_LDO_FD_MASK, \
.active_discharge_reg = a, \
.active_discharge_mask = S2DOS05_LDO_FD_MASK \
}
static const struct regulator_ops s2dos05_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.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_active_discharge = regulator_set_active_discharge_regmap,
};
static const struct regulator_desc regulators[S2DOS05_REGULATOR_MAX] = {
// name, id, ops, min_uv, uV_step, vsel_reg, enable_reg
LDO_DESC("ldo1", _LDO(1), &_ldo_ops(), _LDO(_MIN1),
_LDO(_STEP1), _REG(_LDO1_CFG),
_REG(_EN), _MASK(_L1), _TIME(_LDO), _REG(_LDO1_CFG)),
LDO_DESC("ldo2", _LDO(2), &_ldo_ops(), _LDO(_MIN1),
_LDO(_STEP1), _REG(_LDO2_CFG),
_REG(_EN), _MASK(_L2), _TIME(_LDO), _REG(_LDO2_CFG)),
LDO_DESC("ldo3", _LDO(3), &_ldo_ops(), _LDO(_MIN2),
_LDO(_STEP1), _REG(_LDO3_CFG),
_REG(_EN), _MASK(_L3), _TIME(_LDO), _REG(_LDO3_CFG)),
LDO_DESC("ldo4", _LDO(4), &_ldo_ops(), _LDO(_MIN2),
_LDO(_STEP1), _REG(_LDO4_CFG),
_REG(_EN), _MASK(_L4), _TIME(_LDO), _REG(_LDO4_CFG)),
BUCK_DESC("buck", _BUCK(1), &_buck_ops(), _BUCK(_MIN1),
_BUCK(_STEP1), _REG(_BUCK_VOUT),
_REG(_EN), _MASK(_B1), _TIME(_BUCK), _REG(_BUCK_CFG)),
};
static int s2dos05_pmic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct s2dos05_data *s2dos05;
struct regulator_config config = { };
unsigned int rdev_num = ARRAY_SIZE(regulators);
s2dos05 = devm_kzalloc(dev, sizeof(*s2dos05), GFP_KERNEL);
if (!s2dos05)
return -ENOMEM;
platform_set_drvdata(pdev, s2dos05);
s2dos05->regmap = iodev->regmap_pmic;
s2dos05->dev = dev;
if (!dev->of_node)
dev->of_node = dev->parent->of_node;
config.dev = dev;
config.driver_data = s2dos05;
for (int i = 0; i < rdev_num; i++) {
struct regulator_dev *regulator;
regulator = devm_regulator_register(&pdev->dev,
&regulators[i], &config);
if (IS_ERR(regulator)) {
return dev_err_probe(&pdev->dev, PTR_ERR(regulator),
"regulator init failed for %d\n", i);
}
}
return 0;
}
static const struct platform_device_id s2dos05_pmic_id[] = {
{ "s2dos05-regulator" },
{ },
};
MODULE_DEVICE_TABLE(platform, s2dos05_pmic_id);
static struct platform_driver s2dos05_platform_driver = {
.driver = {
.name = "s2dos05",
},
.probe = s2dos05_pmic_probe,
.id_table = s2dos05_pmic_id,
};
module_platform_driver(s2dos05_platform_driver);
MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
MODULE_DESCRIPTION("Samsung S2DOS05 Regulator Driver");
MODULE_LICENSE("GPL");

View File

@@ -257,7 +257,8 @@ static int process_scmi_regulator_of_node(struct scmi_device *sdev,
struct device_node *np,
struct scmi_regulator_info *rinfo)
{
u32 dom, ret;
u32 dom;
int ret;
ret = of_property_read_u32(np, "reg", &dom);
if (ret)

View File

@@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for regulators found in the SpacemiT P1 PMIC
*
* Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved.
* Derived from code from SpacemiT.
* Copyright (c) 2023, SPACEMIT Co., Ltd
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/linear_range.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#define MOD_NAME "spacemit-p1-regulator"
enum p1_regulator_id {
P1_BUCK1,
P1_BUCK2,
P1_BUCK3,
P1_BUCK4,
P1_BUCK5,
P1_BUCK6,
P1_ALDO1,
P1_ALDO2,
P1_ALDO3,
P1_ALDO4,
P1_DLDO1,
P1_DLDO2,
P1_DLDO3,
P1_DLDO4,
P1_DLDO5,
P1_DLDO6,
P1_DLDO7,
};
static const struct regulator_ops p1_regulator_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.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,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
/* Selector value 255 can be used to disable the buck converter on sleep */
static const struct linear_range p1_buck_ranges[] = {
REGULATOR_LINEAR_RANGE(500000, 0, 170, 5000),
REGULATOR_LINEAR_RANGE(1375000, 171, 254, 25000),
};
/* Selector value 0 can be used for suspend */
static const struct linear_range p1_ldo_ranges[] = {
REGULATOR_LINEAR_RANGE(500000, 11, 127, 25000),
};
/* These define the voltage selector field for buck and LDO regulators */
#define BUCK_MASK GENMASK(7, 0)
#define LDO_MASK GENMASK(6, 0)
#define P1_ID(_TYPE, _n) P1_ ## _TYPE ## _n
#define P1_ENABLE_REG(_off, _n) ((_off) + 3 * ((_n) - 1))
#define P1_REG_DESC(_TYPE, _type, _n, _s, _off, _mask, _nv, _ranges) \
{ \
.name = #_type #_n, \
.supply_name = _s, \
.of_match = of_match_ptr(#_type #_n), \
.regulators_node = of_match_ptr("regulators"), \
.id = P1_ID(_TYPE, _n), \
.n_voltages = _nv, \
.ops = &p1_regulator_ops, \
.owner = THIS_MODULE, \
.linear_ranges = _ranges, \
.n_linear_ranges = ARRAY_SIZE(_ranges), \
.vsel_reg = P1_ENABLE_REG(_off, _n) + 1, \
.vsel_mask = _mask, \
.enable_reg = P1_ENABLE_REG(_off, _n), \
.enable_mask = BIT(0), \
}
#define P1_BUCK_DESC(_n) \
P1_REG_DESC(BUCK, buck, _n, "vcc", 0x47, BUCK_MASK, 254, p1_buck_ranges)
#define P1_ALDO_DESC(_n) \
P1_REG_DESC(ALDO, aldo, _n, "vcc", 0x5b, LDO_MASK, 117, p1_ldo_ranges)
#define P1_DLDO_DESC(_n) \
P1_REG_DESC(DLDO, dldo, _n, "buck5", 0x67, LDO_MASK, 117, p1_ldo_ranges)
static const struct regulator_desc p1_regulator_desc[] = {
P1_BUCK_DESC(1),
P1_BUCK_DESC(2),
P1_BUCK_DESC(3),
P1_BUCK_DESC(4),
P1_BUCK_DESC(5),
P1_BUCK_DESC(6),
P1_ALDO_DESC(1),
P1_ALDO_DESC(2),
P1_ALDO_DESC(3),
P1_ALDO_DESC(4),
P1_DLDO_DESC(1),
P1_DLDO_DESC(2),
P1_DLDO_DESC(3),
P1_DLDO_DESC(4),
P1_DLDO_DESC(5),
P1_DLDO_DESC(6),
P1_DLDO_DESC(7),
};
static int p1_regulator_probe(struct platform_device *pdev)
{
struct regulator_config config = { };
struct device *dev = &pdev->dev;
u32 i;
/*
* The parent device (PMIC) owns the regmap. Since we don't
* provide one in the config structure, that one will be used.
*/
config.dev = dev->parent;
for (i = 0; i < ARRAY_SIZE(p1_regulator_desc); i++) {
const struct regulator_desc *desc = &p1_regulator_desc[i];
struct regulator_dev *rdev;
rdev = devm_regulator_register(dev, desc, &config);
if (IS_ERR(rdev))
return dev_err_probe(dev, PTR_ERR(rdev),
"error registering regulator %s\n",
desc->name);
}
return 0;
}
static struct platform_driver p1_regulator_driver = {
.probe = p1_regulator_probe,
.driver = {
.name = MOD_NAME,
},
};
module_platform_driver(p1_regulator_driver);
MODULE_DESCRIPTION("SpacemiT P1 regulator driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MOD_NAME);

View File

@@ -598,7 +598,6 @@ static int pmic_probe(struct spi_device *spi)
spi_set_drvdata(spi, hw);
memset(hw, 0, sizeof(struct tps6524x));
hw->dev = dev;
hw->spi = spi;
mutex_init(&hw->lock);

View File

@@ -647,7 +647,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev)
default:
dev_err(tps->dev, "unknown chip_id %lu\n", tps->chip_id);
return -EINVAL;
};
}
enum {
MULTI_BUCK12,

View File

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0+ */
// s2dos05.h
//
// Copyright (c) 2016 Samsung Electronics Co., Ltd
// http://www.samsung.com
// Copyright (C) 2024 Dzmitry Sankouski <dsankouski@gmail.com>
#ifndef __LINUX_S2DOS05_H
#define __LINUX_S2DOS05_H
// S2DOS05 registers
// Slave Addr : 0xC0
enum S2DOS05_reg {
S2DOS05_REG_DEV_ID,
S2DOS05_REG_TOPSYS_STAT,
S2DOS05_REG_STAT,
S2DOS05_REG_EN,
S2DOS05_REG_LDO1_CFG,
S2DOS05_REG_LDO2_CFG,
S2DOS05_REG_LDO3_CFG,
S2DOS05_REG_LDO4_CFG,
S2DOS05_REG_BUCK_CFG,
S2DOS05_REG_BUCK_VOUT,
S2DOS05_REG_IRQ_MASK = 0x0D,
S2DOS05_REG_SSD_TSD = 0x0E,
S2DOS05_REG_OCL = 0x10,
S2DOS05_REG_IRQ = 0x11
};
// S2DOS05 regulator ids
enum S2DOS05_regulators {
S2DOS05_LDO1,
S2DOS05_LDO2,
S2DOS05_LDO3,
S2DOS05_LDO4,
S2DOS05_BUCK1,
S2DOS05_REG_MAX,
};
#define S2DOS05_IRQ_PWRMT_MASK BIT(5)
#define S2DOS05_IRQ_TSD_MASK BIT(4)
#define S2DOS05_IRQ_SSD_MASK BIT(3)
#define S2DOS05_IRQ_SCP_MASK BIT(2)
#define S2DOS05_IRQ_UVLO_MASK BIT(1)
#define S2DOS05_IRQ_OCD_MASK BIT(0)
#define S2DOS05_BUCK_MIN1 506250
#define S2DOS05_LDO_MIN1 1500000
#define S2DOS05_LDO_MIN2 2700000
#define S2DOS05_BUCK_STEP1 6250
#define S2DOS05_LDO_STEP1 25000
#define S2DOS05_LDO_VSEL_MASK 0x7F
#define S2DOS05_LDO_FD_MASK 0x80
#define S2DOS05_BUCK_VSEL_MASK 0xFF
#define S2DOS05_BUCK_FD_MASK 0x08
#define S2DOS05_ENABLE_MASK_L1 BIT(0)
#define S2DOS05_ENABLE_MASK_L2 BIT(1)
#define S2DOS05_ENABLE_MASK_L3 BIT(2)
#define S2DOS05_ENABLE_MASK_L4 BIT(3)
#define S2DOS05_ENABLE_MASK_B1 BIT(4)
#define S2DOS05_RAMP_DELAY 12000
#define S2DOS05_ENABLE_TIME_LDO 50
#define S2DOS05_ENABLE_TIME_BUCK 350
#define S2DOS05_LDO_N_VOLTAGES (S2DOS05_LDO_VSEL_MASK + 1)
#define S2DOS05_BUCK_N_VOLTAGES (S2DOS05_BUCK_VSEL_MASK + 1)
#define S2DOS05_REGULATOR_MAX (S2DOS05_REG_MAX)
#endif // __LINUX_S2DOS05_H

View File

@@ -40,4 +40,14 @@ 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)
{
return devm_regulator_get_enable(dev, id);
}
int rust_helper_devm_regulator_get_enable_optional(struct device *dev, const char *id)
{
return devm_regulator_get_enable_optional(dev, id);
}
#endif

View File

@@ -18,7 +18,7 @@
use crate::{
bindings,
device::Device,
device::{Bound, Device},
error::{from_err_ptr, to_result, Result},
prelude::*,
};
@@ -30,7 +30,6 @@ mod private {
impl Sealed for super::Enabled {}
impl Sealed for super::Disabled {}
impl Sealed for super::Dynamic {}
}
/// A trait representing the different states a [`Regulator`] can be in.
@@ -50,13 +49,6 @@ pub struct Enabled;
/// own an `enable` reference count, but the regulator may still be on.
pub struct Disabled;
/// A state that models the C API. The [`Regulator`] can be either enabled or
/// disabled, and the user is in control of the reference count. This is also
/// the default state.
///
/// Use [`Regulator::is_enabled`] to check the regulator's current state.
pub struct Dynamic;
impl RegulatorState for Enabled {
const DISABLE_ON_DROP: bool = true;
}
@@ -65,14 +57,9 @@ impl RegulatorState for Disabled {
const DISABLE_ON_DROP: bool = false;
}
impl RegulatorState for Dynamic {
const DISABLE_ON_DROP: bool = false;
}
/// A trait that abstracts the ability to check if a [`Regulator`] is enabled.
pub trait IsEnabled: RegulatorState {}
impl IsEnabled for Disabled {}
impl IsEnabled for Dynamic {}
/// An error that can occur when trying to convert a [`Regulator`] between states.
pub struct Error<State: RegulatorState> {
@@ -82,6 +69,41 @@ pub struct Error<State: RegulatorState> {
/// The regulator that caused the error, so that the operation may be retried.
pub regulator: Regulator<State>,
}
/// Obtains and enables a [`devres`]-managed regulator for a device.
///
/// This calls [`regulator_disable()`] and [`regulator_put()`] automatically on
/// driver detach.
///
/// This API is identical to `devm_regulator_get_enable()`, and should be
/// preferred over the [`Regulator<T: RegulatorState>`] API if the caller only
/// cares about the regulator being enabled.
///
/// [`devres`]: https://docs.kernel.org/driver-api/driver-model/devres.html
/// [`regulator_disable()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_disable
/// [`regulator_put()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_put
pub fn devm_enable(dev: &Device<Bound>, name: &CStr) -> Result {
// SAFETY: `dev` is a valid and bound device, while `name` is a valid C
// string.
to_result(unsafe { bindings::devm_regulator_get_enable(dev.as_raw(), name.as_ptr()) })
}
/// Same as [`devm_enable`], but calls `devm_regulator_get_enable_optional`
/// instead.
///
/// This obtains and enables a [`devres`]-managed regulator for a device, but
/// does not print a message nor provides a dummy if the regulator is not found.
///
/// This calls [`regulator_disable()`] and [`regulator_put()`] automatically on
/// driver detach.
///
/// [`devres`]: https://docs.kernel.org/driver-api/driver-model/devres.html
/// [`regulator_disable()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_disable
/// [`regulator_put()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_put
pub fn devm_enable_optional(dev: &Device<Bound>, name: &CStr) -> Result {
// SAFETY: `dev` is a valid and bound device, while `name` is a valid C
// string.
to_result(unsafe { bindings::devm_regulator_get_enable_optional(dev.as_raw(), name.as_ptr()) })
}
/// A `struct regulator` abstraction.
///
@@ -159,6 +181,29 @@ pub struct Error<State: RegulatorState> {
/// }
/// ```
///
/// If a driver only cares about the regulator being on for as long it is bound
/// to a device, then it should use [`devm_enable`] or [`devm_enable_optional`].
/// This should be the default use-case unless more fine-grained control over
/// the regulator's state is required.
///
/// [`devm_enable`]: crate::regulator::devm_enable
/// [`devm_optional`]: crate::regulator::devm_enable_optional
///
/// ```
/// # use kernel::prelude::*;
/// # use kernel::c_str;
/// # use kernel::device::{Bound, Device};
/// # use kernel::regulator;
/// fn enable(dev: &Device<Bound>) -> Result {
/// // Obtain a reference to a (fictitious) regulator and enable it. This
/// // call only returns whether the operation succeeded.
/// regulator::devm_enable(dev, c_str!("vcc"))?;
///
/// // The regulator will be disabled and put when `dev` is unbound.
/// Ok(())
/// }
/// ```
///
/// ## Disabling a regulator
///
/// ```
@@ -183,64 +228,13 @@ pub struct Error<State: RegulatorState> {
/// }
/// ```
///
/// ## Using [`Regulator<Dynamic>`]
///
/// This example mimics the behavior of the C API, where the user is in
/// control of the enabled reference count. This is useful for drivers that
/// might call enable and disable to manage the `enable` reference count at
/// runtime, perhaps as a result of `open()` and `close()` calls or whatever
/// other driver-specific or subsystem-specific hooks.
///
/// ```
/// # use kernel::prelude::*;
/// # use kernel::c_str;
/// # use kernel::device::Device;
/// # use kernel::regulator::{Regulator, Dynamic};
/// struct PrivateData {
/// regulator: Regulator<Dynamic>,
/// }
///
/// // A fictictious probe function that obtains a regulator and sets it up.
/// fn probe(dev: &Device) -> Result<PrivateData> {
/// // Obtain a reference to a (fictitious) regulator.
/// let mut regulator = Regulator::<Dynamic>::get(dev, c_str!("vcc"))?;
///
/// Ok(PrivateData { regulator })
/// }
///
/// // A fictictious function that indicates that the device is going to be used.
/// fn open(dev: &Device, data: &mut PrivateData) -> Result {
/// // Increase the `enabled` reference count.
/// data.regulator.enable()?;
///
/// Ok(())
/// }
///
/// fn close(dev: &Device, data: &mut PrivateData) -> Result {
/// // Decrease the `enabled` reference count.
/// data.regulator.disable()?;
///
/// Ok(())
/// }
///
/// fn remove(dev: &Device, data: PrivateData) -> Result {
/// // `PrivateData` is dropped here, which will drop the
/// // `Regulator<Dynamic>` in turn.
/// //
/// // The reference that was obtained by `regulator_get()` will be
/// // released, but it is up to the user to make sure that the number of calls
/// // to `enable()` and `disabled()` are balanced before this point.
/// Ok(())
/// }
/// ```
///
/// # Invariants
///
/// - `inner` is a non-null wrapper over a pointer to a `struct
/// regulator` obtained from [`regulator_get()`].
///
/// [`regulator_get()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_get
pub struct Regulator<State = Dynamic>
pub struct Regulator<State>
where
State: RegulatorState,
{
@@ -267,11 +261,8 @@ impl<T: RegulatorState> Regulator<T> {
pub fn get_voltage(&self) -> Result<Voltage> {
// SAFETY: Safe as per the type invariants of `Regulator`.
let voltage = unsafe { bindings::regulator_get_voltage(self.inner.as_ptr()) };
if voltage < 0 {
Err(kernel::error::Error::from_errno(voltage))
} else {
Ok(Voltage::from_microvolts(voltage))
}
to_result(voltage).map(|()| Voltage::from_microvolts(voltage))
}
fn get_internal(dev: &Device, name: &CStr) -> Result<Regulator<T>> {
@@ -289,12 +280,12 @@ impl<T: RegulatorState> Regulator<T> {
})
}
fn enable_internal(&mut self) -> Result {
fn enable_internal(&self) -> Result {
// SAFETY: Safe as per the type invariants of `Regulator`.
to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) })
}
fn disable_internal(&mut self) -> Result {
fn disable_internal(&self) -> Result {
// SAFETY: Safe as per the type invariants of `Regulator`.
to_result(unsafe { bindings::regulator_disable(self.inner.as_ptr()) })
}
@@ -310,7 +301,7 @@ impl Regulator<Disabled> {
pub fn try_into_enabled(self) -> Result<Regulator<Enabled>, Error<Disabled>> {
// We will be transferring the ownership of our `regulator_get()` count to
// `Regulator<Enabled>`.
let mut regulator = ManuallyDrop::new(self);
let regulator = ManuallyDrop::new(self);
regulator
.enable_internal()
@@ -339,7 +330,7 @@ impl Regulator<Enabled> {
pub fn try_into_disabled(self) -> Result<Regulator<Disabled>, Error<Enabled>> {
// We will be transferring the ownership of our `regulator_get()` count
// to `Regulator<Disabled>`.
let mut regulator = ManuallyDrop::new(self);
let regulator = ManuallyDrop::new(self);
regulator
.disable_internal()
@@ -354,28 +345,6 @@ impl Regulator<Enabled> {
}
}
impl Regulator<Dynamic> {
/// Obtains a [`Regulator`] instance from the system. The current state of
/// the regulator is unknown and it is up to the user to manage the enabled
/// reference count.
///
/// This closely mimics the behavior of the C API and can be used to
/// dynamically manage the enabled reference count at runtime.
pub fn get(dev: &Device, name: &CStr) -> Result<Self> {
Regulator::get_internal(dev, name)
}
/// Increases the `enabled` reference count.
pub fn enable(&mut self) -> Result {
self.enable_internal()
}
/// Decreases the `enabled` reference count.
pub fn disable(&mut self) -> Result {
self.disable_internal()
}
}
impl<T: IsEnabled> Regulator<T> {
/// Checks if the regulator is enabled.
pub fn is_enabled(&self) -> bool {
@@ -398,6 +367,14 @@ impl<T: RegulatorState> Drop for Regulator<T> {
}
}
// SAFETY: It is safe to send a `Regulator<T>` across threads. In particular, a
// Regulator<T> can be dropped from any thread.
unsafe impl<T: RegulatorState> Send for Regulator<T> {}
// SAFETY: It is safe to send a &Regulator<T> across threads because the C side
// handles its own locking.
unsafe impl<T: RegulatorState> Sync for Regulator<T> {}
/// A voltage.
///
/// This type represents a voltage value in microvolts.