mirror of
https://github.com/torvalds/linux.git
synced 2025-11-30 23:16:01 +07:00
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:
@@ -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>;
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
163
Documentation/devicetree/bindings/regulator/nxp,pf0900.yaml
Normal file
163
Documentation/devicetree/bindings/regulator/nxp,pf0900.yaml
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
54
Documentation/devicetree/bindings/regulator/nxp,pf5300.yaml
Normal file
54
Documentation/devicetree/bindings/regulator/nxp,pf5300.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -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
|
||||
|
||||
178
Documentation/devicetree/bindings/regulator/richtek,rt5133.yaml
Normal file
178
Documentation/devicetree/bindings/regulator/richtek,rt5133.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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)
|
||||
=======================================================
|
||||
|
||||
|
||||
15
MAINTAINERS
15
MAINTAINERS
@@ -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/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
221
drivers/regulator/max77838-regulator.c
Normal file
221
drivers/regulator/max77838-regulator.c
Normal 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");
|
||||
975
drivers/regulator/pf0900-regulator.c
Normal file
975
drivers/regulator/pf0900-regulator.c
Normal 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 = ®ulator_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");
|
||||
375
drivers/regulator/pf530x-regulator.c
Normal file
375
drivers/regulator/pf530x-regulator.c
Normal 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");
|
||||
@@ -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)
|
||||
|
||||
642
drivers/regulator/rt5133-regulator.c
Normal file
642
drivers/regulator/rt5133-regulator.c
Normal 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 = ®ulator_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");
|
||||
165
drivers/regulator/s2dos05-regulator.c
Normal file
165
drivers/regulator/s2dos05-regulator.c
Normal 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,
|
||||
®ulators[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");
|
||||
@@ -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)
|
||||
|
||||
157
drivers/regulator/spacemit-p1.c
Normal file
157
drivers/regulator/spacemit-p1.c
Normal 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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
73
include/linux/regulator/s2dos05.h
Normal file
73
include/linux/regulator/s2dos05.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user