Commit d6ccf451 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull regulator updates from Mark Brown:
 "Quite a quiet release for the regulator API, mainly a few new drivers
  plus a lot of fixes for the Raspberry Pi panel driver.

  There's also a SPI commit in here which I managed to apply to the
  wrong tree and then didn't notice until there were too many commits on
  top of it, sorry about that.

   - Make it easier to use the virtual consumer test driver with DT
     systems.

   - Substantial overhaul providing various fixes and robustness
     improvements for the Raspberry Pi panel driver.

   - Support for Qualcomm PMX65 and SDX65, Richtek RT5190A, and Texas
     Instruments TPS62864x"

* tag 'regulator-v5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (26 commits)
  regulator: qcom-rpmh: Add support for SDX65
  regulator: dt-bindings: Add PMX65 compatibles
  regulator: vctrl: Use min() instead of doing it manually
  regulator: rt5190a: Add support for Richtek RT5190A PMIC
  regulator: Add bindings for Richtek RT5190A PMIC
  regulator: Convert TPS62360 binding to json-schema
  regulator: cleanup comments
  regulator: virtual: add devicetree support
  regulator: virtual: warn against production use
  regulator: virtual: use dev_err_probe()
  regulator: tps62864: Fix bindings for SW property
  regulator: Add support for TPS6286x
  regulator: Add bindings for TPS62864x
  regulator/rpi-panel-attiny: Use two transactions for I2C read
  regulator/rpi-panel-attiny: Use the regmap cache
  regulator: rpi-panel: Remove get_brightness hook
  regulator: rpi-panel: Add GPIO control for panel and touch resets
  regulator: rpi-panel: Convert to drive lines directly
  regulator: rpi-panel: Ensure the backlight is off during probe.
  regulator: rpi-panel: Serialise operations.
  ...
parents a50a8c38 5999f85d
...@@ -113,7 +113,7 @@ examples: ...@@ -113,7 +113,7 @@ examples:
}; };
- | - |
#include <dt-bindings/gpio/tegra-gpio.h> #include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>
i2c { i2c {
...@@ -123,8 +123,7 @@ examples: ...@@ -123,8 +123,7 @@ examples:
regulator@1b { regulator@1b {
compatible = "maxim,max77621"; compatible = "maxim,max77621";
reg = <0x1b>; reg = <0x1b>;
interrupt-parent = <&gpio>; interrupts = <1 IRQ_TYPE_LEVEL_LOW>;
interrupts = <TEGRA_GPIO(Y, 1) IRQ_TYPE_LEVEL_LOW>;
regulator-always-on; regulator-always-on;
regulator-boot-on; regulator-boot-on;
......
...@@ -70,7 +70,11 @@ properties: ...@@ -70,7 +70,11 @@ properties:
$ref: "regulator.yaml#" $ref: "regulator.yaml#"
type: object type: object
"^(vsnvs|vref|vrefddr|swbst|coin)$": "^vldo[1-4]$":
$ref: "regulator.yaml#"
type: object
"^(vsnvs|vref|vrefddr|swbst|coin|v33|vccsd)$":
$ref: "regulator.yaml#" $ref: "regulator.yaml#"
type: object type: object
......
...@@ -48,6 +48,7 @@ description: | ...@@ -48,6 +48,7 @@ description: |
For PMI8998, bob For PMI8998, bob
For PMR735A, smps1 - smps3, ldo1 - ldo7 For PMR735A, smps1 - smps3, ldo1 - ldo7
For PMX55, smps1 - smps7, ldo1 - ldo16 For PMX55, smps1 - smps7, ldo1 - ldo16
For PMX65, smps1 - smps8, ldo1 - ldo21
properties: properties:
compatible: compatible:
...@@ -70,6 +71,7 @@ properties: ...@@ -70,6 +71,7 @@ properties:
- qcom,pmm8155au-rpmh-regulators - qcom,pmm8155au-rpmh-regulators
- qcom,pmr735a-rpmh-regulators - qcom,pmr735a-rpmh-regulators
- qcom,pmx55-rpmh-regulators - qcom,pmx55-rpmh-regulators
- qcom,pmx65-rpmh-regulators
qcom,pmic-id: qcom,pmic-id:
description: | description: |
......
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/richtek,rt5190a-regulator.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Richtek RT5190A PMIC Regulator
maintainers:
- ChiYuan Huang <cy_huang@richtek.com>
description: |
The RT5190A integrates 1 channel buck controller, 3 channels high efficiency
synchronous buck converters, 1 LDO, I2C control interface and peripherial
logical control.
It also supports mute AC OFF depop sound and quick setting storage while
input power is removed.
properties:
compatible:
enum:
- richtek,rt5190a
reg:
maxItems: 1
interrupts:
maxItems: 1
vin2-supply:
description: phandle to buck2 input voltage.
vin3-supply:
description: phandle to buck3 input voltage.
vin4-supply:
description: phandle to buck4 input voltage.
vinldo-supply:
description: phandle to ldo input voltage
richtek,mute-enable:
description: |
The mute function uses 'mutein', 'muteout', and 'vdet' pins as the control
signal. When enabled, The normal behavior is to bypass the 'mutein' signal
'muteout'. But if the power source removal is detected from 'vdet',
whatever the 'mutein' signal is, it will pull down the 'muteout' to force
speakers mute. this function is commonly used to prevent the speaker pop
noise during AC power turned off in the modern TV system design.
type: boolean
regulators:
type: object
patternProperties:
"^buck[1-4]$|^ldo$":
type: object
$ref: regulator.yaml#
description: |
regulator description for buck1 and buck4.
properties:
regulator-allowed-modes:
description: |
buck operating mode, only buck1/4 support mode operating.
0: auto mode
1: force pwm mode
items:
enum: [0, 1]
richtek,latchup-enable:
type: boolean
description: |
If specified, undervolt protection mode changes from the default
hiccup to latchup.
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/regulator/richtek,rt5190a-regulator.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@64 {
compatible = "richtek,rt5190a";
reg = <0x64>;
interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;
vin2-supply = <&rt5190_buck1>;
vin3-supply = <&rt5190_buck1>;
vin4-supply = <&rt5190_buck1>;
regulators {
rt5190_buck1: buck1 {
regulator-name = "rt5190a-buck1";
regulator-min-microvolt = <5090000>;
regulator-max-microvolt = <5090000>;
regulator-allowed-modes = <RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
regulator-boot-on;
};
buck2 {
regulator-name = "rt5190a-buck2";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <1400000>;
regulator-boot-on;
};
buck3 {
regulator-name = "rt5190a-buck3";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <1400000>;
regulator-boot-on;
};
buck4 {
regulator-name = "rt5190a-buck4";
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <850000>;
regulator-allowed-modes = <RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
regulator-boot-on;
};
ldo {
regulator-name = "rt5190a-ldo";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-boot-on;
};
};
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/ti,tps62360.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TPS6236x Voltage Regulators
maintainers:
- Laxman Dewangan <ldewangan@nvidia.com>
description: |
The TPS6236x are a family of step down dc-dc converter with
an input voltage range of 2.5V to 5.5V. The devices provide
up to 3A peak load current, and an output voltage range of
0.77V to 1.4V (TPS62360/62) and 0.5V to 1.77V (TPS62361B/63).
Datasheet is available at:
https://www.ti.com/lit/gpn/tps62360
allOf:
- $ref: "regulator.yaml#"
properties:
compatible:
enum:
- ti,tps62360
- ti,tps62361
- ti,tps62362
- ti,tps62363
reg:
maxItems: 1
ti,vsel0-gpio:
description: |
GPIO for controlling VSEL0 line. If this property
is missing, then assume that there is no GPIO for
VSEL0 control.
maxItems: 1
ti,vsel1-gpio:
description: |
GPIO for controlling VSEL1 line. If this property
is missing, then assume that there is no GPIO for
VSEL1 control.
maxItems: 1
ti,enable-vout-discharge:
description: Enable output discharge.
type: boolean
ti,enable-pull-down:
description: Enable pull down.
type: boolean
ti,vsel0-state-high:
description: |
Initial state of VSEL0 input is high. If this property
is missing, then assume the state as low.
type: boolean
ti,vsel1-state-high:
description: |
Initial state of VSEL1 input is high. If this property
is missing, then assume the state as low.
type: boolean
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@60 {
compatible = "ti,tps62361";
reg = <0x60>;
regulator-name = "tps62361-vout";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
ti,vsel0-gpio = <&gpio1 16 GPIO_ACTIVE_HIGH>;
ti,vsel1-gpio = <&gpio1 17 GPIO_ACTIVE_HIGH>;
ti,vsel0-state-high;
ti,vsel1-state-high;
ti,enable-pull-down;
ti,enable-vout-discharge;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/ti,tps62864.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI TPS62864/TPS6286/TPS62868/TPS62869 voltage regulator
maintainers:
- Vincent Whitchurch <vincent.whitchurch@axis.com>
properties:
compatible:
enum:
- ti,tps62864
- ti,tps62866
- ti,tps62868
- ti,tps62869
reg:
maxItems: 1
regulators:
type: object
properties:
"SW":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
#include <dt-bindings/regulator/ti,tps62864.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@48 {
compatible = "ti,tps62864";
reg = <0x48>;
regulators {
SW {
regulator-name = "+0.85V";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <890000>;
regulator-initial-mode = <TPS62864_MODE_FPWM>;
};
};
};
};
...
TPS62360 Voltage regulators
Required properties:
- compatible: Must be one of the following.
"ti,tps62360"
"ti,tps62361",
"ti,tps62362",
"ti,tps62363",
- reg: I2C slave address
Optional properties:
- ti,enable-vout-discharge: Enable output discharge. This is boolean value.
- ti,enable-pull-down: Enable pull down. This is boolean value.
- ti,vsel0-gpio: GPIO for controlling VSEL0 line.
If this property is missing, then assume that there is no GPIO
for vsel0 control.
- ti,vsel1-gpio: Gpio for controlling VSEL1 line.
If this property is missing, then assume that there is no GPIO
for vsel1 control.
- ti,vsel0-state-high: Initial state of vsel0 input is high.
If this property is missing, then assume the state as low (0).
- ti,vsel1-state-high: Initial state of vsel1 input is high.
If this property is missing, then assume the state as low (0).
Any property defined as part of the core regulator binding, defined in
regulator.txt, can also be used.
Example:
abc: tps62360 {
compatible = "ti,tps62361";
reg = <0x60>;
regulator-name = "tps62361-vout";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on
ti,vsel0-gpio = <&gpio1 16 0>;
ti,vsel1-gpio = <&gpio1 17 0>;
ti,vsel0-state-high;
ti,vsel1-state-high;
ti,enable-pull-down;
ti,enable-force-pwm;
ti,enable-vout-discharge;
};
...@@ -984,6 +984,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY ...@@ -984,6 +984,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY
tristate "Raspberry Pi 7-inch touchscreen panel ATTINY regulator" tristate "Raspberry Pi 7-inch touchscreen panel ATTINY regulator"
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on I2C depends on I2C
depends on OF_GPIO
select REGMAP_I2C select REGMAP_I2C
help help
This driver supports ATTINY regulator on the Raspberry Pi 7-inch This driver supports ATTINY regulator on the Raspberry Pi 7-inch
...@@ -1046,6 +1047,16 @@ config REGULATOR_RT5033 ...@@ -1046,6 +1047,16 @@ config REGULATOR_RT5033
RT5033 PMIC. The device supports multiple regulators like RT5033 PMIC. The device supports multiple regulators like
current source, LDO and Buck. current source, LDO and Buck.
config REGULATOR_RT5190A
tristate "Richtek RT5190A PMIC"
depends on I2C
select REGMAP_I2C
help
This adds support for voltage regulator in Richtek RT5190A PMIC.
It integratas 1 channel buck controller, 3 channels high efficiency
buck converters, 1 LDO, mute AC OFF depop function, with the general
I2C control interface.
config REGULATOR_RT6160 config REGULATOR_RT6160
tristate "Richtek RT6160 BuckBoost voltage regulator" tristate "Richtek RT6160 BuckBoost voltage regulator"
depends on I2C depends on I2C
...@@ -1263,6 +1274,15 @@ config REGULATOR_TPS62360 ...@@ -1263,6 +1274,15 @@ config REGULATOR_TPS62360
high-frequency synchronous step down dc-dc converter optimized high-frequency synchronous step down dc-dc converter optimized
for battery-powered portable applications. for battery-powered portable applications.
config REGULATOR_TPS6286X
tristate "TI TPS6286x Power Regulator"
depends on I2C && OF
select REGMAP_I2C
help
This driver supports TPS6236x voltage regulator chips. These are
high-frequency synchronous step-down converters with an I2C
interface.
config REGULATOR_TPS65023 config REGULATOR_TPS65023
tristate "TI TPS65023 Power regulators" tristate "TI TPS65023 Power regulators"
depends on I2C depends on I2C
......
...@@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o ...@@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o
obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o
obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o
obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o
obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o
obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o
obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o
...@@ -149,6 +150,7 @@ obj-$(CONFIG_REGULATOR_SY8827N) += sy8827n.o ...@@ -149,6 +150,7 @@ obj-$(CONFIG_REGULATOR_SY8827N) += sy8827n.o
obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o
obj-$(CONFIG_REGULATOR_TPS6286X) += tps6286x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o
......
...@@ -459,7 +459,7 @@ static int max8973_thermal_read_temp(void *data, int *temp) ...@@ -459,7 +459,7 @@ static int max8973_thermal_read_temp(void *data, int *temp)
return ret; return ret;
} }
/* +1 degC to trigger cool devive */ /* +1 degC to trigger cool device */
if (val & MAX77621_CHIPID_TJINT_S) if (val & MAX77621_CHIPID_TJINT_S)
*temp = mchip->junction_temp_warning + 1000; *temp = mchip->junction_temp_warning + 1000;
else else
......
...@@ -1121,6 +1121,39 @@ static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { ...@@ -1121,6 +1121,39 @@ static const struct rpmh_vreg_init_data pmx55_vreg_data[] = {
{} {}
}; };
static const struct rpmh_vreg_init_data pmx65_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"),
RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"),
RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"),
RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"),
RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"),
RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"),
RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"),
RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l18"),
RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"),
RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"),
RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6-l16"),
RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6-l16"),
RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7"),
RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8-l9"),
RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l8-l9"),
RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10"),
RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11-l13"),
RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"),
RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l11-l13"),
RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"),
RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"),
RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l6-l16"),
RPMH_VREG("ldo17", "ldo%s17", &pmic5_nldo, "vdd-l17"),
/* ldo18 not configured */
RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo, "vdd-l19"),
RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo, "vdd-l20"),
RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo, "vdd-l21"),
{}
};
static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { static const struct rpmh_vreg_init_data pm7325_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"),
...@@ -1276,6 +1309,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { ...@@ -1276,6 +1309,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
.compatible = "qcom,pmx55-rpmh-regulators", .compatible = "qcom,pmx55-rpmh-regulators",
.data = pmx55_vreg_data, .data = pmx55_vreg_data,
}, },
{
.compatible = "qcom,pmx65-rpmh-regulators",
.data = pmx65_vreg_data,
},
{ {
.compatible = "qcom,pm7325-rpmh-regulators", .compatible = "qcom,pm7325-rpmh-regulators",
.data = pm7325_vreg_data, .data = pm7325_vreg_data,
......
...@@ -1317,8 +1317,10 @@ static int rpm_reg_probe(struct platform_device *pdev) ...@@ -1317,8 +1317,10 @@ static int rpm_reg_probe(struct platform_device *pdev)
for_each_available_child_of_node(dev->of_node, node) { for_each_available_child_of_node(dev->of_node, node) {
vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
if (!vreg) if (!vreg) {
of_node_put(node);
return -ENOMEM; return -ENOMEM;
}
ret = rpm_regulator_init_vreg(vreg, dev, node, rpm, vreg_data); ret = rpm_regulator_init_vreg(vreg, dev, node, rpm, vreg_data);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -21,63 +22,146 @@ ...@@ -21,63 +22,146 @@
/* I2C registers of the Atmel microcontroller. */ /* I2C registers of the Atmel microcontroller. */
#define REG_ID 0x80 #define REG_ID 0x80
#define REG_PORTA 0x81 #define REG_PORTA 0x81
#define REG_PORTA_HF BIT(2)
#define REG_PORTA_VF BIT(3)
#define REG_PORTB 0x82 #define REG_PORTB 0x82
#define REG_PORTC 0x83
#define REG_POWERON 0x85 #define REG_POWERON 0x85
#define REG_PWM 0x86 #define REG_PWM 0x86
#define REG_ADDR_L 0x8c
#define REG_ADDR_H 0x8d
#define REG_WRITE_DATA_H 0x90
#define REG_WRITE_DATA_L 0x91
#define PA_LCD_DITHB BIT(0)
#define PA_LCD_MODE BIT(1)
#define PA_LCD_LR BIT(2)
#define PA_LCD_UD BIT(3)
#define PB_BRIDGE_PWRDNX_N BIT(0)
#define PB_LCD_VCC_N BIT(1)
#define PB_LCD_MAIN BIT(7)
#define PC_LED_EN BIT(0)
#define PC_RST_TP_N BIT(1)
#define PC_RST_LCD_N BIT(2)
#define PC_RST_BRIDGE_N BIT(3)
enum gpio_signals {
RST_BRIDGE_N, /* TC358762 bridge reset */
RST_TP_N, /* Touch controller reset */
NUM_GPIO
};
struct gpio_signal_mappings {
unsigned int reg;
unsigned int mask;
};
static const struct gpio_signal_mappings mappings[NUM_GPIO] = {
[RST_BRIDGE_N] = { REG_PORTC, PC_RST_BRIDGE_N | PC_RST_LCD_N },
[RST_TP_N] = { REG_PORTC, PC_RST_TP_N },
};
struct attiny_lcd {
/* lock to serialise overall accesses to the Atmel */
struct mutex lock;
struct regmap *regmap;
bool gpio_states[NUM_GPIO];
u8 port_states[3];
struct gpio_chip gc;
};
static const struct regmap_config attiny_regmap_config = { static const struct regmap_config attiny_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 8, .val_bits = 8,
.max_register = REG_PWM, .disable_locking = 1,
.cache_type = REGCACHE_NONE, .max_register = REG_WRITE_DATA_L,
.cache_type = REGCACHE_RBTREE,
};
static int attiny_set_port_state(struct attiny_lcd *state, int reg, u8 val)
{
state->port_states[reg - REG_PORTA] = val;
return regmap_write(state->regmap, reg, val);
};
static u8 attiny_get_port_state(struct attiny_lcd *state, int reg)
{
return state->port_states[reg - REG_PORTA];
}; };
static int attiny_lcd_power_enable(struct regulator_dev *rdev) static int attiny_lcd_power_enable(struct regulator_dev *rdev)
{ {
unsigned int data; struct attiny_lcd *state = rdev_get_drvdata(rdev);
regmap_write(rdev->regmap, REG_POWERON, 1); mutex_lock(&state->lock);
/* Wait for nPWRDWN to go low to indicate poweron is done. */
regmap_read_poll_timeout(rdev->regmap, REG_PORTB, data, /* Ensure bridge, and tp stay in reset */
data & BIT(0), 10, 1000000); attiny_set_port_state(state, REG_PORTC, 0);
usleep_range(5000, 10000);
/* Default to the same orientation as the closed source /* Default to the same orientation as the closed source
* firmware used for the panel. Runtime rotation * firmware used for the panel. Runtime rotation
* configuration will be supported using VC4's plane * configuration will be supported using VC4's plane
* orientation bits. * orientation bits.
*/ */
regmap_write(rdev->regmap, REG_PORTA, BIT(2)); attiny_set_port_state(state, REG_PORTA, PA_LCD_LR);
usleep_range(5000, 10000);
/* Main regulator on, and power to the panel (LCD_VCC_N) */
attiny_set_port_state(state, REG_PORTB, PB_LCD_MAIN);
usleep_range(5000, 10000);
/* Bring controllers out of reset */
attiny_set_port_state(state, REG_PORTC, PC_LED_EN);
msleep(80);
mutex_unlock(&state->lock);
return 0; return 0;
} }
static int attiny_lcd_power_disable(struct regulator_dev *rdev) static int attiny_lcd_power_disable(struct regulator_dev *rdev)
{ {
struct attiny_lcd *state = rdev_get_drvdata(rdev);
mutex_lock(&state->lock);
regmap_write(rdev->regmap, REG_PWM, 0); regmap_write(rdev->regmap, REG_PWM, 0);
regmap_write(rdev->regmap, REG_POWERON, 0); usleep_range(5000, 10000);
udelay(1);
attiny_set_port_state(state, REG_PORTA, 0);
usleep_range(5000, 10000);
attiny_set_port_state(state, REG_PORTB, PB_LCD_VCC_N);
usleep_range(5000, 10000);
attiny_set_port_state(state, REG_PORTC, 0);
msleep(30);
mutex_unlock(&state->lock);
return 0; return 0;
} }
static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev)
{ {
struct attiny_lcd *state = rdev_get_drvdata(rdev);
unsigned int data; unsigned int data;
int ret; int ret, i;
ret = regmap_read(rdev->regmap, REG_POWERON, &data); mutex_lock(&state->lock);
if (ret < 0)
return ret;
if (!(data & BIT(0))) for (i = 0; i < 10; i++) {
return 0; ret = regmap_read(rdev->regmap, REG_PORTC, &data);
if (!ret)
break;
usleep_range(10000, 12000);
}
mutex_unlock(&state->lock);
ret = regmap_read(rdev->regmap, REG_PORTB, &data);
if (ret < 0) if (ret < 0)
return ret; return ret;
return data & BIT(0); return data & PC_RST_BRIDGE_N;
} }
static const struct regulator_init_data attiny_regulator_default = { static const struct regulator_init_data attiny_regulator_default = {
...@@ -101,33 +185,104 @@ static const struct regulator_desc attiny_regulator = { ...@@ -101,33 +185,104 @@ static const struct regulator_desc attiny_regulator = {
static int attiny_update_status(struct backlight_device *bl) static int attiny_update_status(struct backlight_device *bl)
{ {
struct regmap *regmap = bl_get_data(bl); struct attiny_lcd *state = bl_get_data(bl);
struct regmap *regmap = state->regmap;
int brightness = bl->props.brightness; int brightness = bl->props.brightness;
int ret, i;
mutex_lock(&state->lock);
if (bl->props.power != FB_BLANK_UNBLANK || if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK) bl->props.fb_blank != FB_BLANK_UNBLANK)
brightness = 0; brightness = 0;
return regmap_write(regmap, REG_PWM, brightness); for (i = 0; i < 10; i++) {
} ret = regmap_write(regmap, REG_PWM, brightness);
if (!ret)
static int attiny_get_brightness(struct backlight_device *bl) break;
{ }
struct regmap *regmap = bl_get_data(bl);
int ret, brightness;
ret = regmap_read(regmap, REG_PWM, &brightness); mutex_unlock(&state->lock);
if (ret)
return ret;
return brightness; return ret;
} }
static const struct backlight_ops attiny_bl = { static const struct backlight_ops attiny_bl = {
.update_status = attiny_update_status, .update_status = attiny_update_status,
.get_brightness = attiny_get_brightness,
}; };
static int attiny_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
{
return GPIO_LINE_DIRECTION_OUT;
}
static void attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
{
struct attiny_lcd *state = gpiochip_get_data(gc);
u8 last_val;
if (off >= NUM_GPIO)
return;
mutex_lock(&state->lock);
last_val = attiny_get_port_state(state, mappings[off].reg);
if (val)
last_val |= mappings[off].mask;
else
last_val &= ~mappings[off].mask;
attiny_set_port_state(state, mappings[off].reg, last_val);
if (off == RST_BRIDGE_N && val) {
usleep_range(5000, 8000);
regmap_write(state->regmap, REG_ADDR_H, 0x04);
usleep_range(5000, 8000);
regmap_write(state->regmap, REG_ADDR_L, 0x7c);
usleep_range(5000, 8000);
regmap_write(state->regmap, REG_WRITE_DATA_H, 0x00);
usleep_range(5000, 8000);
regmap_write(state->regmap, REG_WRITE_DATA_L, 0x00);
msleep(100);
}
mutex_unlock(&state->lock);
}
static int attiny_i2c_read(struct i2c_client *client, u8 reg, unsigned int *buf)
{
struct i2c_msg msgs[1];
u8 addr_buf[1] = { reg };
u8 data_buf[1] = { 0, };
int ret;
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = ARRAY_SIZE(addr_buf);
msgs[0].buf = addr_buf;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs))
return -EIO;
usleep_range(5000, 10000);
/* Read data from register */
msgs[0].addr = client->addr;
msgs[0].flags = I2C_M_RD;
msgs[0].len = 1;
msgs[0].buf = data_buf;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs))
return -EIO;
*buf = data_buf[0];
return 0;
}
/* /*
* I2C driver interface functions * I2C driver interface functions
*/ */
...@@ -138,22 +293,30 @@ static int attiny_i2c_probe(struct i2c_client *i2c, ...@@ -138,22 +293,30 @@ static int attiny_i2c_probe(struct i2c_client *i2c,
struct regulator_config config = { }; struct regulator_config config = { };
struct backlight_device *bl; struct backlight_device *bl;
struct regulator_dev *rdev; struct regulator_dev *rdev;
struct attiny_lcd *state;
struct regmap *regmap; struct regmap *regmap;
unsigned int data; unsigned int data;
int ret; int ret;
state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
mutex_init(&state->lock);
i2c_set_clientdata(i2c, state);
regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config); regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config);
if (IS_ERR(regmap)) { if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap); ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n", dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret); ret);
return ret; goto error;
} }
ret = regmap_read(regmap, REG_ID, &data); ret = attiny_i2c_read(i2c, REG_ID, &data);
if (ret < 0) { if (ret < 0) {
dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret);
return ret; goto error;
} }
switch (data) { switch (data) {
...@@ -162,34 +325,73 @@ static int attiny_i2c_probe(struct i2c_client *i2c, ...@@ -162,34 +325,73 @@ static int attiny_i2c_probe(struct i2c_client *i2c,
break; break;
default: default:
dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data); dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data);
return -ENODEV; ret = -ENODEV;
goto error;
} }
regmap_write(regmap, REG_POWERON, 0); regmap_write(regmap, REG_POWERON, 0);
mdelay(1); msleep(30);
regmap_write(regmap, REG_PWM, 0);
config.dev = &i2c->dev; config.dev = &i2c->dev;
config.regmap = regmap; config.regmap = regmap;
config.of_node = i2c->dev.of_node; config.of_node = i2c->dev.of_node;
config.init_data = &attiny_regulator_default; config.init_data = &attiny_regulator_default;
config.driver_data = state;
rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config);
if (IS_ERR(rdev)) { if (IS_ERR(rdev)) {
dev_err(&i2c->dev, "Failed to register ATTINY regulator\n"); dev_err(&i2c->dev, "Failed to register ATTINY regulator\n");
return PTR_ERR(rdev); ret = PTR_ERR(rdev);
goto error;
} }
props.type = BACKLIGHT_RAW; props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff; props.max_brightness = 0xff;
bl = devm_backlight_device_register(&i2c->dev,
"7inch-touchscreen-panel-bl", state->regmap = regmap;
&i2c->dev, regmap, &attiny_bl,
bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev),
&i2c->dev, state, &attiny_bl,
&props); &props);
if (IS_ERR(bl)) if (IS_ERR(bl)) {
return PTR_ERR(bl); ret = PTR_ERR(bl);
goto error;
}
bl->props.brightness = 0xff; bl->props.brightness = 0xff;
state->gc.parent = &i2c->dev;
state->gc.label = i2c->name;
state->gc.owner = THIS_MODULE;
state->gc.of_node = i2c->dev.of_node;
state->gc.base = -1;
state->gc.ngpio = NUM_GPIO;
state->gc.set = attiny_gpio_set;
state->gc.get_direction = attiny_gpio_get_direction;
state->gc.can_sleep = true;
ret = devm_gpiochip_add_data(&i2c->dev, &state->gc, state);
if (ret) {
dev_err(&i2c->dev, "Failed to create gpiochip: %d\n", ret);
goto error;
}
return 0;
error:
mutex_destroy(&state->lock);
return ret;
}
static int attiny_i2c_remove(struct i2c_client *client)
{
struct attiny_lcd *state = i2c_get_clientdata(client);
mutex_destroy(&state->lock);
return 0; return 0;
} }
...@@ -205,6 +407,7 @@ static struct i2c_driver attiny_regulator_driver = { ...@@ -205,6 +407,7 @@ static struct i2c_driver attiny_regulator_driver = {
.of_match_table = of_match_ptr(attiny_dt_ids), .of_match_table = of_match_ptr(attiny_dt_ids),
}, },
.probe = attiny_i2c_probe, .probe = attiny_i2c_probe,
.remove = attiny_i2c_remove,
}; };
module_i2c_driver(attiny_regulator_driver); module_i2c_driver(attiny_regulator_driver);
......
// SPDX-License-Identifier: GPL-2.0+
#include <dt-bindings/regulator/richtek,rt5190a-regulator.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#define RT5190A_REG_MANUFACTURE 0x00
#define RT5190A_REG_BUCK2VSEL 0x04
#define RT5190A_REG_BUCK3VSEL 0x05
#define RT5190A_REG_DCDCCNTL 0x06
#define RT5190A_REG_ENABLE 0x07
#define RT5190A_REG_DISCHARGE 0x09
#define RT5190A_REG_PROTMODE 0x0A
#define RT5190A_REG_MUTECNTL 0x0B
#define RT5190A_REG_PGSTAT 0x0F
#define RT5190A_REG_OVINT 0x10
#define RT5190A_REG_HOTDIEMASK 0x17
#define RT5190A_VSEL_MASK GENMASK(6, 0)
#define RT5190A_RID_BITMASK(rid) BIT(rid + 1)
#define RT5190A_BUCK1_DISCHG_MASK GENMASK(1, 0)
#define RT5190A_BUCK1_DISCHG_ONVAL 0x01
#define RT5190A_OVERVOLT_MASK GENMASK(7, 0)
#define RT5190A_UNDERVOLT_MASK GENMASK(15, 8)
#define RT5190A_CH234OT_MASK BIT(29)
#define RT5190A_CHIPOT_MASK BIT(28)
#define RT5190A_BUCK23_MINUV 600000
#define RT5190A_BUCK23_MAXUV 1400000
#define RT5190A_BUCK23_STEPUV 10000
#define RT5190A_BUCK23_STEPNUM ((1400000 - 600000) / 10000 + 1)
enum {
RT5190A_IDX_BUCK1 = 0,
RT5190A_IDX_BUCK2,
RT5190A_IDX_BUCK3,
RT5190A_IDX_BUCK4,
RT5190A_IDX_LDO,
RT5190A_MAX_IDX
};
struct rt5190a_priv {
struct device *dev;
struct regmap *regmap;
struct regulator_desc rdesc[RT5190A_MAX_IDX];
struct regulator_dev *rdev[RT5190A_MAX_IDX];
};
static int rt5190a_get_error_flags(struct regulator_dev *rdev,
unsigned int *flags)
{
struct regmap *regmap = rdev_get_regmap(rdev);
int rid = rdev_get_id(rdev);
unsigned int pgood_stat;
int ret;
ret = regmap_read(regmap, RT5190A_REG_PGSTAT, &pgood_stat);
if (ret)
return ret;
if (!(pgood_stat & RT5190A_RID_BITMASK(rid)))
*flags = REGULATOR_ERROR_FAIL;
else
*flags = 0;
return 0;
}
static int rt5190a_fixed_buck_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
struct regmap *regmap = rdev_get_regmap(rdev);
int rid = rdev_get_id(rdev);
unsigned int mask = RT5190A_RID_BITMASK(rid), val;
switch (mode) {
case REGULATOR_MODE_FAST:
val = mask;
break;
case REGULATOR_MODE_NORMAL:
val = 0;
break;
default:
return -EINVAL;
}
return regmap_update_bits(regmap, RT5190A_REG_DCDCCNTL, mask, val);
}
static unsigned int rt5190a_fixed_buck_get_mode(struct regulator_dev *rdev)
{
struct regmap *regmap = rdev_get_regmap(rdev);
int rid = rdev_get_id(rdev);
unsigned int val;
int ret;
ret = regmap_read(regmap, RT5190A_REG_DCDCCNTL, &val);
if (ret) {
dev_err(&rdev->dev, "Failed to get mode [%d]\n", ret);
return ret;
}
if (val & RT5190A_RID_BITMASK(rid))
return REGULATOR_MODE_FAST;
return REGULATOR_MODE_NORMAL;
}
static const struct regulator_ops rt5190a_ranged_buck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_active_discharge = regulator_set_active_discharge_regmap,
.get_error_flags = rt5190a_get_error_flags,
};
static const struct regulator_ops rt5190a_fixed_buck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_active_discharge = regulator_set_active_discharge_regmap,
.set_mode = rt5190a_fixed_buck_set_mode,
.get_mode = rt5190a_fixed_buck_get_mode,
.get_error_flags = rt5190a_get_error_flags,
};
static const struct regulator_ops rt5190a_fixed_ldo_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_active_discharge = regulator_set_active_discharge_regmap,
.get_error_flags = rt5190a_get_error_flags,
};
static irqreturn_t rt5190a_irq_handler(int irq, void *data)
{
struct rt5190a_priv *priv = data;
__le32 raws;
unsigned int events, fields;
static const struct {
unsigned int bitmask;
unsigned int report;
} event_tbl[] = {
{ RT5190A_OVERVOLT_MASK, REGULATOR_ERROR_REGULATION_OUT },
{ RT5190A_UNDERVOLT_MASK, REGULATOR_ERROR_UNDER_VOLTAGE }
};
int i, j, ret;
ret = regmap_raw_read(priv->regmap, RT5190A_REG_OVINT, &raws,
sizeof(raws));
if (ret) {
dev_err(priv->dev, "Failed to read events\n");
return IRQ_NONE;
}
events = le32_to_cpu(raws);
ret = regmap_raw_write(priv->regmap, RT5190A_REG_OVINT, &raws,
sizeof(raws));
if (ret)
dev_err(priv->dev, "Failed to write-clear events\n");
/* Handle OV,UV events */
for (i = 0; i < ARRAY_SIZE(event_tbl); i++) {
fields = events & event_tbl[i].bitmask;
fields >>= ffs(event_tbl[i].bitmask) - 1;
for (j = 0; j < RT5190A_MAX_IDX; j++) {
if (!(fields & RT5190A_RID_BITMASK(j)))
continue;
regulator_notifier_call_chain(priv->rdev[j],
event_tbl[i].report,
NULL);
}
}
/* Handle CH234 OT event */
if (events & RT5190A_CH234OT_MASK) {
for (j = RT5190A_IDX_BUCK2; j < RT5190A_IDX_LDO; j++) {
regulator_notifier_call_chain(priv->rdev[j],
REGULATOR_ERROR_OVER_TEMP,
NULL);
}
}
/* Warning if CHIP OT occur */
if (events & RT5190A_CHIPOT_MASK)
dev_warn(priv->dev, "CHIP overheat\n");
return IRQ_HANDLED;
}
static unsigned int rt5190a_of_map_mode(unsigned int mode)
{
switch (mode) {
case RT5190A_OPMODE_AUTO:
return REGULATOR_MODE_NORMAL;
case RT5190A_OPMODE_FPWM:
return REGULATOR_MODE_FAST;
default:
return REGULATOR_MODE_INVALID;
}
}
static int rt5190a_of_parse_cb(struct rt5190a_priv *priv, int rid,
struct of_regulator_match *match)
{
struct regulator_desc *desc = priv->rdesc + rid;
struct regulator_init_data *init_data = match->init_data;
struct device_node *np = match->of_node;
bool latchup_enable;
unsigned int mask = RT5190A_RID_BITMASK(rid), val;
switch (rid) {
case RT5190A_IDX_BUCK1:
case RT5190A_IDX_BUCK4:
case RT5190A_IDX_LDO:
init_data->constraints.apply_uV = 0;
if (init_data->constraints.min_uV ==
init_data->constraints.max_uV)
desc->fixed_uV = init_data->constraints.min_uV;
else {
dev_err(priv->dev,
"Variable voltage for fixed regulator\n");
return -EINVAL;
}
break;
default:
break;
}
latchup_enable = of_property_read_bool(np, "richtek,latchup-enable");
/* latchup: 0, default hiccup: 1 */
val = !latchup_enable ? mask : 0;
return regmap_update_bits(priv->regmap, RT5190A_REG_PROTMODE, mask, val);
}
static void rt5190a_fillin_regulator_desc(struct regulator_desc *desc, int rid)
{
static const char * const regu_name[] = { "buck1", "buck2",
"buck3", "buck4",
"ldo" };
static const char * const supply[] = { NULL, "vin2", "vin3", "vin4",
"vinldo" };
desc->name = regu_name[rid];
desc->supply_name = supply[rid];
desc->owner = THIS_MODULE;
desc->type = REGULATOR_VOLTAGE;
desc->id = rid;
desc->enable_reg = RT5190A_REG_ENABLE;
desc->enable_mask = RT5190A_RID_BITMASK(rid);
desc->active_discharge_reg = RT5190A_REG_DISCHARGE;
desc->active_discharge_mask = RT5190A_RID_BITMASK(rid);
desc->active_discharge_on = RT5190A_RID_BITMASK(rid);
switch (rid) {
case RT5190A_IDX_BUCK1:
desc->active_discharge_mask = RT5190A_BUCK1_DISCHG_MASK;
desc->active_discharge_on = RT5190A_BUCK1_DISCHG_ONVAL;
desc->n_voltages = 1;
desc->ops = &rt5190a_fixed_buck_ops;
desc->of_map_mode = rt5190a_of_map_mode;
break;
case RT5190A_IDX_BUCK2:
desc->vsel_reg = RT5190A_REG_BUCK2VSEL;
desc->vsel_mask = RT5190A_VSEL_MASK;
desc->min_uV = RT5190A_BUCK23_MINUV;
desc->uV_step = RT5190A_BUCK23_STEPUV;
desc->n_voltages = RT5190A_BUCK23_STEPNUM;
desc->ops = &rt5190a_ranged_buck_ops;
break;
case RT5190A_IDX_BUCK3:
desc->vsel_reg = RT5190A_REG_BUCK3VSEL;
desc->vsel_mask = RT5190A_VSEL_MASK;
desc->min_uV = RT5190A_BUCK23_MINUV;
desc->uV_step = RT5190A_BUCK23_STEPUV;
desc->n_voltages = RT5190A_BUCK23_STEPNUM;
desc->ops = &rt5190a_ranged_buck_ops;
break;
case RT5190A_IDX_BUCK4:
desc->n_voltages = 1;
desc->ops = &rt5190a_fixed_buck_ops;
desc->of_map_mode = rt5190a_of_map_mode;
break;
case RT5190A_IDX_LDO:
desc->n_voltages = 1;
desc->ops = &rt5190a_fixed_ldo_ops;
break;
}
}
static struct of_regulator_match rt5190a_regulator_match[] = {
{ .name = "buck1", },
{ .name = "buck2", },
{ .name = "buck3", },
{ .name = "buck4", },
{ .name = "ldo", }
};
static int rt5190a_parse_regulator_dt_data(struct rt5190a_priv *priv)
{
struct device_node *regulator_np;
struct regulator_desc *reg_desc;
struct of_regulator_match *match;
int i, ret;
for (i = 0; i < RT5190A_MAX_IDX; i++) {
reg_desc = priv->rdesc + i;
match = rt5190a_regulator_match + i;
rt5190a_fillin_regulator_desc(reg_desc, i);
match->desc = reg_desc;
}
regulator_np = of_get_child_by_name(priv->dev->of_node, "regulators");
if (!regulator_np) {
dev_err(priv->dev, "Could not find 'regulators' node\n");
return -ENODEV;
}
ret = of_regulator_match(priv->dev, regulator_np,
rt5190a_regulator_match,
ARRAY_SIZE(rt5190a_regulator_match));
of_node_put(regulator_np);
if (ret < 0) {
dev_err(priv->dev,
"Error parsing regulator init data: %d\n", ret);
return ret;
}
for (i = 0; i < RT5190A_MAX_IDX; i++) {
match = rt5190a_regulator_match + i;
ret = rt5190a_of_parse_cb(priv, i, match);
if (ret) {
dev_err(priv->dev, "Failed in [%d] of_parse_cb\n", i);
return ret;
}
}
return 0;
}
static const struct reg_sequence rt5190a_init_patch[] = {
{ 0x09, 0x3d, },
{ 0x0a, 0x3e, },
{ 0x0b, 0x01, },
{ 0x10, 0xff, },
{ 0x11, 0xff, },
{ 0x12, 0xff, },
{ 0x13, 0xff, },
{ 0x14, 0, },
{ 0x15, 0, },
{ 0x16, 0x3e, },
{ 0x17, 0, }
};
static int rt5190a_device_initialize(struct rt5190a_priv *priv)
{
bool mute_enable;
int ret;
ret = regmap_register_patch(priv->regmap, rt5190a_init_patch,
ARRAY_SIZE(rt5190a_init_patch));
if (ret) {
dev_err(priv->dev, "Failed to do register patch\n");
return ret;
}
mute_enable = device_property_read_bool(priv->dev,
"richtek,mute-enable");
if (mute_enable) {
ret = regmap_write(priv->regmap, RT5190A_REG_MUTECNTL, 0x00);
if (ret) {
dev_err(priv->dev, "Failed to enable mute function\n");
return ret;
}
}
return 0;
}
static int rt5190a_device_check(struct rt5190a_priv *priv)
{
u16 devid;
int ret;
ret = regmap_raw_read(priv->regmap, RT5190A_REG_MANUFACTURE, &devid,
sizeof(devid));
if (ret)
return ret;
if (devid) {
dev_err(priv->dev, "Incorrect device id 0x%04x\n", devid);
return -ENODEV;
}
return 0;
}
static const struct regmap_config rt5190a_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RT5190A_REG_HOTDIEMASK,
};
static int rt5190a_probe(struct i2c_client *i2c)
{
struct rt5190a_priv *priv;
struct regulator_config cfg = {};
int i, ret;
priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &i2c->dev;
priv->regmap = devm_regmap_init_i2c(i2c, &rt5190a_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(&i2c->dev, "Failed to allocate regmap\n");
return PTR_ERR(priv->regmap);
}
ret = rt5190a_device_check(priv);
if (ret) {
dev_err(&i2c->dev, "Failed to check device %d\n", ret);
return ret;
}
ret = rt5190a_device_initialize(priv);
if (ret) {
dev_err(&i2c->dev, "Failed to initialize the device\n");
return ret;
}
ret = rt5190a_parse_regulator_dt_data(priv);
if (ret) {
dev_err(&i2c->dev, "Failed to parse regulator dt\n");
return ret;
}
cfg.dev = &i2c->dev;
cfg.regmap = priv->regmap;
for (i = 0; i < RT5190A_MAX_IDX; i++) {
struct regulator_desc *desc = priv->rdesc + i;
struct of_regulator_match *match = rt5190a_regulator_match + i;
cfg.init_data = match->init_data;
cfg.of_node = match->of_node;
priv->rdev[i] = devm_regulator_register(&i2c->dev, desc, &cfg);
if (IS_ERR(priv->rdev[i])) {
dev_err(&i2c->dev, "Failed to register regulator %s\n",
desc->name);
return PTR_ERR(priv->rdev[i]);
}
}
if (i2c->irq) {
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5190a_irq_handler,
IRQF_ONESHOT,
dev_name(&i2c->dev), priv);
if (ret) {
dev_err(&i2c->dev, "Failed to register interrupt\n");
return ret;
}
}
return 0;
}
static const struct of_device_id __maybe_unused rt5190a_device_table[] = {
{ .compatible = "richtek,rt5190a", },
{}
};
MODULE_DEVICE_TABLE(of, rt5190a_device_table);
static struct i2c_driver rt5190a_driver = {
.driver = {
.name = "rt5190a",
.of_match_table = rt5190a_device_table,
},
.probe_new = rt5190a_probe,
};
module_i2c_driver(rt5190a_driver);
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_DESCRIPTION("Richtek RT5190A Regulator Driver");
MODULE_LICENSE("GPL v2");
//SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2017 Spreadtrum Communications Inc. * Copyright (C) 2017 Spreadtrum Communications Inc.
*/ */
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
/** /**
* struct ti_abb_info - ABB information per voltage setting * struct ti_abb_info - ABB information per voltage setting
* @opp_sel: one of TI_ABB macro * @opp_sel: one of TI_ABB macro
* @vset: (optional) vset value that LDOVBB needs to be overriden with. * @vset: (optional) vset value that LDOVBB needs to be overridden with.
* *
* Array of per voltage entries organized in the same order as regulator_desc's * Array of per voltage entries organized in the same order as regulator_desc's
* volt_table list. (selector is used to index from this array) * volt_table list. (selector is used to index from this array)
...@@ -484,7 +484,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) ...@@ -484,7 +484,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb)
/* Calculate cycle rate */ /* Calculate cycle rate */
cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate); cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate);
/* Calulate SR2_WTCNT_VALUE */ /* Calculate SR2_WTCNT_VALUE */
sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate); sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate);
dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__,
...@@ -688,7 +688,7 @@ MODULE_DEVICE_TABLE(of, ti_abb_of_match); ...@@ -688,7 +688,7 @@ MODULE_DEVICE_TABLE(of, ti_abb_of_match);
* @pdev: ABB platform device * @pdev: ABB platform device
* *
* Initializes an individual ABB LDO for required Body-Bias. ABB is used to * Initializes an individual ABB LDO for required Body-Bias. ABB is used to
* addional bias supply to SoC modules for power savings or mandatory stability * additional bias supply to SoC modules for power savings or mandatory stability
* configuration at certain Operating Performance Points(OPPs). * configuration at certain Operating Performance Points(OPPs).
* *
* Return: 0 on success or appropriate error value when fails * Return: 0 on success or appropriate error value when fails
......
// SPDX-License-Identifier: GPL-2.0-only
// Copyright Axis Communications AB
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/driver.h>
#include <dt-bindings/regulator/ti,tps62864.h>
#define TPS6286X_VOUT1 0x01
#define TPS6286X_VOUT1_VO1_SET GENMASK(7, 0)
#define TPS6286X_CONTROL 0x03
#define TPS6286X_CONTROL_FPWM BIT(4)
#define TPS6286X_CONTROL_SWEN BIT(5)
#define TPS6286X_MIN_MV 400
#define TPS6286X_MAX_MV 1675
#define TPS6286X_STEP_MV 5
static const struct regmap_config tps6286x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
unsigned int val;
switch (mode) {
case REGULATOR_MODE_NORMAL:
val = 0;
break;
case REGULATOR_MODE_FAST:
val = TPS6286X_CONTROL_FPWM;
break;
default:
return -EINVAL;
}
return regmap_update_bits(rdev->regmap, TPS6286X_CONTROL,
TPS6286X_CONTROL_FPWM, val);
}
static unsigned int tps6286x_get_mode(struct regulator_dev *rdev)
{
unsigned int val;
int ret;
ret = regmap_read(rdev->regmap, TPS6286X_CONTROL, &val);
if (ret < 0)
return 0;
return (val & TPS6286X_CONTROL_FPWM) ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
}
static const struct regulator_ops tps6286x_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_mode = tps6286x_set_mode,
.get_mode = tps6286x_get_mode,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
};
static unsigned int tps6286x_of_map_mode(unsigned int mode)
{
switch (mode) {
case TPS62864_MODE_NORMAL:
return REGULATOR_MODE_NORMAL;
case TPS62864_MODE_FPWM:
return REGULATOR_MODE_FAST;
default:
return REGULATOR_MODE_INVALID;
}
}
static const struct regulator_desc tps6286x_reg = {
.name = "tps6286x",
.of_match = of_match_ptr("SW"),
.owner = THIS_MODULE,
.ops = &tps6286x_regulator_ops,
.of_map_mode = tps6286x_of_map_mode,
.regulators_node = of_match_ptr("regulators"),
.type = REGULATOR_VOLTAGE,
.n_voltages = ((TPS6286X_MAX_MV - TPS6286X_MIN_MV) / TPS6286X_STEP_MV) + 1,
.min_uV = TPS6286X_MIN_MV * 1000,
.uV_step = TPS6286X_STEP_MV * 1000,
.vsel_reg = TPS6286X_VOUT1,
.vsel_mask = TPS6286X_VOUT1_VO1_SET,
.enable_reg = TPS6286X_CONTROL,
.enable_mask = TPS6286X_CONTROL_SWEN,
.ramp_delay = 1000,
/* tDelay + tRamp, rounded up */
.enable_time = 3000,
};
static const struct of_device_id tps6286x_dt_ids[] = {
{ .compatible = "ti,tps62864", },
{ .compatible = "ti,tps62866", },
{ .compatible = "ti,tps62868", },
{ .compatible = "ti,tps62869", },
{ }
};
MODULE_DEVICE_TABLE(of, tps6286x_dt_ids);
static int tps6286x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct device *dev = &i2c->dev;
struct regulator_config config = {};
struct regulator_dev *rdev;
struct regmap *regmap;
regmap = devm_regmap_init_i2c(i2c, &tps6286x_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
config.dev = &i2c->dev;
config.of_node = dev->of_node;
config.regmap = regmap;
rdev = devm_regulator_register(&i2c->dev, &tps6286x_reg, &config);
if (IS_ERR(rdev)) {
dev_err(&i2c->dev, "Failed to register tps6286x regulator\n");
return PTR_ERR(rdev);
}
return 0;
}
static const struct i2c_device_id tps6286x_i2c_id[] = {
{ "tps62864", 0 },
{ "tps62866", 0 },
{ "tps62868", 0 },
{ "tps62869", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, tps6286x_i2c_id);
static struct i2c_driver tps6286x_regulator_driver = {
.driver = {
.name = "tps6286x",
.of_match_table = of_match_ptr(tps6286x_dt_ids),
},
.probe = tps6286x_i2c_probe,
.id_table = tps6286x_i2c_id,
};
module_i2c_driver(tps6286x_regulator_driver);
MODULE_LICENSE("GPL v2");
...@@ -185,10 +185,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev, ...@@ -185,10 +185,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
unsigned int next_sel; unsigned int next_sel;
int delay; int delay;
if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel) next_sel = max_t(unsigned int, selector, vctrl->vtable[vctrl->sel].ovp_min_sel);
next_sel = selector;
else
next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
ret = regulator_set_voltage_rdev(rdev->supply->rdev, ret = regulator_set_voltage_rdev(rdev->supply->rdev,
vctrl->vtable[next_sel].ctrl, vctrl->vtable[next_sel].ctrl,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
struct virtual_consumer_data { struct virtual_consumer_data {
struct mutex lock; struct mutex lock;
...@@ -281,26 +282,53 @@ static const struct attribute_group regulator_virtual_attr_group = { ...@@ -281,26 +282,53 @@ static const struct attribute_group regulator_virtual_attr_group = {
.attrs = regulator_virtual_attributes, .attrs = regulator_virtual_attributes,
}; };
#ifdef CONFIG_OF
static const struct of_device_id regulator_virtual_consumer_of_match[] = {
{ .compatible = "regulator-virtual-consumer" },
{},
};
MODULE_DEVICE_TABLE(of, regulator_virtual_consumer_of_match);
#endif
static int regulator_virtual_probe(struct platform_device *pdev) static int regulator_virtual_probe(struct platform_device *pdev)
{ {
char *reg_id = dev_get_platdata(&pdev->dev); char *reg_id = dev_get_platdata(&pdev->dev);
struct virtual_consumer_data *drvdata; struct virtual_consumer_data *drvdata;
static bool warned;
int ret; int ret;
if (!warned) {
warned = true;
pr_warn("**********************************************************\n");
pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
pr_warn("** **\n");
pr_warn("** regulator-virtual-consumer is only for testing and **\n");
pr_warn("** debugging. Do not use it in a production kernel. **\n");
pr_warn("** **\n");
pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
pr_warn("**********************************************************\n");
}
drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data), drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
GFP_KERNEL); GFP_KERNEL);
if (drvdata == NULL) if (drvdata == NULL)
return -ENOMEM; return -ENOMEM;
/*
* This virtual consumer does not have any hardware-defined supply
* name, so just allow the regulator to be specified in a property
* named "default-supply" when we're being probed from devicetree.
*/
if (!reg_id && pdev->dev.of_node)
reg_id = "default";
mutex_init(&drvdata->lock); mutex_init(&drvdata->lock);
drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id); drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
if (IS_ERR(drvdata->regulator)) { if (IS_ERR(drvdata->regulator))
ret = PTR_ERR(drvdata->regulator); return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->regulator),
dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n", "Failed to obtain supply '%s'\n",
reg_id, ret); reg_id);
return ret;
}
ret = sysfs_create_group(&pdev->dev.kobj, ret = sysfs_create_group(&pdev->dev.kobj,
&regulator_virtual_attr_group); &regulator_virtual_attr_group);
...@@ -334,6 +362,7 @@ static struct platform_driver regulator_virtual_consumer_driver = { ...@@ -334,6 +362,7 @@ static struct platform_driver regulator_virtual_consumer_driver = {
.remove = regulator_virtual_remove, .remove = regulator_virtual_remove,
.driver = { .driver = {
.name = "reg-virt-consumer", .name = "reg-virt-consumer",
.of_match_table = of_match_ptr(regulator_virtual_consumer_of_match),
}, },
}; };
......
...@@ -1112,7 +1112,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev) ...@@ -1112,7 +1112,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev)
if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B) if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B)
return -ENODEV; return -ENODEV;
/* do any regulatior specific init */ /* do any regulator specific init */
switch (pdev->id) { switch (pdev->id) {
case WM8350_DCDC_1: case WM8350_DCDC_1:
val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
......
...@@ -898,11 +898,8 @@ static int spi_geni_probe(struct platform_device *pdev) ...@@ -898,11 +898,8 @@ static int spi_geni_probe(struct platform_device *pdev)
return irq; return irq;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret) { if (ret)
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); return dev_err_probe(dev, ret, "could not set DMA mask\n");
if (ret)
return dev_err_probe(dev, ret, "could not set DMA mask\n");
}
base = devm_platform_ioremap_resource(pdev, 0); base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) if (IS_ERR(base))
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __DT_BINDINGS_RICHTEK_RT5190A_REGULATOR_H__
#define __DT_BINDINGS_RICHTEK_RT5190A_REGULATOR_H__
/*
* BUCK/LDO mode constants which may be used in devicetree properties
* (eg. regulator-allowed-modes).
* See the manufacturer's datasheet for more information on these modes.
*/
#define RT5190A_OPMODE_AUTO 0
#define RT5190A_OPMODE_FPWM 1
#endif
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
#ifndef _DT_BINDINGS_REGULATOR_TI_TPS62864_H
#define _DT_BINDINGS_REGULATOR_TI_TPS62864_H
#define TPS62864_MODE_NORMAL 0
#define TPS62864_MODE_FPWM 1
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment