Merge remote-tracking branch 'regulator/for-5.12' into regulator-next

This commit is contained in:
Mark Brown 2021-02-12 14:00:07 +00:00
commit f03e2a72e5
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
36 changed files with 2651 additions and 1474 deletions

View File

@ -35,6 +35,7 @@ Optional properties:
- interrupts: Interrupt specifiers for two interrupt sources.
- First interrupt specifier is for 'irq1' interrupt.
- Second interrupt specifier is for 'alert' interrupt.
- charger-supply: regulator node for charging current.
- max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs.
- max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs.
- max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs.

View File

@ -4,7 +4,8 @@ Required properties:
- compatible: "microchip,mcp16502"
- reg: I2C slave address
- lpm-gpios: GPIO for LPM pin. Note that this GPIO *must* remain high during
suspend-to-ram, keeping the PMIC into HIBERNATE mode.
suspend-to-ram, keeping the PMIC into HIBERNATE mode; this
property is optional;
- regulators: A node that houses a sub-node for each regulator within
the device. Each sub-node is identified using the node's
name. The content of each sub-node is defined by the

View File

@ -0,0 +1,69 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/mt6315-regulator.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mediatek MT6315 Regulator
maintainers:
- Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>
description: |
The MT6315 is a power management IC (PMIC) configurable with SPMI.
that contains 4 BUCKs output which can combine with each other
by different efuse settings.
properties:
compatible:
const: mediatek,mt6315-regulator
reg:
maxItems: 1
regulators:
type: object
description: List of regulators and its properties
patternProperties:
"^vbuck[1-4]$":
type: object
$ref: "regulator.yaml#"
properties:
regulator-name:
pattern: "^vbuck[1-4]$"
additionalProperties: false
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
pmic@6 {
compatible = "mediatek,mt6315-regulator";
reg = <0x6 0>;
regulators {
vbuck1 {
regulator-compatible = "vbuck1";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <1193750>;
regulator-enable-ramp-delay = <256>;
regulator-allowed-modes = <0 1 2 4>;
};
vbuck3 {
regulator-compatible = "vbuck3";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <1193750>;
regulator-enable-ramp-delay = <256>;
regulator-allowed-modes = <0 1 2 4>;
};
};
};

View File

@ -87,6 +87,11 @@ properties:
additionalProperties: false
sd-vsel-gpios:
description: GPIO that is used to switch LDO5 between being configured by
LDO5CTRL_L or LDO5CTRL_H register. Use this if the SD_VSEL signal is
connected to a host GPIO.
required:
- compatible
- reg

View File

@ -62,8 +62,11 @@ properties:
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 2100
maximum: 4500
deprecated: true
description:
BUCK regulators current limit in mA.
This property is deprecated, please use
"regulator-max-microamp" instead.
Listed current limits in mA are,
2100 (default)
@ -73,21 +76,11 @@ properties:
nxp,phase-shift:
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 45
maximum: 0
default: 0
enum: [ 0, 45, 90, 135, 180, 225, 270, 315 ]
description:
BUCK regulators phase shift control in degrees.
Listed phase shift control values in degrees are,
45
90
135
180
225
270
315
0 (default)
unevaluatedProperties: false
"^vsnvs$":

View File

@ -50,6 +50,8 @@ First Level Nodes - PMIC
"qcom,pm8350-rpmh-regulators"
"qcom,pm8350c-rpmh-regulators"
"qcom,pm8998-rpmh-regulators"
"qcom,pmc8180-rpmh-regulators"
"qcom,pmc8180c-rpmh-regulators"
"qcom,pmi8998-rpmh-regulators"
"qcom,pm6150-rpmh-regulators"
"qcom,pm6150l-rpmh-regulators"

View File

@ -22,11 +22,17 @@ properties:
type: object
properties:
qcom,soft-start-us:
$ref: /schemas/types.yaml#/definitions/uint32
description: Regulator soft start time in microseconds.
enum: [200, 400, 600, 800]
default: 200
interrupts:
maxItems: 1
minItems: 1
maxItems: 2
description:
Short-circuit interrupt for lab.
Short-circuit and over-current interrupts for lab.
required:
- interrupts
@ -35,11 +41,17 @@ properties:
type: object
properties:
qcom,discharge-resistor-kohms:
$ref: /schemas/types.yaml#/definitions/uint32
description: Discharge resistor value in KiloOhms.
enum: [300, 64, 32, 16]
default: 300
interrupts:
maxItems: 1
minItems: 1
maxItems: 2
description:
Short-circuit interrupt for lab.
Short-circuit and over-current interrupts for ibb.
required:
- interrupts
@ -57,13 +69,15 @@ examples:
compatible = "qcom,pmi8998-lab-ibb";
lab {
interrupts = <0x3 0x0 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "sc-err";
interrupts = <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>,
<0x3 0xde 0x0 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "sc-err", "ocp";
};
ibb {
interrupts = <0x3 0x2 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "sc-err";
interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>,
<0x3 0xdc 0x0 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "sc-err", "ocp";
};
};

View File

@ -0,0 +1,35 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/richtek,rt4831-regulator.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Richtek RT4831 Display Bias Voltage Regulator
maintainers:
- ChiYuan Huang <cy_huang@richtek.com>
description: |
RT4831 is a multifunctional device that can provide power to the LCD display
and LCD backlight.
For Display Bias Voltage DSVP and DSVN, the output range is about 4V to 6.5V.
It is sufficient to meet the current LCD power requirement.
DSVLCM is a boost regulator in IC internal as DSVP and DSVN input power.
Its voltage should be configured above 0.15V to 0.2V gap larger than the
voltage needed for DSVP and DSVN. Too much voltage gap could improve the
voltage drop from the heavy loading scenario. But it also make the power
efficiency worse. It's a trade-off.
Datasheet is available at
https://www.richtek.com/assets/product_file/RT4831A/DS4831A-05.pdf
patternProperties:
"^DSV(LCM|P|N)$":
type: object
$ref: regulator.yaml#
description:
Properties for single Display Bias Voltage regulator.
additionalProperties: false

View File

@ -11684,9 +11684,9 @@ F: drivers/video/fbdev/atmel_lcdfb.c
F: include/video/atmel_lcdc.h
MICROCHIP MCP16502 PMIC DRIVER
M: Andrei Stefanescu <andrei.stefanescu@microchip.com>
M: Claudiu Beznea <claudiu.beznea@microchip.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
S: Supported
F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
F: drivers/regulator/mcp16502.c

View File

@ -122,7 +122,7 @@ vreg_s4a_1p8: vreg-s4a-1p8 {
&apps_rsc {
pm8009-rpmh-regulators {
compatible = "qcom,pm8009-rpmh-regulators";
compatible = "qcom,pm8009-1-rpmh-regulators";
qcom,pmic-id = "f";
vdd-s1-supply = <&vph_pwr>;
@ -131,6 +131,13 @@ pm8009-rpmh-regulators {
vdd-l5-l6-supply = <&vreg_bob>;
vdd-l7-supply = <&vreg_s4a_1p8>;
vreg_s2f_0p95: smps2 {
regulator-name = "vreg_s2f_0p95";
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <952000>;
regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
};
vreg_l1f_1p1: ldo1 {
regulator-name = "vreg_l1f_1p1";
regulator-min-microvolt = <1104000>;

View File

@ -21,7 +21,6 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/ab8500.h>
#include <linux/of.h>
#include <linux/of_device.h>

View File

@ -122,15 +122,6 @@ config REGULATOR_AAT2870
If you have a AnalogicTech AAT2870 say Y to enable the
regulator driver.
config REGULATOR_AB3100
tristate "ST-Ericsson AB3100 Regulator functions"
depends on AB3100_CORE
default y if AB3100_CORE
help
These regulators correspond to functionality in the
AB3100 analog baseband dealing with power regulators
for the system.
config REGULATOR_AB8500
bool "ST-Ericsson AB8500 Power Regulators"
depends on AB8500_CORE
@ -179,6 +170,14 @@ config REGULATOR_AS3722
AS3722 PMIC. This will enable support for all the software
controllable DCDC/LDO regulators.
config REGULATOR_ATC260X
tristate "Actions Semi ATC260x PMIC Regulators"
depends on MFD_ATC260X
help
This driver provides support for the voltage regulators on the
ATC260x PMICs. This will enable support for all the software
controllable DCDC/LDO regulators.
config REGULATOR_AXP20X
tristate "X-POWERS AXP20X PMIC Regulators"
depends on MFD_AXP20X
@ -732,6 +731,16 @@ config REGULATOR_MT6311
This driver supports the control of different power rails of device
through regulator interface.
config REGULATOR_MT6315
tristate "MediaTek MT6315 PMIC"
depends on SPMI
select REGMAP_SPMI
help
Say y here to select this option to enable the power regulator of
MediaTek MT6315 PMIC.
This driver supports the control of different power rails of device
through regulator interface.
config REGULATOR_MT6323
tristate "MediaTek MT6323 PMIC"
depends on MFD_MT6397
@ -777,6 +786,16 @@ config REGULATOR_MT6397
This driver supports the control of different power rails of device
through regulator interface.
config REGULATOR_MTK_DVFSRC
tristate "MediaTek DVFSRC regulator driver"
depends on MTK_DVFSRC
help
Say y here to control regulator by DVFSRC (dynamic voltage
and frequency scaling resource collector).
This driver supports to control regulators via the DVFSRC
of Mediatek. It allows for voting on regulator state
between multiple users.
config REGULATOR_PALMAS
tristate "TI Palmas PMIC Regulators"
depends on MFD_PALMAS
@ -828,6 +847,10 @@ config REGULATOR_PF8X00
Say y here to support the regulators found on the NXP
PF8100/PF8121A/PF8200 PMIC.
Say M here if you want to support for the regulators found
on the NXP PF8100/PF8121A/PF8200 PMIC. The module will be named
"pf8x00-regulator".
config REGULATOR_PFUZE100
tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
depends on I2C && OF
@ -969,6 +992,16 @@ config REGULATOR_RT4801
This adds support for voltage regulators in Richtek RT4801 Display Bias IC.
The device supports two regulators (DSVP/DSVN).
config REGULATOR_RT4831
tristate "Richtek RT4831 DSV Regulators"
depends on MFD_RT4831
help
This adds support for voltage regulators in Richtek RT4831.
There are three regulators (VLCM/DSVP/DSVN).
VLCM is a virtual voltage input for DSVP/DSVN inside IC.
And DSVP/DSVN is the real Vout range from 4V to 6.5V.
It's common used to provide the power for the display panel.
config REGULATOR_RT5033
tristate "Richtek RT5033 Regulators"
depends on MFD_RT5033

View File

@ -16,7 +16,6 @@ obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_CROS_EC) += cros-ec-regulator.o
obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o
obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o
obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o
@ -27,6 +26,7 @@ obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o
obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o
@ -89,11 +89,13 @@ obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o
obj-$(CONFIG_REGULATOR_MP886X) += mp886x.o
obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
obj-$(CONFIG_REGULATOR_MT6315) += mt6315-regulator.o
obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o
obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
@ -118,6 +120,7 @@ obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o
obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o
obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o
obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o
obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o

View File

@ -1,724 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/regulator/ab3100.c
*
* Copyright (C) 2008-2009 ST-Ericsson AB
* Low-level control of the AB3100 IC Low Dropout (LDO)
* regulators, external regulator and buck converter
* Author: Mattias Wallin <mattias.wallin@stericsson.com>
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/mfd/ab3100.h>
#include <linux/mfd/abx500.h>
#include <linux/of.h>
#include <linux/regulator/of_regulator.h>
/* LDO registers and some handy masking definitions for AB3100 */
#define AB3100_LDO_A 0x40
#define AB3100_LDO_C 0x41
#define AB3100_LDO_D 0x42
#define AB3100_LDO_E 0x43
#define AB3100_LDO_E_SLEEP 0x44
#define AB3100_LDO_F 0x45
#define AB3100_LDO_G 0x46
#define AB3100_LDO_H 0x47
#define AB3100_LDO_H_SLEEP_MODE 0
#define AB3100_LDO_H_SLEEP_EN 2
#define AB3100_LDO_ON 4
#define AB3100_LDO_H_VSEL_AC 5
#define AB3100_LDO_K 0x48
#define AB3100_LDO_EXT 0x49
#define AB3100_BUCK 0x4A
#define AB3100_BUCK_SLEEP 0x4B
#define AB3100_REG_ON_MASK 0x10
/**
* struct ab3100_regulator
* A struct passed around the individual regulator functions
* @platform_device: platform device holding this regulator
* @dev: handle to the device
* @plfdata: AB3100 platform data passed in at probe time
* @regreg: regulator register number in the AB3100
*/
struct ab3100_regulator {
struct device *dev;
struct ab3100_platform_data *plfdata;
u8 regreg;
};
/* The order in which registers are initialized */
static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = {
AB3100_LDO_A,
AB3100_LDO_C,
AB3100_LDO_E,
AB3100_LDO_E_SLEEP,
AB3100_LDO_F,
AB3100_LDO_G,
AB3100_LDO_H,
AB3100_LDO_K,
AB3100_LDO_EXT,
AB3100_BUCK,
AB3100_BUCK_SLEEP,
AB3100_LDO_D,
};
/* Preset (hardware defined) voltages for these regulators */
#define LDO_A_VOLTAGE 2750000
#define LDO_C_VOLTAGE 2650000
#define LDO_D_VOLTAGE 2650000
static const unsigned int ldo_e_buck_typ_voltages[] = {
1800000,
1400000,
1300000,
1200000,
1100000,
1050000,
900000,
};
static const unsigned int ldo_f_typ_voltages[] = {
1800000,
1400000,
1300000,
1200000,
1100000,
1050000,
2500000,
2650000,
};
static const unsigned int ldo_g_typ_voltages[] = {
2850000,
2750000,
1800000,
1500000,
};
static const unsigned int ldo_h_typ_voltages[] = {
2750000,
1800000,
1500000,
1200000,
};
static const unsigned int ldo_k_typ_voltages[] = {
2750000,
1800000,
};
/* The regulator devices */
static struct ab3100_regulator
ab3100_regulators[AB3100_NUM_REGULATORS] = {
{
.regreg = AB3100_LDO_A,
},
{
.regreg = AB3100_LDO_C,
},
{
.regreg = AB3100_LDO_D,
},
{
.regreg = AB3100_LDO_E,
},
{
.regreg = AB3100_LDO_F,
},
{
.regreg = AB3100_LDO_G,
},
{
.regreg = AB3100_LDO_H,
},
{
.regreg = AB3100_LDO_K,
},
{
.regreg = AB3100_LDO_EXT,
/* No voltages for the external regulator */
},
{
.regreg = AB3100_BUCK,
},
};
/*
* General functions for enable, disable and is_enabled used for
* LDO: A,C,E,F,G,H,K,EXT and BUCK
*/
static int ab3100_enable_regulator(struct regulator_dev *reg)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
int err;
u8 regval;
err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
&regval);
if (err) {
dev_warn(&reg->dev, "failed to get regid %d value\n",
abreg->regreg);
return err;
}
/* The regulator is already on, no reason to go further */
if (regval & AB3100_REG_ON_MASK)
return 0;
regval |= AB3100_REG_ON_MASK;
err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg,
regval);
if (err) {
dev_warn(&reg->dev, "failed to set regid %d value\n",
abreg->regreg);
return err;
}
return 0;
}
static int ab3100_disable_regulator(struct regulator_dev *reg)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
int err;
u8 regval;
/*
* LDO D is a special regulator. When it is disabled, the entire
* system is shut down. So this is handled specially.
*/
pr_info("Called ab3100_disable_regulator\n");
if (abreg->regreg == AB3100_LDO_D) {
dev_info(&reg->dev, "disabling LDO D - shut down system\n");
/* Setting LDO D to 0x00 cuts the power to the SoC */
return abx500_set_register_interruptible(abreg->dev, 0,
AB3100_LDO_D, 0x00U);
}
/*
* All other regulators are handled here
*/
err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
&regval);
if (err) {
dev_err(&reg->dev, "unable to get register 0x%x\n",
abreg->regreg);
return err;
}
regval &= ~AB3100_REG_ON_MASK;
return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg,
regval);
}
static int ab3100_is_enabled_regulator(struct regulator_dev *reg)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
u8 regval;
int err;
err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
&regval);
if (err) {
dev_err(&reg->dev, "unable to get register 0x%x\n",
abreg->regreg);
return err;
}
return regval & AB3100_REG_ON_MASK;
}
static int ab3100_get_voltage_regulator(struct regulator_dev *reg)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
u8 regval;
int err;
/*
* For variable types, read out setting and index into
* supplied voltage list.
*/
err = abx500_get_register_interruptible(abreg->dev, 0,
abreg->regreg, &regval);
if (err) {
dev_warn(&reg->dev,
"failed to get regulator value in register %02x\n",
abreg->regreg);
return err;
}
/* The 3 highest bits index voltages */
regval &= 0xE0;
regval >>= 5;
if (regval >= reg->desc->n_voltages) {
dev_err(&reg->dev,
"regulator register %02x contains an illegal voltage setting\n",
abreg->regreg);
return -EINVAL;
}
return reg->desc->volt_table[regval];
}
static int ab3100_set_voltage_regulator_sel(struct regulator_dev *reg,
unsigned selector)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
u8 regval;
int err;
err = abx500_get_register_interruptible(abreg->dev, 0,
abreg->regreg, &regval);
if (err) {
dev_warn(&reg->dev,
"failed to get regulator register %02x\n",
abreg->regreg);
return err;
}
/* The highest three bits control the variable regulators */
regval &= ~0xE0;
regval |= (selector << 5);
err = abx500_set_register_interruptible(abreg->dev, 0,
abreg->regreg, regval);
if (err)
dev_warn(&reg->dev, "failed to set regulator register %02x\n",
abreg->regreg);
return err;
}
static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg,
int uV)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
u8 regval;
int err;
int bestindex;
u8 targetreg;
if (abreg->regreg == AB3100_LDO_E)
targetreg = AB3100_LDO_E_SLEEP;
else if (abreg->regreg == AB3100_BUCK)
targetreg = AB3100_BUCK_SLEEP;
else
return -EINVAL;
/* LDO E and BUCK have special suspend voltages you can set */
bestindex = regulator_map_voltage_iterate(reg, uV, uV);
err = abx500_get_register_interruptible(abreg->dev, 0,
targetreg, &regval);
if (err) {
dev_warn(&reg->dev,
"failed to get regulator register %02x\n",
targetreg);
return err;
}
/* The highest three bits control the variable regulators */
regval &= ~0xE0;
regval |= (bestindex << 5);
err = abx500_set_register_interruptible(abreg->dev, 0,
targetreg, regval);
if (err)
dev_warn(&reg->dev, "failed to set regulator register %02x\n",
abreg->regreg);
return err;
}
/*
* The external regulator can just define a fixed voltage.
*/
static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg)
{
struct ab3100_regulator *abreg = rdev_get_drvdata(reg);
if (abreg->plfdata)
return abreg->plfdata->external_voltage;
else
/* TODO: encode external voltage into device tree */
return 0;
}
static const struct regulator_ops regulator_ops_fixed = {
.enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator,
};
static const struct regulator_ops regulator_ops_variable = {
.enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator,
.get_voltage = ab3100_get_voltage_regulator,
.set_voltage_sel = ab3100_set_voltage_regulator_sel,
.list_voltage = regulator_list_voltage_table,
};
static const struct regulator_ops regulator_ops_variable_sleepable = {
.enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator,
.get_voltage = ab3100_get_voltage_regulator,
.set_voltage_sel = ab3100_set_voltage_regulator_sel,
.set_suspend_voltage = ab3100_set_suspend_voltage_regulator,
.list_voltage = regulator_list_voltage_table,
};
/*
* LDO EXT is an external regulator so it is really
* not possible to set any voltage locally here, AB3100
* is an on/off switch plain an simple. The external
* voltage is defined in the board set-up if any.
*/
static const struct regulator_ops regulator_ops_external = {
.enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator,
.get_voltage = ab3100_get_voltage_regulator_external,
};
static const struct regulator_desc
ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
{
.name = "LDO_A",
.id = AB3100_LDO_A,
.ops = &regulator_ops_fixed,
.n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.fixed_uV = LDO_A_VOLTAGE,
.enable_time = 200,
},
{
.name = "LDO_C",
.id = AB3100_LDO_C,
.ops = &regulator_ops_fixed,
.n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.fixed_uV = LDO_C_VOLTAGE,
.enable_time = 200,
},
{
.name = "LDO_D",
.id = AB3100_LDO_D,
.ops = &regulator_ops_fixed,
.n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.fixed_uV = LDO_D_VOLTAGE,
.enable_time = 200,
},
{
.name = "LDO_E",
.id = AB3100_LDO_E,
.ops = &regulator_ops_variable_sleepable,
.n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
.volt_table = ldo_e_buck_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 200,
},
{
.name = "LDO_F",
.id = AB3100_LDO_F,
.ops = &regulator_ops_variable,
.n_voltages = ARRAY_SIZE(ldo_f_typ_voltages),
.volt_table = ldo_f_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 600,
},
{
.name = "LDO_G",
.id = AB3100_LDO_G,
.ops = &regulator_ops_variable,
.n_voltages = ARRAY_SIZE(ldo_g_typ_voltages),
.volt_table = ldo_g_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 400,
},
{
.name = "LDO_H",
.id = AB3100_LDO_H,
.ops = &regulator_ops_variable,
.n_voltages = ARRAY_SIZE(ldo_h_typ_voltages),
.volt_table = ldo_h_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 200,
},
{
.name = "LDO_K",
.id = AB3100_LDO_K,
.ops = &regulator_ops_variable,
.n_voltages = ARRAY_SIZE(ldo_k_typ_voltages),
.volt_table = ldo_k_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 200,
},
{
.name = "LDO_EXT",
.id = AB3100_LDO_EXT,
.ops = &regulator_ops_external,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
},
{
.name = "BUCK",
.id = AB3100_BUCK,
.ops = &regulator_ops_variable_sleepable,
.n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
.volt_table = ldo_e_buck_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 1000,
},
};
static int ab3100_regulator_register(struct platform_device *pdev,
struct ab3100_platform_data *plfdata,
struct regulator_init_data *init_data,
struct device_node *np,
unsigned long id)
{
const struct regulator_desc *desc;
struct ab3100_regulator *reg;
struct regulator_dev *rdev;
struct regulator_config config = { };
int err, i;
for (i = 0; i < AB3100_NUM_REGULATORS; i++) {
desc = &ab3100_regulator_desc[i];
if (desc->id == id)
break;
}
if (desc->id != id)
return -ENODEV;
/* Same index used for this array */
reg = &ab3100_regulators[i];
/*
* Initialize per-regulator struct.
* Inherit platform data, this comes down from the
* i2c boarddata, from the machine. So if you want to
* see what it looks like for a certain machine, go
* into the machine I2C setup.
*/
reg->dev = &pdev->dev;
if (plfdata) {
reg->plfdata = plfdata;
config.init_data = &plfdata->reg_constraints[i];
} else if (np) {
config.of_node = np;
config.init_data = init_data;
}
config.dev = &pdev->dev;
config.driver_data = reg;
rdev = devm_regulator_register(&pdev->dev, desc, &config);
if (IS_ERR(rdev)) {
err = PTR_ERR(rdev);
dev_err(&pdev->dev,
"%s: failed to register regulator %s err %d\n",
__func__, desc->name,
err);
return err;
}
return 0;
}
static struct of_regulator_match ab3100_regulator_matches[] = {
{ .name = "ab3100_ldo_a", .driver_data = (void *) AB3100_LDO_A, },
{ .name = "ab3100_ldo_c", .driver_data = (void *) AB3100_LDO_C, },
{ .name = "ab3100_ldo_d", .driver_data = (void *) AB3100_LDO_D, },
{ .name = "ab3100_ldo_e", .driver_data = (void *) AB3100_LDO_E, },
{ .name = "ab3100_ldo_f", .driver_data = (void *) AB3100_LDO_F },
{ .name = "ab3100_ldo_g", .driver_data = (void *) AB3100_LDO_G },
{ .name = "ab3100_ldo_h", .driver_data = (void *) AB3100_LDO_H },
{ .name = "ab3100_ldo_k", .driver_data = (void *) AB3100_LDO_K },
{ .name = "ab3100_ext", .driver_data = (void *) AB3100_LDO_EXT },
{ .name = "ab3100_buck", .driver_data = (void *) AB3100_BUCK },
};
/*
* Initial settings of ab3100 registers.
* Common for below LDO regulator settings are that
* bit 7-5 controls voltage. Bit 4 turns regulator ON(1) or OFF(0).
* Bit 3-2 controls sleep enable and bit 1-0 controls sleep mode.
*/
/* LDO_A 0x16: 2.75V, ON, SLEEP_A, SLEEP OFF GND */
#define LDO_A_SETTING 0x16
/* LDO_C 0x10: 2.65V, ON, SLEEP_A or B, SLEEP full power */
#define LDO_C_SETTING 0x10
/* LDO_D 0x10: 2.65V, ON, sleep mode not used */
#define LDO_D_SETTING 0x10
/* LDO_E 0x10: 1.8V, ON, SLEEP_A or B, SLEEP full power */
#define LDO_E_SETTING 0x10
/* LDO_E SLEEP 0x00: 1.8V, not used, SLEEP_A or B, not used */
#define LDO_E_SLEEP_SETTING 0x00
/* LDO_F 0xD0: 2.5V, ON, SLEEP_A or B, SLEEP full power */
#define LDO_F_SETTING 0xD0
/* LDO_G 0x00: 2.85V, OFF, SLEEP_A or B, SLEEP full power */
#define LDO_G_SETTING 0x00
/* LDO_H 0x18: 2.75V, ON, SLEEP_B, SLEEP full power */
#define LDO_H_SETTING 0x18
/* LDO_K 0x00: 2.75V, OFF, SLEEP_A or B, SLEEP full power */
#define LDO_K_SETTING 0x00
/* LDO_EXT 0x00: Voltage not set, OFF, not used, not used */
#define LDO_EXT_SETTING 0x00
/* BUCK 0x7D: 1.2V, ON, SLEEP_A and B, SLEEP low power */
#define BUCK_SETTING 0x7D
/* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */
#define BUCK_SLEEP_SETTING 0xAC
static const u8 ab3100_reg_initvals[] = {
LDO_A_SETTING,
LDO_C_SETTING,
LDO_E_SETTING,
LDO_E_SLEEP_SETTING,
LDO_F_SETTING,
LDO_G_SETTING,
LDO_H_SETTING,
LDO_K_SETTING,
LDO_EXT_SETTING,
BUCK_SETTING,
BUCK_SLEEP_SETTING,
LDO_D_SETTING,
};
static int
ab3100_regulator_of_probe(struct platform_device *pdev, struct device_node *np)
{
int err, i;
/*
* Set up the regulator registers, as was previously done with
* platform data.
*/
/* Set up regulators */
for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
err = abx500_set_register_interruptible(&pdev->dev, 0,
ab3100_reg_init_order[i],
ab3100_reg_initvals[i]);
if (err) {
dev_err(&pdev->dev, "regulator initialization failed with error %d\n",
err);
return err;
}
}
for (i = 0; i < ARRAY_SIZE(ab3100_regulator_matches); i++) {
err = ab3100_regulator_register(
pdev, NULL, ab3100_regulator_matches[i].init_data,
ab3100_regulator_matches[i].of_node,
(unsigned long)ab3100_regulator_matches[i].driver_data);
if (err)
return err;
}
return 0;
}
static int ab3100_regulators_probe(struct platform_device *pdev)
{
struct ab3100_platform_data *plfdata = dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
int err = 0;
u8 data;
int i;
/* Check chip state */
err = abx500_get_register_interruptible(&pdev->dev, 0,
AB3100_LDO_D, &data);
if (err) {
dev_err(&pdev->dev, "could not read initial status of LDO_D\n");
return err;
}
if (data & 0x10)
dev_notice(&pdev->dev,
"chip is already in active mode (Warm start)\n");
else
dev_notice(&pdev->dev,
"chip is in inactive mode (Cold start)\n");
if (np) {
err = of_regulator_match(&pdev->dev, np,
ab3100_regulator_matches,
ARRAY_SIZE(ab3100_regulator_matches));
if (err < 0) {
dev_err(&pdev->dev,
"Error parsing regulator init data: %d\n", err);
return err;
}
return ab3100_regulator_of_probe(pdev, np);
}
/* Set up regulators */
for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
err = abx500_set_register_interruptible(&pdev->dev, 0,
ab3100_reg_init_order[i],
plfdata->reg_initvals[i]);
if (err) {
dev_err(&pdev->dev, "regulator initialization failed with error %d\n",
err);
return err;
}
}
/* Register the regulators */
for (i = 0; i < AB3100_NUM_REGULATORS; i++) {
const struct regulator_desc *desc = &ab3100_regulator_desc[i];
err = ab3100_regulator_register(pdev, plfdata, NULL, NULL,
desc->id);
if (err)
return err;
}
return 0;
}
static struct platform_driver ab3100_regulators_driver = {
.driver = {
.name = "ab3100-regulators",
},
.probe = ab3100_regulators_probe,
};
static __init int ab3100_regulators_init(void)
{
return platform_driver_register(&ab3100_regulators_driver);
}
static __exit void ab3100_regulators_exit(void)
{
platform_driver_unregister(&ab3100_regulators_driver);
}
subsys_initcall(ab3100_regulators_init);
module_exit(ab3100_regulators_exit);
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
MODULE_DESCRIPTION("AB3100 Regulator driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ab3100-regulators");

View File

@ -22,403 +22,17 @@
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/regulator/ab8500.h>
static struct regulator_consumer_supply ab8500_vaux1_consumers[] = {
/* Main display, u8500 R3 uib */
REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
/* Main display, u8500 uib and ST uib */
REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"),
/* Secondary display, ST uib */
REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"),
/* SFH7741 proximity sensor */
REGULATOR_SUPPLY("vcc", "gpio-keys.0"),
/* BH1780GLS ambient light sensor */
REGULATOR_SUPPLY("vcc", "2-0029"),
/* lsm303dlh accelerometer */
REGULATOR_SUPPLY("vdd", "2-0018"),
/* lsm303dlhc accelerometer */
REGULATOR_SUPPLY("vdd", "2-0019"),
/* lsm303dlh magnetometer */
REGULATOR_SUPPLY("vdd", "2-001e"),
/* Rohm BU21013 Touchscreen devices */
REGULATOR_SUPPLY("avdd", "3-005c"),
REGULATOR_SUPPLY("avdd", "3-005d"),
/* Synaptics RMI4 Touchscreen device */
REGULATOR_SUPPLY("vdd", "3-004b"),
/* L3G4200D Gyroscope device */
REGULATOR_SUPPLY("vdd", "2-0068"),
/* Ambient light sensor device */
REGULATOR_SUPPLY("vdd", "3-0029"),
/* Pressure sensor device */
REGULATOR_SUPPLY("vdd", "2-005c"),
/* Cypress TrueTouch Touchscreen device */
REGULATOR_SUPPLY("vcpin", "spi8.0"),
/* Camera device */
REGULATOR_SUPPLY("vaux12v5", "mmio_camera"),
/* AB8500 external regulators */
enum ab8500_ext_regulator_id {
AB8500_EXT_SUPPLY1,
AB8500_EXT_SUPPLY2,
AB8500_EXT_SUPPLY3,
AB8500_NUM_EXT_REGULATORS,
};
static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
/* On-board eMMC power */
REGULATOR_SUPPLY("vmmc", "sdi4"),
/* AB8500 audio codec */
REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"),
/* AB8500 accessory detect 1 */
REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"),
/* AB8500 Tv-out device */
REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"),
/* AV8100 HDMI device */
REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"),
};
static struct regulator_consumer_supply ab8500_vaux3_consumers[] = {
REGULATOR_SUPPLY("v-SD-STM", "stm"),
/* External MMC slot power */
REGULATOR_SUPPLY("vmmc", "sdi0"),
};
static struct regulator_consumer_supply ab8500_vtvout_consumers[] = {
/* TV-out DENC supply */
REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"),
/* Internal general-purpose ADC */
REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"),
/* ADC for charger */
REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"),
/* AB8500 Tv-out device */
REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"),
};
static struct regulator_consumer_supply ab8500_vaud_consumers[] = {
/* AB8500 audio-codec main supply */
REGULATOR_SUPPLY("vaud", "ab8500-codec.0"),
};
static struct regulator_consumer_supply ab8500_vamic1_consumers[] = {
/* AB8500 audio-codec Mic1 supply */
REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"),
};
static struct regulator_consumer_supply ab8500_vamic2_consumers[] = {
/* AB8500 audio-codec Mic2 supply */
REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"),
};
static struct regulator_consumer_supply ab8500_vdmic_consumers[] = {
/* AB8500 audio-codec DMic supply */
REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"),
};
static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
/* SoC core supply, no device */
REGULATOR_SUPPLY("v-intcore", NULL),
/* USB Transceiver */
REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"),
/* Handled by abx500 clk driver */
REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"),
};
static struct regulator_consumer_supply ab8500_vana_consumers[] = {
/* DB8500 DSI */
REGULATOR_SUPPLY("vdddsi1v2", "mcde"),
REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"),
REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"),
REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"),
REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"),
REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"),
/* DB8500 CSI */
REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"),
};
/* ab8500 regulator register initialization */
static struct ab8500_regulator_reg_init ab8500_reg_init[] = {
/*
* VanaRequestCtrl = HP/LP depending on VxRequest
* VextSupply1RequestCtrl = HP/LP depending on VxRequest
*/
INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00),
/*
* VextSupply2RequestCtrl = HP/LP depending on VxRequest
* VextSupply3RequestCtrl = HP/LP depending on VxRequest
* Vaux1RequestCtrl = HP/LP depending on VxRequest
* Vaux2RequestCtrl = HP/LP depending on VxRequest
*/
INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00),
/*
* Vaux3RequestCtrl = HP/LP depending on VxRequest
* SwHPReq = Control through SWValid disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00),
/*
* VanaSysClkReq1HPValid = disabled
* Vaux1SysClkReq1HPValid = disabled
* Vaux2SysClkReq1HPValid = disabled
* Vaux3SysClkReq1HPValid = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00),
/*
* VextSupply1SysClkReq1HPValid = disabled
* VextSupply2SysClkReq1HPValid = disabled
* VextSupply3SysClkReq1HPValid = SysClkReq1 controlled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40),
/*
* VanaHwHPReq1Valid = disabled
* Vaux1HwHPreq1Valid = disabled
* Vaux2HwHPReq1Valid = disabled
* Vaux3HwHPReqValid = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00),
/*
* VextSupply1HwHPReq1Valid = disabled
* VextSupply2HwHPReq1Valid = disabled
* VextSupply3HwHPReq1Valid = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00),
/*
* VanaHwHPReq2Valid = disabled
* Vaux1HwHPReq2Valid = disabled
* Vaux2HwHPReq2Valid = disabled
* Vaux3HwHPReq2Valid = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00),
/*
* VextSupply1HwHPReq2Valid = disabled
* VextSupply2HwHPReq2Valid = disabled
* VextSupply3HwHPReq2Valid = HWReq2 controlled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04),
/*
* VanaSwHPReqValid = disabled
* Vaux1SwHPReqValid = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00),
/*
* Vaux2SwHPReqValid = disabled
* Vaux3SwHPReqValid = disabled
* VextSupply1SwHPReqValid = disabled
* VextSupply2SwHPReqValid = disabled
* VextSupply3SwHPReqValid = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00),
/*
* SysClkReq2Valid1 = SysClkReq2 controlled
* SysClkReq3Valid1 = disabled
* SysClkReq4Valid1 = SysClkReq4 controlled
* SysClkReq5Valid1 = disabled
* SysClkReq6Valid1 = SysClkReq6 controlled
* SysClkReq7Valid1 = disabled
* SysClkReq8Valid1 = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a),
/*
* SysClkReq2Valid2 = disabled
* SysClkReq3Valid2 = disabled
* SysClkReq4Valid2 = disabled
* SysClkReq5Valid2 = disabled
* SysClkReq6Valid2 = SysClkReq6 controlled
* SysClkReq7Valid2 = disabled
* SysClkReq8Valid2 = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20),
/*
* VTVoutEna = disabled
* Vintcore12Ena = disabled
* Vintcore12Sel = 1.25 V
* Vintcore12LP = inactive (HP)
* VTVoutLP = inactive (HP)
*/
INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10),
/*
* VaudioEna = disabled
* VdmicEna = disabled
* Vamic1Ena = disabled
* Vamic2Ena = disabled
*/
INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00),
/*
* Vamic1_dzout = high-Z when Vamic1 is disabled
* Vamic2_dzout = high-Z when Vamic2 is disabled
*/
INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00),
/*
* VPll = Hw controlled (NOTE! PRCMU bits)
* VanaRegu = force off
*/
INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02),
/*
* VrefDDREna = disabled
* VrefDDRSleepMode = inactive (no pulldown)
*/
INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00),
/*
* VextSupply1Regu = force LP
* VextSupply2Regu = force OFF
* VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP)
* ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0
* ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0
*/
INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13),
/*
* Vaux1Regu = force HP
* Vaux2Regu = force off
*/
INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01),
/*
* Vaux3Regu = force off
*/
INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00),
/*
* Vaux1Sel = 2.8 V
*/
INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C),
/*
* Vaux2Sel = 2.9 V
*/
INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d),
/*
* Vaux3Sel = 2.91 V
*/
INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07),
/*
* VextSupply12LP = disabled (no LP)
*/
INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00),
/*
* Vaux1Disch = short discharge time
* Vaux2Disch = short discharge time
* Vaux3Disch = short discharge time
* Vintcore12Disch = short discharge time
* VTVoutDisch = short discharge time
* VaudioDisch = short discharge time
*/
INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00),
/*
* VanaDisch = short discharge time
* VdmicPullDownEna = pulldown disabled when Vdmic is disabled
* VdmicDisch = short discharge time
*/
INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00),
};
/* AB8500 regulators */
static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
/* supplies to the display/camera */
[AB8500_LDO_AUX1] = {
.supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-DISPLAY",
.min_uV = 2800000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS,
.boot_on = 1, /* display is on at boot */
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers),
.consumer_supplies = ab8500_vaux1_consumers,
},
/* supplies to the on-board eMMC */
[AB8500_LDO_AUX2] = {
.supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-eMMC1",
.min_uV = 1100000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS |
REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_NORMAL |
REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers),
.consumer_supplies = ab8500_vaux2_consumers,
},
/* supply for VAUX3, supplies to SDcard slots */
[AB8500_LDO_AUX3] = {
.supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-MMC-SD",
.min_uV = 1100000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS |
REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_NORMAL |
REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers),
.consumer_supplies = ab8500_vaux3_consumers,
},
/* supply for tvout, gpadc, TVOUT LDO */
[AB8500_LDO_TVOUT] = {
.constraints = {
.name = "V-TVOUT",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers),
.consumer_supplies = ab8500_vtvout_consumers,
},
/* supply for ab8500-vaudio, VAUDIO LDO */
[AB8500_LDO_AUDIO] = {
.constraints = {
.name = "V-AUD",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers),
.consumer_supplies = ab8500_vaud_consumers,
},
/* supply for v-anamic1 VAMic1-LDO */
[AB8500_LDO_ANAMIC1] = {
.constraints = {
.name = "V-AMIC1",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers),
.consumer_supplies = ab8500_vamic1_consumers,
},
/* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */
[AB8500_LDO_ANAMIC2] = {
.constraints = {
.name = "V-AMIC2",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers),
.consumer_supplies = ab8500_vamic2_consumers,
},
/* supply for v-dmic, VDMIC LDO */
[AB8500_LDO_DMIC] = {
.constraints = {
.name = "V-DMIC",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers),
.consumer_supplies = ab8500_vdmic_consumers,
},
/* supply for v-intcore12, VINTCORE12 LDO */
[AB8500_LDO_INTCORE] = {
.constraints = {
.name = "V-INTCORE",
.min_uV = 1250000,
.max_uV = 1350000,
.input_uV = 1800000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS |
REGULATOR_CHANGE_MODE |
REGULATOR_CHANGE_DRMS,
.valid_modes_mask = REGULATOR_MODE_NORMAL |
REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers),
.consumer_supplies = ab8500_vintcore_consumers,
},
/* supply for U8500 CSI-DSI, VANA LDO */
[AB8500_LDO_ANA] = {
.constraints = {
.name = "V-CSI-DSI",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers),
.consumer_supplies = ab8500_vana_consumers,
},
struct ab8500_ext_regulator_cfg {
bool hwreq; /* requires hw mode or high power mode */
};
/* supply for VextSupply3 */
@ -465,15 +79,6 @@ static struct regulator_init_data ab8500_ext_regulators[] = {
},
};
static struct ab8500_regulator_platform_data ab8500_regulator_plat_data = {
.reg_init = ab8500_reg_init,
.num_reg_init = ARRAY_SIZE(ab8500_reg_init),
.regulator = ab8500_regulators,
.num_regulator = ARRAY_SIZE(ab8500_regulators),
.ext_regulator = ab8500_ext_regulators,
.num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators),
};
/**
* struct ab8500_ext_regulator_info - ab8500 regulator information
* @dev: device pointer
@ -788,7 +393,6 @@ static struct ab8500_ext_regulator_info
static int ab8500_ext_regulator_probe(struct platform_device *pdev)
{
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
struct ab8500_regulator_platform_data *pdata = &ab8500_regulator_plat_data;
struct regulator_config config = { };
struct regulator_dev *rdev;
int i;
@ -798,12 +402,6 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev)
return -EINVAL;
}
/* make sure the platform data has the correct size */
if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) {
dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
return -EINVAL;
}
/* check for AB8500 2.x */
if (is_ab8500_2p0_or_earlier(ab8500)) {
struct ab8500_ext_regulator_info *info;
@ -823,11 +421,11 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev)
info = &ab8500_ext_regulator_info[i];
info->dev = &pdev->dev;
info->cfg = (struct ab8500_ext_regulator_cfg *)
pdata->ext_regulator[i].driver_data;
ab8500_ext_regulators[i].driver_data;
config.dev = &pdev->dev;
config.driver_data = info;
config.init_data = &pdata->ext_regulator[i];
config.init_data = &ab8500_ext_regulators[i];
/* register regulator with framework */
rdev = devm_regulator_register(&pdev->dev, &info->desc,

View File

@ -25,9 +25,123 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
#include <linux/slab.h>
/* AB8500 regulators */
enum ab8500_regulator_id {
AB8500_LDO_AUX1,
AB8500_LDO_AUX2,
AB8500_LDO_AUX3,
AB8500_LDO_INTCORE,
AB8500_LDO_TVOUT,
AB8500_LDO_AUDIO,
AB8500_LDO_ANAMIC1,
AB8500_LDO_ANAMIC2,
AB8500_LDO_DMIC,
AB8500_LDO_ANA,
AB8500_NUM_REGULATORS,
};
/* AB8505 regulators */
enum ab8505_regulator_id {
AB8505_LDO_AUX1,
AB8505_LDO_AUX2,
AB8505_LDO_AUX3,
AB8505_LDO_AUX4,
AB8505_LDO_AUX5,
AB8505_LDO_AUX6,
AB8505_LDO_INTCORE,
AB8505_LDO_ADC,
AB8505_LDO_AUDIO,
AB8505_LDO_ANAMIC1,
AB8505_LDO_ANAMIC2,
AB8505_LDO_AUX8,
AB8505_LDO_ANA,
AB8505_NUM_REGULATORS,
};
/* AB8500 registers */
enum ab8500_regulator_reg {
AB8500_REGUREQUESTCTRL2,
AB8500_REGUREQUESTCTRL3,
AB8500_REGUREQUESTCTRL4,
AB8500_REGUSYSCLKREQ1HPVALID1,
AB8500_REGUSYSCLKREQ1HPVALID2,
AB8500_REGUHWHPREQ1VALID1,
AB8500_REGUHWHPREQ1VALID2,
AB8500_REGUHWHPREQ2VALID1,
AB8500_REGUHWHPREQ2VALID2,
AB8500_REGUSWHPREQVALID1,
AB8500_REGUSWHPREQVALID2,
AB8500_REGUSYSCLKREQVALID1,
AB8500_REGUSYSCLKREQVALID2,
AB8500_REGUMISC1,
AB8500_VAUDIOSUPPLY,
AB8500_REGUCTRL1VAMIC,
AB8500_VPLLVANAREGU,
AB8500_VREFDDR,
AB8500_EXTSUPPLYREGU,
AB8500_VAUX12REGU,
AB8500_VRF1VAUX3REGU,
AB8500_VAUX1SEL,
AB8500_VAUX2SEL,
AB8500_VRF1VAUX3SEL,
AB8500_REGUCTRL2SPARE,
AB8500_REGUCTRLDISCH,
AB8500_REGUCTRLDISCH2,
AB8500_NUM_REGULATOR_REGISTERS,
};
/* AB8505 registers */
enum ab8505_regulator_reg {
AB8505_REGUREQUESTCTRL1,
AB8505_REGUREQUESTCTRL2,
AB8505_REGUREQUESTCTRL3,
AB8505_REGUREQUESTCTRL4,
AB8505_REGUSYSCLKREQ1HPVALID1,
AB8505_REGUSYSCLKREQ1HPVALID2,
AB8505_REGUHWHPREQ1VALID1,
AB8505_REGUHWHPREQ1VALID2,
AB8505_REGUHWHPREQ2VALID1,
AB8505_REGUHWHPREQ2VALID2,
AB8505_REGUSWHPREQVALID1,
AB8505_REGUSWHPREQVALID2,
AB8505_REGUSYSCLKREQVALID1,
AB8505_REGUSYSCLKREQVALID2,
AB8505_REGUVAUX4REQVALID,
AB8505_REGUMISC1,
AB8505_VAUDIOSUPPLY,
AB8505_REGUCTRL1VAMIC,
AB8505_VSMPSAREGU,
AB8505_VSMPSBREGU,
AB8505_VSAFEREGU, /* NOTE! PRCMU register */
AB8505_VPLLVANAREGU,
AB8505_EXTSUPPLYREGU,
AB8505_VAUX12REGU,
AB8505_VRF1VAUX3REGU,
AB8505_VSMPSASEL1,
AB8505_VSMPSASEL2,
AB8505_VSMPSASEL3,
AB8505_VSMPSBSEL1,
AB8505_VSMPSBSEL2,
AB8505_VSMPSBSEL3,
AB8505_VSAFESEL1, /* NOTE! PRCMU register */
AB8505_VSAFESEL2, /* NOTE! PRCMU register */
AB8505_VSAFESEL3, /* NOTE! PRCMU register */
AB8505_VAUX1SEL,
AB8505_VAUX2SEL,
AB8505_VRF1VAUX3SEL,
AB8505_VAUX4REQCTRL,
AB8505_VAUX4REGU,
AB8505_VAUX4SEL,
AB8505_REGUCTRLDISCH,
AB8505_REGUCTRLDISCH2,
AB8505_REGUCTRLDISCH3,
AB8505_CTRLVAUX5,
AB8505_CTRLVAUX6,
AB8505_NUM_REGULATOR_REGISTERS,
};
/**
* struct ab8500_shared_mode - is used when mode is shared between
* two regulators.

View File

@ -0,0 +1,539 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Regulator driver for ATC260x PMICs
//
// Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
// Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
#include <linux/mfd/atc260x/core.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
struct atc260x_regulator_data {
int voltage_time_dcdc;
int voltage_time_ldo;
};
static const struct linear_range atc2603c_dcdc_voltage_ranges[] = {
REGULATOR_LINEAR_RANGE(1300000, 0, 13, 50000),
REGULATOR_LINEAR_RANGE(1950000, 14, 15, 100000),
};
static const struct linear_range atc2609a_dcdc_voltage_ranges[] = {
REGULATOR_LINEAR_RANGE(600000, 0, 127, 6250),
REGULATOR_LINEAR_RANGE(1400000, 128, 232, 25000),
};
static const struct linear_range atc2609a_ldo_voltage_ranges0[] = {
REGULATOR_LINEAR_RANGE(700000, 0, 15, 100000),
REGULATOR_LINEAR_RANGE(2100000, 16, 28, 100000),
};
static const struct linear_range atc2609a_ldo_voltage_ranges1[] = {
REGULATOR_LINEAR_RANGE(850000, 0, 15, 100000),
REGULATOR_LINEAR_RANGE(2100000, 16, 27, 100000),
};
static const unsigned int atc260x_ldo_voltage_range_sel[] = {
0x0, 0x1,
};
static int atc260x_dcdc_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_selector,
unsigned int new_selector)
{
struct atc260x_regulator_data *data = rdev_get_drvdata(rdev);
if (new_selector > old_selector)
return data->voltage_time_dcdc;
return 0;
}
static int atc260x_ldo_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_selector,
unsigned int new_selector)
{
struct atc260x_regulator_data *data = rdev_get_drvdata(rdev);
if (new_selector > old_selector)
return data->voltage_time_ldo;
return 0;
}
static const struct regulator_ops atc260x_dcdc_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel,
};
static const struct regulator_ops atc260x_ldo_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,
};
static const struct regulator_ops atc260x_ldo_bypass_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,
.set_bypass = regulator_set_bypass_regmap,
.get_bypass = regulator_get_bypass_regmap,
};
static const struct regulator_ops atc260x_ldo_bypass_discharge_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,
.set_bypass = regulator_set_bypass_regmap,
.get_bypass = regulator_get_bypass_regmap,
.set_active_discharge = regulator_set_active_discharge_regmap,
};
static const struct regulator_ops atc260x_dcdc_range_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 = atc260x_dcdc_set_voltage_time_sel,
};
static const struct regulator_ops atc260x_ldo_range_pick_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_pickable_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_pickable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,
};
static const struct regulator_ops atc260x_dcdc_fixed_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = atc260x_dcdc_set_voltage_time_sel,
};
static const struct regulator_ops atc260x_ldo_fixed_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = atc260x_ldo_set_voltage_time_sel,
};
static const struct regulator_ops atc260x_no_ops = {
};
/*
* Note LDO8 is not documented in datasheet (v2.4), but supported
* in the vendor's driver implementation (xapp-le-kernel).
*/
enum atc2603c_reg_ids {
ATC2603C_ID_DCDC1,
ATC2603C_ID_DCDC2,
ATC2603C_ID_DCDC3,
ATC2603C_ID_LDO1,
ATC2603C_ID_LDO2,
ATC2603C_ID_LDO3,
ATC2603C_ID_LDO5,
ATC2603C_ID_LDO6,
ATC2603C_ID_LDO7,
ATC2603C_ID_LDO8,
ATC2603C_ID_LDO11,
ATC2603C_ID_LDO12,
ATC2603C_ID_SWITCHLDO1,
ATC2603C_ID_MAX,
};
#define atc2603c_reg_desc_dcdc(num, min, step, n_volt, vsel_h, vsel_l) { \
.name = "DCDC"#num, \
.supply_name = "dcdc"#num, \
.of_match = of_match_ptr("dcdc"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_DCDC##num, \
.ops = &atc260x_dcdc_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = min, \
.uV_step = step, \
.n_voltages = n_volt, \
.vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \
.vsel_mask = GENMASK(vsel_h, vsel_l), \
.enable_reg = ATC2603C_PMU_DC##num##_CTL0, \
.enable_mask = BIT(15), \
.enable_time = 800, \
.owner = THIS_MODULE, \
}
#define atc2603c_reg_desc_dcdc_range(num, vsel_h, vsel_l) { \
.name = "DCDC"#num, \
.supply_name = "dcdc"#num, \
.of_match = of_match_ptr("dcdc"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_DCDC##num, \
.ops = &atc260x_dcdc_range_ops, \
.type = REGULATOR_VOLTAGE, \
.n_voltages = 16, \
.linear_ranges = atc2603c_dcdc_voltage_ranges, \
.n_linear_ranges = ARRAY_SIZE(atc2603c_dcdc_voltage_ranges), \
.vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \
.vsel_mask = GENMASK(vsel_h, vsel_l), \
.enable_reg = ATC2603C_PMU_DC##num##_CTL0, \
.enable_mask = BIT(15), \
.enable_time = 800, \
.owner = THIS_MODULE, \
}
#define atc2603c_reg_desc_dcdc_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \
.name = "DCDC"#num, \
.supply_name = "dcdc"#num, \
.of_match = of_match_ptr("dcdc"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_DCDC##num, \
.ops = &atc260x_dcdc_fixed_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = min, \
.uV_step = step, \
.n_voltages = n_volt, \
.vsel_reg = ATC2603C_PMU_DC##num##_CTL0, \
.vsel_mask = GENMASK(vsel_h, vsel_l), \
.enable_time = 800, \
.owner = THIS_MODULE, \
}
#define atc2603c_reg_desc_ldo(num, min, step, n_volt, vsel_h, vsel_l) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_LDO##num, \
.ops = &atc260x_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = min, \
.uV_step = step, \
.n_voltages = n_volt, \
.vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \
.vsel_mask = GENMASK(vsel_h, vsel_l), \
.enable_reg = ATC2603C_PMU_LDO##num##_CTL, \
.enable_mask = BIT(0), \
.enable_time = 2000, \
.owner = THIS_MODULE, \
}
#define atc2603c_reg_desc_ldo_fixed(num, min, step, n_volt, vsel_h, vsel_l) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_LDO##num, \
.ops = &atc260x_ldo_fixed_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = min, \
.uV_step = step, \
.n_voltages = n_volt, \
.vsel_reg = ATC2603C_PMU_LDO##num##_CTL, \
.vsel_mask = GENMASK(vsel_h, vsel_l), \
.enable_time = 2000, \
.owner = THIS_MODULE, \
}
#define atc2603c_reg_desc_ldo_noops(num, vfixed) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_LDO##num, \
.ops = &atc260x_no_ops, \
.type = REGULATOR_VOLTAGE, \
.fixed_uV = vfixed, \
.n_voltages = 1, \
.owner = THIS_MODULE, \
}
#define atc2603c_reg_desc_ldo_switch(num, min, step, n_volt, vsel_h, vsel_l) { \
.name = "SWITCHLDO"#num, \
.supply_name = "switchldo"#num, \
.of_match = of_match_ptr("switchldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2603C_ID_SWITCHLDO##num, \
.ops = &atc260x_ldo_bypass_discharge_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = min, \
.uV_step = step, \
.n_voltages = n_volt, \
.vsel_reg = ATC2603C_PMU_SWITCH_CTL, \
.vsel_mask = GENMASK(vsel_h, vsel_l), \
.enable_reg = ATC2603C_PMU_SWITCH_CTL, \
.enable_mask = BIT(15), \
.enable_is_inverted = true, \
.enable_time = 2000, \
.bypass_reg = ATC2603C_PMU_SWITCH_CTL, \
.bypass_mask = BIT(5), \
.active_discharge_reg = ATC2603C_PMU_SWITCH_CTL, \
.active_discharge_mask = BIT(1), \
.owner = THIS_MODULE, \
}
static const struct regulator_desc atc2603c_reg[] = {
atc2603c_reg_desc_dcdc_fixed(1, 700000, 25000, 29, 11, 7),
atc2603c_reg_desc_dcdc_range(2, 12, 8),
atc2603c_reg_desc_dcdc_fixed(3, 2600000, 100000, 8, 11, 9),
atc2603c_reg_desc_ldo_fixed(1, 2600000, 100000, 8, 15, 13),
atc2603c_reg_desc_ldo_fixed(2, 2600000, 100000, 8, 15, 13),
atc2603c_reg_desc_ldo_fixed(3, 1500000, 100000, 6, 15, 13),
atc2603c_reg_desc_ldo(5, 2600000, 100000, 8, 15, 13),
atc2603c_reg_desc_ldo_fixed(6, 700000, 25000, 29, 15, 11),
atc2603c_reg_desc_ldo(7, 1500000, 100000, 6, 15, 13),
atc2603c_reg_desc_ldo(8, 2300000, 100000, 11, 15, 12),
atc2603c_reg_desc_ldo_fixed(11, 2600000, 100000, 8, 15, 13),
atc2603c_reg_desc_ldo_noops(12, 1800000),
atc2603c_reg_desc_ldo_switch(1, 3000000, 100000, 4, 4, 3),
};
static const struct regulator_desc atc2603c_reg_dcdc2_ver_b =
atc2603c_reg_desc_dcdc(2, 1000000, 50000, 18, 12, 8);
enum atc2609a_reg_ids {
ATC2609A_ID_DCDC0,
ATC2609A_ID_DCDC1,
ATC2609A_ID_DCDC2,
ATC2609A_ID_DCDC3,
ATC2609A_ID_DCDC4,
ATC2609A_ID_LDO0,
ATC2609A_ID_LDO1,
ATC2609A_ID_LDO2,
ATC2609A_ID_LDO3,
ATC2609A_ID_LDO4,
ATC2609A_ID_LDO5,
ATC2609A_ID_LDO6,
ATC2609A_ID_LDO7,
ATC2609A_ID_LDO8,
ATC2609A_ID_LDO9,
ATC2609A_ID_MAX,
};
#define atc2609a_reg_desc_dcdc(num, en_bit) { \
.name = "DCDC"#num, \
.supply_name = "dcdc"#num, \
.of_match = of_match_ptr("dcdc"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2609A_ID_DCDC##num, \
.ops = &atc260x_dcdc_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = 600000, \
.uV_step = 6250, \
.n_voltages = 256, \
.vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \
.vsel_mask = GENMASK(15, 8), \
.enable_reg = ATC2609A_PMU_DC_OSC, \
.enable_mask = BIT(en_bit), \
.enable_time = 800, \
.owner = THIS_MODULE, \
}
#define atc2609a_reg_desc_dcdc_range(num, en_bit) { \
.name = "DCDC"#num, \
.supply_name = "dcdc"#num, \
.of_match = of_match_ptr("dcdc"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2609A_ID_DCDC##num, \
.ops = &atc260x_dcdc_range_ops, \
.type = REGULATOR_VOLTAGE, \
.n_voltages = 233, \
.linear_ranges = atc2609a_dcdc_voltage_ranges, \
.n_linear_ranges = ARRAY_SIZE(atc2609a_dcdc_voltage_ranges), \
.vsel_reg = ATC2609A_PMU_DC##num##_CTL0, \
.vsel_mask = GENMASK(15, 8), \
.enable_reg = ATC2609A_PMU_DC_OSC, \
.enable_mask = BIT(en_bit), \
.enable_time = 800, \
.owner = THIS_MODULE, \
}
#define atc2609a_reg_desc_ldo(num) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2609A_ID_LDO##num, \
.ops = &atc260x_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = 700000, \
.uV_step = 100000, \
.n_voltages = 16, \
.vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.vsel_mask = GENMASK(4, 1), \
.enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.enable_mask = BIT(0), \
.enable_time = 2000, \
.owner = THIS_MODULE, \
}
#define atc2609a_reg_desc_ldo_bypass(num) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2609A_ID_LDO##num, \
.ops = &atc260x_ldo_bypass_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = 2300000, \
.uV_step = 100000, \
.n_voltages = 12, \
.vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.vsel_mask = GENMASK(5, 2), \
.enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.enable_mask = BIT(0), \
.enable_time = 2000, \
.bypass_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.bypass_mask = BIT(1), \
.owner = THIS_MODULE, \
}
#define atc2609a_reg_desc_ldo_range_pick(num, n_range) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2609A_ID_LDO##num, \
.ops = &atc260x_ldo_range_pick_ops, \
.type = REGULATOR_VOLTAGE, \
.linear_ranges = atc2609a_ldo_voltage_ranges##n_range, \
.n_linear_ranges = ARRAY_SIZE(atc2609a_ldo_voltage_ranges##n_range), \
.vsel_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.vsel_mask = GENMASK(4, 1), \
.vsel_range_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.vsel_range_mask = BIT(5), \
.linear_range_selectors = atc260x_ldo_voltage_range_sel, \
.enable_reg = ATC2609A_PMU_LDO##num##_CTL0, \
.enable_mask = BIT(0), \
.enable_time = 2000, \
.owner = THIS_MODULE, \
}
#define atc2609a_reg_desc_ldo_fixed(num) { \
.name = "LDO"#num, \
.supply_name = "ldo"#num, \
.of_match = of_match_ptr("ldo"#num), \
.regulators_node = of_match_ptr("regulators"), \
.id = ATC2609A_ID_LDO##num, \
.ops = &atc260x_ldo_fixed_ops, \
.type = REGULATOR_VOLTAGE, \
.min_uV = 2600000, \
.uV_step = 100000, \
.n_voltages = 8, \
.vsel_reg = ATC2609A_PMU_LDO##num##_CTL, \
.vsel_mask = GENMASK(15, 13), \
.enable_time = 2000, \
.owner = THIS_MODULE, \
}
static const struct regulator_desc atc2609a_reg[] = {
atc2609a_reg_desc_dcdc(0, 4),
atc2609a_reg_desc_dcdc(1, 5),
atc2609a_reg_desc_dcdc(2, 6),
atc2609a_reg_desc_dcdc_range(3, 7),
atc2609a_reg_desc_dcdc(4, 8),
atc2609a_reg_desc_ldo_bypass(0),
atc2609a_reg_desc_ldo_bypass(1),
atc2609a_reg_desc_ldo_bypass(2),
atc2609a_reg_desc_ldo_range_pick(3, 0),
atc2609a_reg_desc_ldo_range_pick(4, 0),
atc2609a_reg_desc_ldo(5),
atc2609a_reg_desc_ldo_range_pick(6, 1),
atc2609a_reg_desc_ldo_range_pick(7, 0),
atc2609a_reg_desc_ldo_range_pick(8, 0),
atc2609a_reg_desc_ldo_fixed(9),
};
static int atc260x_regulator_probe(struct platform_device *pdev)
{
struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
struct device *dev = atc260x->dev;
struct atc260x_regulator_data *atc260x_data;
struct regulator_config config = {};
struct regulator_dev *atc260x_rdev;
const struct regulator_desc *regulators;
bool atc2603c_ver_b = false;
int i, nregulators;
atc260x_data = devm_kzalloc(&pdev->dev, sizeof(*atc260x_data), GFP_KERNEL);
if (!atc260x_data)
return -ENOMEM;
atc260x_data->voltage_time_dcdc = 350;
atc260x_data->voltage_time_ldo = 800;
switch (atc260x->ic_type) {
case ATC2603C:
regulators = atc2603c_reg;
nregulators = ATC2603C_ID_MAX;
atc2603c_ver_b = atc260x->ic_ver == ATC260X_B;
break;
case ATC2609A:
atc260x_data->voltage_time_dcdc = 250;
regulators = atc2609a_reg;
nregulators = ATC2609A_ID_MAX;
break;
default:
dev_err(dev, "unsupported ATC260X ID %d\n", atc260x->ic_type);
return -EINVAL;
}
config.dev = dev;
config.regmap = atc260x->regmap;
config.driver_data = atc260x_data;
/* Instantiate the regulators */
for (i = 0; i < nregulators; i++) {
if (atc2603c_ver_b && regulators[i].id == ATC2603C_ID_DCDC2)
atc260x_rdev = devm_regulator_register(&pdev->dev,
&atc2603c_reg_dcdc2_ver_b,
&config);
else
atc260x_rdev = devm_regulator_register(&pdev->dev,
&regulators[i],
&config);
if (IS_ERR(atc260x_rdev)) {
dev_err(dev, "failed to register regulator: %d\n", i);
return PTR_ERR(atc260x_rdev);
}
}
return 0;
}
static struct platform_driver atc260x_regulator_driver = {
.probe = atc260x_regulator_probe,
.driver = {
.name = "atc260x-regulator",
},
};
module_platform_driver(atc260x_regulator_driver);
MODULE_DESCRIPTION("Regulator driver for ATC260x PMICs");
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -1070,7 +1070,7 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
static int axp20x_regulator_parse_dt(struct platform_device *pdev)
{
struct device_node *np, *regulators;
int ret;
int ret = 0;
u32 dcdcfreq = 0;
np = of_node_get(pdev->dev.parent->of_node);
@ -1085,13 +1085,12 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
if (ret < 0) {
dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret);
return ret;
}
of_node_put(regulators);
}
return 0;
of_node_put(np);
return ret;
}
static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)

View File

@ -244,19 +244,14 @@ static const struct regulator_desc bd70528_desc[] = {
static int bd70528_probe(struct platform_device *pdev)
{
struct rohm_regmap_dev *bd70528;
int i;
struct regulator_config config = {
.dev = pdev->dev.parent,
};
bd70528 = dev_get_drvdata(pdev->dev.parent);
if (!bd70528) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
config.regmap = bd70528->regmap;
config.regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!config.regmap)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(bd70528_desc); i++) {
struct regulator_dev *rdev;

View File

@ -749,19 +749,14 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
static int bd71828_probe(struct platform_device *pdev)
{
struct rohm_regmap_dev *bd71828;
int i, j, ret;
struct regulator_config config = {
.dev = pdev->dev.parent,
};
bd71828 = dev_get_drvdata(pdev->dev.parent);
if (!bd71828) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
config.regmap = bd71828->regmap;
config.regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!config.regmap)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) {
struct regulator_dev *rdev;
@ -777,7 +772,7 @@ static int bd71828_probe(struct platform_device *pdev)
return PTR_ERR(rdev);
}
for (j = 0; j < rd->reg_init_amnt; j++) {
ret = regmap_update_bits(bd71828->regmap,
ret = regmap_update_bits(config.regmap,
rd->reg_inits[j].reg,
rd->reg_inits[j].mask,
rd->reg_inits[j].val);

View File

@ -1554,7 +1554,7 @@ static int get_special_regulators(struct device *dev,
static int bd718xx_probe(struct platform_device *pdev)
{
struct bd718xx *mfd;
struct regmap *regmap;
struct regulator_config config = { 0 };
int i, j, err, omit_enable;
bool use_snvs;
@ -1563,11 +1563,10 @@ static int bd718xx_probe(struct platform_device *pdev)
enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
const struct regulator_ops **swops, **hwops;
mfd = dev_get_drvdata(pdev->dev.parent);
if (!mfd) {
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap) {
dev_err(&pdev->dev, "No MFD driver data\n");
err = -EINVAL;
goto err;
return -EINVAL;
}
switch (chip) {
@ -1590,7 +1589,7 @@ static int bd718xx_probe(struct platform_device *pdev)
}
/* Register LOCK release */
err = regmap_update_bits(mfd->chip.regmap, BD718XX_REG_REGLOCK,
err = regmap_update_bits(regmap, BD718XX_REG_REGLOCK,
(REGLOCK_PWRSEQ | REGLOCK_VREG), 0);
if (err) {
dev_err(&pdev->dev, "Failed to unlock PMIC (%d)\n", err);
@ -1609,8 +1608,7 @@ static int bd718xx_probe(struct platform_device *pdev)
* bit allowing HW defaults for power rails to be used
*/
if (!use_snvs) {
err = regmap_update_bits(mfd->chip.regmap,
BD718XX_REG_TRANS_COND1,
err = regmap_update_bits(regmap, BD718XX_REG_TRANS_COND1,
BD718XX_ON_REQ_POWEROFF_MASK |
BD718XX_SWRESET_POWEROFF_MASK |
BD718XX_WDOG_POWEROFF_MASK |
@ -1626,7 +1624,7 @@ static int bd718xx_probe(struct platform_device *pdev)
}
config.dev = pdev->dev.parent;
config.regmap = mfd->chip.regmap;
config.regmap = regmap;
/*
* There are cases when we want to leave the enable-control for
* the HW state machine and use this driver only for voltage control.
@ -1685,7 +1683,7 @@ static int bd718xx_probe(struct platform_device *pdev)
if (!no_enable_control && (!use_snvs ||
!rdev->constraints->always_on ||
!rdev->constraints->boot_on)) {
err = regmap_update_bits(mfd->chip.regmap, r->init.reg,
err = regmap_update_bits(regmap, r->init.reg,
r->init.mask, r->init.val);
if (err) {
dev_err(&pdev->dev,
@ -1695,7 +1693,7 @@ static int bd718xx_probe(struct platform_device *pdev)
}
}
for (j = 0; j < r->additional_init_amnt; j++) {
err = regmap_update_bits(mfd->chip.regmap,
err = regmap_update_bits(regmap,
r->additional_inits[j].reg,
r->additional_inits[j].mask,
r->additional_inits[j].val);

View File

@ -1617,7 +1617,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
const char *supply_name)
{
struct regulator *regulator;
int err;
int err = 0;
if (dev) {
char buf[REG_STR_SIZE];
@ -1663,8 +1663,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
}
}
regulator->debugfs = debugfs_create_dir(supply_name,
rdev->debugfs);
if (err != -EEXIST)
regulator->debugfs = debugfs_create_dir(supply_name, rdev->debugfs);
if (!regulator->debugfs) {
rdev_dbg(rdev, "Failed to create debugfs directory\n");
} else {
@ -2042,7 +2042,7 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
* Returns a struct regulator corresponding to the regulator producer,
* or IS_ERR() condition containing errno.
*
* Use of supply names configured via regulator_set_device_supply() is
* Use of supply names configured via set_consumer_device_supply() is
* strongly encouraged. It is recommended that the supply name used
* should match the name used for the supply and/or the relevant
* device pins in the datasheet.
@ -2069,7 +2069,7 @@ EXPORT_SYMBOL_GPL(regulator_get);
* regulator off for correct operation of the hardware they are
* controlling.
*
* Use of supply names configured via regulator_set_device_supply() is
* Use of supply names configured via set_consumer_device_supply() is
* strongly encouraged. It is recommended that the supply name used
* should match the name used for the supply and/or the relevant
* device pins in the datasheet.
@ -2095,7 +2095,7 @@ EXPORT_SYMBOL_GPL(regulator_get_exclusive);
* disrupting the operation of drivers that can handle absent
* supplies.
*
* Use of supply names configured via regulator_set_device_supply() is
* Use of supply names configured via set_consumer_device_supply() is
* strongly encouraged. It is recommended that the supply name used
* should match the name used for the supply and/or the relevant
* device pins in the datasheet.
@ -4153,7 +4153,11 @@ int regulator_sync_voltage(struct regulator *regulator)
if (ret < 0)
goto out;
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
/* balance only, if regulator is coupled */
if (rdev->coupling_desc.n_coupled > 1)
ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON);
else
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
out:
regulator_unlock(rdev);

View File

@ -550,7 +550,7 @@ static int mcp16502_probe(struct i2c_client *client,
config.regmap = rmap;
config.driver_data = mcp;
mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW);
mcp->lpm = devm_gpiod_get_optional(dev, "lpm", GPIOD_OUT_LOW);
if (IS_ERR(mcp->lpm)) {
dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm));
return PTR_ERR(mcp->lpm);

View File

@ -0,0 +1,299 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2021 MediaTek Inc.
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/mt6315-regulator.h>
#include <linux/regulator/of_regulator.h>
#include <linux/spmi.h>
#define MT6315_BUCK_MODE_AUTO 0
#define MT6315_BUCK_MODE_FORCE_PWM 1
#define MT6315_BUCK_MODE_LP 2
struct mt6315_regulator_info {
struct regulator_desc desc;
u32 status_reg;
u32 lp_mode_mask;
u32 lp_mode_shift;
};
struct mt_regulator_init_data {
u32 modeset_mask[MT6315_VBUCK_MAX];
};
struct mt6315_chip {
struct device *dev;
struct regmap *regmap;
};
#define MT_BUCK(_name, _bid, _vsel) \
[_bid] = { \
.desc = { \
.name = _name, \
.of_match = of_match_ptr(_name), \
.regulators_node = "regulators", \
.ops = &mt6315_volt_range_ops, \
.type = REGULATOR_VOLTAGE, \
.id = _bid, \
.owner = THIS_MODULE, \
.n_voltages = 0xbf, \
.linear_ranges = mt_volt_range1, \
.n_linear_ranges = ARRAY_SIZE(mt_volt_range1), \
.vsel_reg = _vsel, \
.vsel_mask = 0xff, \
.enable_reg = MT6315_BUCK_TOP_CON0, \
.enable_mask = BIT(_bid), \
.of_map_mode = mt6315_map_mode, \
}, \
.status_reg = _bid##_DBG4, \
.lp_mode_mask = BIT(_bid), \
.lp_mode_shift = _bid, \
}
static const struct linear_range mt_volt_range1[] = {
REGULATOR_LINEAR_RANGE(0, 0, 0xbf, 6250),
};
static unsigned int mt6315_map_mode(u32 mode)
{
switch (mode) {
case MT6315_BUCK_MODE_AUTO:
return REGULATOR_MODE_NORMAL;
case MT6315_BUCK_MODE_FORCE_PWM:
return REGULATOR_MODE_FAST;
case MT6315_BUCK_MODE_LP:
return REGULATOR_MODE_IDLE;
default:
return -EINVAL;
}
}
static unsigned int mt6315_regulator_get_mode(struct regulator_dev *rdev)
{
struct mt_regulator_init_data *init = rdev_get_drvdata(rdev);
const struct mt6315_regulator_info *info;
int ret, regval;
u32 modeset_mask;
info = container_of(rdev->desc, struct mt6315_regulator_info, desc);
modeset_mask = init->modeset_mask[rdev_get_id(rdev)];
ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_4PHASE_ANA_CON42, &regval);
if (ret != 0) {
dev_notice(&rdev->dev, "Failed to get mode: %d\n", ret);
return ret;
}
if ((regval & modeset_mask) == modeset_mask)
return REGULATOR_MODE_FAST;
ret = regmap_read(rdev->regmap, MT6315_BUCK_TOP_CON1, &regval);
if (ret != 0) {
dev_notice(&rdev->dev, "Failed to get lp mode: %d\n", ret);
return ret;
}
if (regval & info->lp_mode_mask)
return REGULATOR_MODE_IDLE;
else
return REGULATOR_MODE_NORMAL;
}
static int mt6315_regulator_set_mode(struct regulator_dev *rdev,
u32 mode)
{
struct mt_regulator_init_data *init = rdev_get_drvdata(rdev);
const struct mt6315_regulator_info *info;
int ret, val, curr_mode;
u32 modeset_mask;
info = container_of(rdev->desc, struct mt6315_regulator_info, desc);
modeset_mask = init->modeset_mask[rdev_get_id(rdev)];
curr_mode = mt6315_regulator_get_mode(rdev);
switch (mode) {
case REGULATOR_MODE_FAST:
ret = regmap_update_bits(rdev->regmap,
MT6315_BUCK_TOP_4PHASE_ANA_CON42,
modeset_mask,
modeset_mask);
break;
case REGULATOR_MODE_NORMAL:
if (curr_mode == REGULATOR_MODE_FAST) {
ret = regmap_update_bits(rdev->regmap,
MT6315_BUCK_TOP_4PHASE_ANA_CON42,
modeset_mask,
0);
} else if (curr_mode == REGULATOR_MODE_IDLE) {
ret = regmap_update_bits(rdev->regmap,
MT6315_BUCK_TOP_CON1,
info->lp_mode_mask,
0);
usleep_range(100, 110);
} else {
ret = -EINVAL;
}
break;
case REGULATOR_MODE_IDLE:
val = MT6315_BUCK_MODE_LP >> 1;
val <<= info->lp_mode_shift;
ret = regmap_update_bits(rdev->regmap,
MT6315_BUCK_TOP_CON1,
info->lp_mode_mask,
val);
break;
default:
ret = -EINVAL;
dev_notice(&rdev->dev, "Unsupported mode: %d\n", mode);
break;
}
if (ret != 0) {
dev_notice(&rdev->dev, "Failed to set mode: %d\n", ret);
return ret;
}
return 0;
}
static int mt6315_get_status(struct regulator_dev *rdev)
{
const struct mt6315_regulator_info *info;
int ret;
u32 regval;
info = container_of(rdev->desc, struct mt6315_regulator_info, desc);
ret = regmap_read(rdev->regmap, info->status_reg, &regval);
if (ret < 0) {
dev_notice(&rdev->dev, "Failed to get enable reg: %d\n", ret);
return ret;
}
return (regval & BIT(0)) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF;
}
static const struct regulator_ops mt6315_volt_range_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_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,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_status = mt6315_get_status,
.set_mode = mt6315_regulator_set_mode,
.get_mode = mt6315_regulator_get_mode,
};
static const struct mt6315_regulator_info mt6315_regulators[MT6315_VBUCK_MAX] = {
MT_BUCK("vbuck1", MT6315_VBUCK1, MT6315_BUCK_TOP_ELR0),
MT_BUCK("vbuck2", MT6315_VBUCK2, MT6315_BUCK_TOP_ELR2),
MT_BUCK("vbuck3", MT6315_VBUCK3, MT6315_BUCK_TOP_ELR4),
MT_BUCK("vbuck4", MT6315_VBUCK4, MT6315_BUCK_TOP_ELR6),
};
static const struct regmap_config mt6315_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.max_register = 0x16d0,
.fast_io = true,
};
static const struct of_device_id mt6315_of_match[] = {
{
.compatible = "mediatek,mt6315-regulator",
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(of, mt6315_of_match);
static int mt6315_regulator_probe(struct spmi_device *pdev)
{
struct device *dev = &pdev->dev;
struct regmap *regmap;
struct mt6315_chip *chip;
struct mt_regulator_init_data *init_data;
struct regulator_config config = {};
struct regulator_dev *rdev;
int i;
regmap = devm_regmap_init_spmi_ext(pdev, &mt6315_regmap_config);
if (!regmap)
return -ENODEV;
chip = devm_kzalloc(dev, sizeof(struct mt6315_chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
init_data = devm_kzalloc(dev, sizeof(struct mt_regulator_init_data), GFP_KERNEL);
if (!init_data)
return -ENOMEM;
switch (pdev->usid) {
case MT6315_PP:
init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1) | BIT(MT6315_VBUCK2) |
BIT(MT6315_VBUCK4);
break;
case MT6315_SP:
case MT6315_RP:
init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1) | BIT(MT6315_VBUCK2);
break;
default:
init_data->modeset_mask[MT6315_VBUCK1] = BIT(MT6315_VBUCK1);
break;
}
for (i = MT6315_VBUCK2; i < MT6315_VBUCK_MAX; i++)
init_data->modeset_mask[i] = BIT(i);
chip->dev = dev;
chip->regmap = regmap;
dev_set_drvdata(dev, chip);
config.dev = dev;
config.regmap = regmap;
for (i = MT6315_VBUCK1; i < MT6315_VBUCK_MAX; i++) {
config.driver_data = init_data;
rdev = devm_regulator_register(dev, &mt6315_regulators[i].desc, &config);
if (IS_ERR(rdev)) {
dev_notice(dev, "Failed to register %s\n", mt6315_regulators[i].desc.name);
continue;
}
}
return 0;
}
static void mt6315_regulator_shutdown(struct spmi_device *pdev)
{
struct mt6315_chip *chip = dev_get_drvdata(&pdev->dev);
int ret = 0;
ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY_H, PROTECTION_KEY_H);
ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY, PROTECTION_KEY);
ret |= regmap_update_bits(chip->regmap, MT6315_TOP2_ELR7, 1, 1);
ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY, 0);
ret |= regmap_write(chip->regmap, MT6315_TOP_TMA_KEY_H, 0);
if (ret < 0)
dev_notice(&pdev->dev, "[%#x] Failed to enable power off sequence. %d\n",
pdev->usid, ret);
}
static struct spmi_driver mt6315_regulator_driver = {
.driver = {
.name = "mt6315-regulator",
.of_match_table = mt6315_of_match,
},
.probe = mt6315_regulator_probe,
.shutdown = mt6315_regulator_shutdown,
};
module_spmi_driver(mt6315_regulator_driver);
MODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>");
MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6315 PMIC");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,215 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2020 MediaTek Inc.
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/soc/mediatek/mtk_dvfsrc.h>
#define DVFSRC_ID_VCORE 0
#define DVFSRC_ID_VSCP 1
#define MT_DVFSRC_REGULAR(match, _name, _volt_table) \
[DVFSRC_ID_##_name] = { \
.desc = { \
.name = match, \
.of_match = of_match_ptr(match), \
.ops = &dvfsrc_vcore_ops, \
.type = REGULATOR_VOLTAGE, \
.id = DVFSRC_ID_##_name, \
.owner = THIS_MODULE, \
.n_voltages = ARRAY_SIZE(_volt_table), \
.volt_table = _volt_table, \
}, \
}
/*
* DVFSRC regulators' information
*
* @desc: standard fields of regulator description.
* @voltage_selector: Selector used for get_voltage_sel() and
* set_voltage_sel() callbacks
*/
struct dvfsrc_regulator {
struct regulator_desc desc;
};
/*
* MTK DVFSRC regulators' init data
*
* @size: num of regulators
* @regulator_info: regulator info.
*/
struct dvfsrc_regulator_init_data {
u32 size;
struct dvfsrc_regulator *regulator_info;
};
static inline struct device *to_dvfsrc_dev(struct regulator_dev *rdev)
{
return rdev_get_dev(rdev)->parent;
}
static int dvfsrc_set_voltage_sel(struct regulator_dev *rdev,
unsigned int selector)
{
struct device *dvfsrc_dev = to_dvfsrc_dev(rdev);
int id = rdev_get_id(rdev);
if (id == DVFSRC_ID_VCORE)
mtk_dvfsrc_send_request(dvfsrc_dev,
MTK_DVFSRC_CMD_VCORE_REQUEST,
selector);
else if (id == DVFSRC_ID_VSCP)
mtk_dvfsrc_send_request(dvfsrc_dev,
MTK_DVFSRC_CMD_VSCP_REQUEST,
selector);
else
return -EINVAL;
return 0;
}
static int dvfsrc_get_voltage_sel(struct regulator_dev *rdev)
{
struct device *dvfsrc_dev = to_dvfsrc_dev(rdev);
int id = rdev_get_id(rdev);
int val, ret;
if (id == DVFSRC_ID_VCORE)
ret = mtk_dvfsrc_query_info(dvfsrc_dev,
MTK_DVFSRC_CMD_VCORE_LEVEL_QUERY,
&val);
else if (id == DVFSRC_ID_VSCP)
ret = mtk_dvfsrc_query_info(dvfsrc_dev,
MTK_DVFSRC_CMD_VSCP_LEVEL_QUERY,
&val);
else
return -EINVAL;
if (ret != 0)
return ret;
return val;
}
static const struct regulator_ops dvfsrc_vcore_ops = {
.list_voltage = regulator_list_voltage_table,
.get_voltage_sel = dvfsrc_get_voltage_sel,
.set_voltage_sel = dvfsrc_set_voltage_sel,
};
static const unsigned int mt8183_voltages[] = {
725000,
800000,
};
static struct dvfsrc_regulator mt8183_regulators[] = {
MT_DVFSRC_REGULAR("dvfsrc-vcore", VCORE,
mt8183_voltages),
};
static const struct dvfsrc_regulator_init_data regulator_mt8183_data = {
.size = ARRAY_SIZE(mt8183_regulators),
.regulator_info = &mt8183_regulators[0],
};
static const unsigned int mt6873_voltages[] = {
575000,
600000,
650000,
725000,
};
static struct dvfsrc_regulator mt6873_regulators[] = {
MT_DVFSRC_REGULAR("dvfsrc-vcore", VCORE,
mt6873_voltages),
MT_DVFSRC_REGULAR("dvfsrc-vscp", VSCP,
mt6873_voltages),
};
static const struct dvfsrc_regulator_init_data regulator_mt6873_data = {
.size = ARRAY_SIZE(mt6873_regulators),
.regulator_info = &mt6873_regulators[0],
};
static const struct of_device_id mtk_dvfsrc_regulator_match[] = {
{
.compatible = "mediatek,mt8183-dvfsrc",
.data = &regulator_mt8183_data,
}, {
.compatible = "mediatek,mt8192-dvfsrc",
.data = &regulator_mt6873_data,
}, {
.compatible = "mediatek,mt6873-dvfsrc",
.data = &regulator_mt6873_data,
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(of, mtk_dvfsrc_regulator_match);
static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct regulator_config config = { };
struct regulator_dev *rdev;
const struct dvfsrc_regulator_init_data *regulator_init_data;
struct dvfsrc_regulator *mt_regulators;
int i;
match = of_match_node(mtk_dvfsrc_regulator_match, dev->parent->of_node);
if (!match) {
dev_err(dev, "invalid compatible string\n");
return -ENODEV;
}
regulator_init_data = match->data;
mt_regulators = regulator_init_data->regulator_info;
for (i = 0; i < regulator_init_data->size; i++) {
config.dev = dev->parent;
config.driver_data = (mt_regulators + i);
rdev = devm_regulator_register(dev->parent,
&(mt_regulators + i)->desc,
&config);
if (IS_ERR(rdev)) {
dev_err(dev, "failed to register %s\n",
(mt_regulators + i)->desc.name);
return PTR_ERR(rdev);
}
}
return 0;
}
static struct platform_driver mtk_dvfsrc_regulator_driver = {
.driver = {
.name = "mtk-dvfsrc-regulator",
},
.probe = dvfsrc_vcore_regulator_probe,
};
static int __init mtk_dvfsrc_regulator_init(void)
{
return platform_driver_register(&mtk_dvfsrc_regulator_driver);
}
subsys_initcall(mtk_dvfsrc_regulator_init);
static void __exit mtk_dvfsrc_regulator_exit(void)
{
platform_driver_unregister(&mtk_dvfsrc_regulator_driver);
}
module_exit(mtk_dvfsrc_regulator_exit);
MODULE_AUTHOR("Arvin wang <arvin.wang@mediatek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -5,6 +5,7 @@
*/
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@ -32,6 +33,7 @@ struct pca9450_regulator_desc {
struct pca9450 {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *sd_vsel_gpio;
enum pca9450_chip_type type;
unsigned int rcnt;
int irq;
@ -795,6 +797,26 @@ static int pca9450_i2c_probe(struct i2c_client *i2c,
return ret;
}
/* Set reset behavior on assertion of WDOG_B signal */
ret = regmap_update_bits(pca9450->regmap, PCA9450_REG_RESET_CTRL,
WDOG_B_CFG_MASK, WDOG_B_CFG_COLD_LDO12);
if (ret) {
dev_err(&i2c->dev, "Failed to set WDOG_B reset behavior\n");
return ret;
}
/*
* The driver uses the LDO5CTRL_H register to control the LDO5 regulator.
* This is only valid if the SD_VSEL input of the PMIC is high. Let's
* check if the pin is available as GPIO and set it to high.
*/
pca9450->sd_vsel_gpio = gpiod_get_optional(pca9450->dev, "sd-vsel", GPIOD_OUT_HIGH);
if (IS_ERR(pca9450->sd_vsel_gpio)) {
dev_err(&i2c->dev, "Failed to get SD_VSEL GPIO\n");
return ret;
}
dev_info(&i2c->dev, "%s probed.\n",
type == PCA9450_TYPE_PCA9450A ? "pca9450a" : "pca9450bc");

View File

@ -114,7 +114,6 @@ enum swxilim_bits {
#define PF8X00_SWXILIM_SHIFT 3
#define PF8X00_SWXILIM_MASK GENMASK(4, 3)
#define PF8X00_SWXPHASE_MASK GENMASK(2, 0)
#define PF8X00_SWXPHASE_DEFAULT 0
#define PF8X00_SWXPHASE_SHIFT 7
enum pf8x00_devid {
@ -126,10 +125,12 @@ enum pf8x00_devid {
#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4)
#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0)
struct pf8x00_regulator {
struct pf8x00_regulator_data {
struct regulator_desc desc;
u8 ilim;
u8 phase_shift;
unsigned int suspend_enable_reg;
unsigned int suspend_enable_mask;
unsigned int suspend_voltage_reg;
unsigned int suspend_voltage_cache;
};
struct pf8x00_chip {
@ -150,35 +151,16 @@ static const int pf8x00_ldo_voltages[] = {
3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000,
};
#define SWV(i) (6250 * i + 400000)
#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \
SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7)
/* Output: 2.1A to 4.5A */
static const unsigned int pf8x00_sw_current_table[] = {
2100000, 2600000, 3000000, 4500000,
};
/* Output: 0.4V to 1.8V */
static const int pf8x00_sw1_to_6_voltages[] = {
SWV_LINE(0),
SWV_LINE(1),
SWV_LINE(2),
SWV_LINE(3),
SWV_LINE(4),
SWV_LINE(5),
SWV_LINE(6),
SWV_LINE(7),
SWV_LINE(8),
SWV_LINE(9),
SWV_LINE(10),
SWV_LINE(11),
SWV_LINE(12),
SWV_LINE(13),
SWV_LINE(14),
SWV_LINE(15),
SWV_LINE(16),
SWV_LINE(17),
SWV_LINE(18),
SWV_LINE(19),
SWV_LINE(20),
SWV_LINE(21),
1500000, 1800000,
#define PF8XOO_SW1_6_VOLTAGE_NUM 0xB2
static const struct linear_range pf8x00_sw1_to_6_voltages[] = {
REGULATOR_LINEAR_RANGE(400000, 0x00, 0xB0, 6250),
REGULATOR_LINEAR_RANGE(1800000, 0xB1, 0xB1, 0),
};
/* Output: 1.0V to 4.1V */
@ -194,15 +176,10 @@ static const int pf8x00_vsnvs_voltages[] = {
0, 1800000, 3000000, 3300000,
};
static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc)
static void swxilim_select(struct pf8x00_chip *chip, int id, int ilim)
{
return container_of(desc, struct pf8x00_regulator, desc);
}
static void swxilim_select(const struct regulator_desc *desc, int ilim)
{
struct pf8x00_regulator *data = desc_to_regulator(desc);
u8 ilim_sel;
u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
switch (ilim) {
case 2100:
@ -222,43 +199,130 @@ static void swxilim_select(const struct regulator_desc *desc, int ilim)
break;
}
data->ilim = ilim_sel;
regmap_update_bits(chip->regmap, reg,
PF8X00_SWXILIM_MASK,
ilim_sel << PF8X00_SWXILIM_SHIFT);
}
static void handle_ilim_property(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *config)
{
struct pf8x00_chip *chip = config->driver_data;
int ret;
int val;
if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) {
ret = of_property_read_u32(np, "nxp,ilim-ma", &val);
if (ret) {
dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use value stored in OTP\n",
desc->id - PF8X00_LDO4);
return;
}
dev_warn(chip->dev, "nxp,ilim-ma is deprecated, please use regulator-max-microamp\n");
swxilim_select(chip, desc->id, val);
} else
dev_warn(chip->dev, "nxp,ilim-ma used with incorrect regulator (%d)\n", desc->id);
}
static void handle_shift_property(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *config)
{
unsigned char id = desc->id - PF8X00_LDO4;
unsigned char reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
struct pf8x00_chip *chip = config->driver_data;
int phase;
int val;
int ret;
if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) {
ret = of_property_read_u32(np, "nxp,phase-shift", &val);
if (ret) {
dev_dbg(chip->dev,
"unspecified phase-shift for BUCK%d, using OTP configuration\n",
id);
return;
}
if (val < 0 || val > 315 || val % 45 != 0) {
dev_warn(config->dev,
"invalid phase_shift %d for BUCK%d, using OTP configuration\n",
val, id);
return;
}
phase = val / 45;
if (phase >= 1)
phase -= 1;
else
phase = PF8X00_SWXPHASE_SHIFT;
regmap_update_bits(chip->regmap, reg,
PF8X00_SWXPHASE_MASK,
phase);
} else
dev_warn(chip->dev, "nxp,phase-shift used with incorrect regulator (%d)\n", id);
}
static int pf8x00_of_parse_cb(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *config)
{
struct pf8x00_regulator *data = desc_to_regulator(desc);
struct pf8x00_chip *chip = config->driver_data;
int phase;
int val;
handle_ilim_property(np, desc, config);
handle_shift_property(np, desc, config);
return 0;
}
static int pf8x00_suspend_enable(struct regulator_dev *rdev)
{
struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev);
struct regmap *rmap = rdev_get_regmap(rdev);
return regmap_update_bits(rmap, regl->suspend_enable_reg,
regl->suspend_enable_mask,
regl->suspend_enable_mask);
}
static int pf8x00_suspend_disable(struct regulator_dev *rdev)
{
struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev);
struct regmap *rmap = rdev_get_regmap(rdev);
return regmap_update_bits(rmap, regl->suspend_enable_reg,
regl->suspend_enable_mask, 0);
}
static int pf8x00_set_suspend_voltage(struct regulator_dev *rdev, int uV)
{
struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev);
int ret;
ret = of_property_read_u32(np, "nxp,ilim-ma", &val);
if (ret)
dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n",
desc->id - PF8X00_LDO4);
if (regl->suspend_voltage_cache == uV)
return 0;
swxilim_select(desc, val);
ret = of_property_read_u32(np, "nxp,phase-shift", &val);
if (ret) {
dev_dbg(chip->dev,
"unspecified phase-shift for BUCK%d, use 0 degrees\n",
desc->id - PF8X00_LDO4);
val = PF8X00_SWXPHASE_DEFAULT;
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;
}
phase = val / 45;
if ((phase * 45) != val) {
dev_warn(config->dev,
"invalid phase_shift %d for BUCK%d, use 0 degrees\n",
(phase * 45), desc->id - PF8X00_LDO4);
phase = PF8X00_SWXPHASE_SHIFT;
dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n",
uV, regl->suspend_voltage_reg, regl->desc.vsel_mask, ret);
ret = regmap_update_bits(rdev->regmap, regl->suspend_voltage_reg,
regl->desc.vsel_mask, ret);
if (ret < 0) {
dev_err(rdev_get_dev(rdev), "failed to set %i uV\n", uV);
return ret;
}
data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT;
regl->suspend_voltage_cache = uV;
return 0;
}
@ -270,15 +334,37 @@ static const struct regulator_ops pf8x00_ldo_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_suspend_enable = pf8x00_suspend_enable,
.set_suspend_disable = pf8x00_suspend_disable,
.set_suspend_voltage = pf8x00_set_suspend_voltage,
};
static const struct regulator_ops pf8x00_buck_ops = {
static const struct regulator_ops pf8x00_buck1_6_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,
.get_current_limit = regulator_get_current_limit_regmap,
.set_current_limit = regulator_set_current_limit_regmap,
.set_suspend_enable = pf8x00_suspend_enable,
.set_suspend_disable = pf8x00_suspend_disable,
.set_suspend_voltage = pf8x00_set_suspend_voltage,
};
static const struct regulator_ops pf8x00_buck7_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.get_current_limit = regulator_get_current_limit_regmap,
.set_current_limit = regulator_set_current_limit_regmap,
.set_suspend_enable = pf8x00_suspend_enable,
.set_suspend_disable = pf8x00_suspend_disable,
};
static const struct regulator_ops pf8x00_vsnvs_ops = {
@ -310,6 +396,9 @@ static const struct regulator_ops pf8x00_vsnvs_ops = {
.disable_val = 0x0, \
.enable_mask = 2, \
}, \
.suspend_enable_reg = (base) + LDO_CONFIG2, \
.suspend_enable_mask = 1, \
.suspend_voltage_reg = (base) + LDO_STBY_VOLT, \
}
#define PF8X00BUCK(_id, _name, base, voltages) \
@ -319,14 +408,54 @@ static const struct regulator_ops pf8x00_vsnvs_ops = {
.of_match = _name, \
.regulators_node = "regulators", \
.of_parse_cb = pf8x00_of_parse_cb, \
.n_voltages = ARRAY_SIZE(voltages), \
.ops = &pf8x00_buck_ops, \
.n_voltages = PF8XOO_SW1_6_VOLTAGE_NUM, \
.ops = &pf8x00_buck1_6_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PF8X00_BUCK ## _id, \
.owner = THIS_MODULE, \
.ramp_delay = 19000, \
.linear_ranges = pf8x00_sw1_to_6_voltages, \
.n_linear_ranges = \
ARRAY_SIZE(pf8x00_sw1_to_6_voltages), \
.vsel_reg = (base) + SW_RUN_VOLT, \
.vsel_mask = 0xff, \
.curr_table = pf8x00_sw_current_table, \
.n_current_limits = \
ARRAY_SIZE(pf8x00_sw_current_table), \
.csel_reg = (base) + SW_CONFIG2, \
.csel_mask = PF8X00_SWXILIM_MASK, \
.enable_reg = (base) + SW_MODE1, \
.enable_val = 0x3, \
.disable_val = 0x0, \
.enable_mask = 0x3, \
.enable_time = 500, \
}, \
.suspend_enable_reg = (base) + SW_MODE1, \
.suspend_enable_mask = 0xc, \
.suspend_voltage_reg = (base) + SW_STBY_VOLT, \
}
#define PF8X00BUCK7(_name, base, voltages) \
[PF8X00_BUCK7] = { \
.desc = { \
.name = _name, \
.of_match = _name, \
.regulators_node = "regulators", \
.of_parse_cb = pf8x00_of_parse_cb, \
.n_voltages = ARRAY_SIZE(voltages), \
.ops = &pf8x00_buck7_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PF8X00_BUCK7, \
.owner = THIS_MODULE, \
.ramp_delay = 19000, \
.volt_table = voltages, \
.vsel_reg = (base) + SW_RUN_VOLT, \
.vsel_mask = 0xff, \
.curr_table = pf8x00_sw_current_table, \
.n_current_limits = \
ARRAY_SIZE(pf8x00_sw_current_table), \
.csel_reg = (base) + SW_CONFIG2, \
.csel_mask = PF8X00_SWXILIM_MASK, \
.enable_reg = (base) + SW_MODE1, \
.enable_val = 0x3, \
.disable_val = 0x0, \
@ -335,6 +464,7 @@ static const struct regulator_ops pf8x00_vsnvs_ops = {
}, \
}
#define PF8X00VSNVS(_name, base, voltages) \
[PF8X00_VSNVS] = { \
.desc = { \
@ -352,7 +482,7 @@ static const struct regulator_ops pf8x00_vsnvs_ops = {
}, \
}
static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = {
static struct pf8x00_regulator_data pf8x00_regs_data[PF8X00_MAX_REGULATORS] = {
PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages),
PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages),
PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages),
@ -363,7 +493,7 @@ static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = {
PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages),
PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages),
PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages),
PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages),
PF8X00BUCK7("buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages),
PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages),
};
@ -437,12 +567,12 @@ static int pf8x00_i2c_probe(struct i2c_client *client)
if (ret)
return ret;
for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) {
struct pf8x00_regulator *data = &pf8x00_regulators_data[id];
for (id = 0; id < ARRAY_SIZE(pf8x00_regs_data); id++) {
struct pf8x00_regulator_data *data = &pf8x00_regs_data[id];
struct regulator_dev *rdev;
config.dev = chip->dev;
config.driver_data = chip;
config.driver_data = data;
config.regmap = chip->regmap;
rdev = devm_regulator_register(&client->dev, &data->desc, &config);
@ -451,18 +581,6 @@ static int pf8x00_i2c_probe(struct i2c_client *client)
"failed to register %s regulator\n", data->desc.name);
return PTR_ERR(rdev);
}
if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) {
u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
regmap_update_bits(chip->regmap, reg,
PF8X00_SWXPHASE_MASK,
data->phase_shift);
regmap_update_bits(chip->regmap, reg,
PF8X00_SWXILIM_MASK,
data->ilim << PF8X00_SWXILIM_SHIFT);
}
}
return 0;

View File

@ -17,11 +17,48 @@
#define PMI8998_LAB_REG_BASE 0xde00
#define PMI8998_IBB_REG_BASE 0xdc00
#define PMI8998_IBB_LAB_REG_OFFSET 0x200
#define REG_LABIBB_STATUS1 0x08
#define LABIBB_STATUS1_SC_BIT BIT(6)
#define LABIBB_STATUS1_VREG_OK_BIT BIT(7)
#define REG_LABIBB_INT_SET_TYPE 0x11
#define REG_LABIBB_INT_POLARITY_HIGH 0x12
#define REG_LABIBB_INT_POLARITY_LOW 0x13
#define REG_LABIBB_INT_LATCHED_CLR 0x14
#define REG_LABIBB_INT_EN_SET 0x15
#define REG_LABIBB_INT_EN_CLR 0x16
#define LABIBB_INT_VREG_OK BIT(0)
#define LABIBB_INT_VREG_TYPE_LEVEL 0
#define REG_LABIBB_VOLTAGE 0x41
#define LABIBB_VOLTAGE_OVERRIDE_EN BIT(7)
#define LAB_VOLTAGE_SET_MASK GENMASK(3, 0)
#define IBB_VOLTAGE_SET_MASK GENMASK(5, 0)
#define REG_LABIBB_ENABLE_CTL 0x46
#define LABIBB_STATUS1_VREG_OK_BIT BIT(7)
#define LABIBB_CONTROL_ENABLE BIT(7)
#define LABIBB_CONTROL_ENABLE BIT(7)
#define REG_LABIBB_PD_CTL 0x47
#define LAB_PD_CTL_MASK GENMASK(1, 0)
#define IBB_PD_CTL_MASK (BIT(0) | BIT(7))
#define LAB_PD_CTL_STRONG_PULL BIT(0)
#define IBB_PD_CTL_HALF_STRENGTH BIT(0)
#define IBB_PD_CTL_EN BIT(7)
#define REG_LABIBB_CURRENT_LIMIT 0x4b
#define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0)
#define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0)
#define LAB_CURRENT_LIMIT_OVERRIDE_EN BIT(3)
#define LABIBB_CURRENT_LIMIT_EN BIT(7)
#define REG_IBB_PWRUP_PWRDN_CTL_1 0x58
#define IBB_CTL_1_DISCHARGE_EN BIT(2)
#define REG_LABIBB_SOFT_START_CTL 0x5f
#define REG_LABIBB_SEC_ACCESS 0xd0
#define LABIBB_SEC_UNLOCK_CODE 0xa5
#define LAB_ENABLE_CTL_MASK BIT(7)
#define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6))
@ -30,14 +67,35 @@
#define LAB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 2)
#define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10)
#define LABIBB_POLL_ENABLED_TIME 1000
#define OCP_RECOVERY_INTERVAL_MS 500
#define SC_RECOVERY_INTERVAL_MS 250
#define LABIBB_MAX_OCP_COUNT 4
#define LABIBB_MAX_SC_COUNT 3
#define LABIBB_MAX_FATAL_COUNT 2
struct labibb_current_limits {
u32 uA_min;
u32 uA_step;
u8 ovr_val;
};
struct labibb_regulator {
struct regulator_desc desc;
struct device *dev;
struct regmap *regmap;
struct regulator_dev *rdev;
struct labibb_current_limits uA_limits;
struct delayed_work ocp_recovery_work;
struct delayed_work sc_recovery_work;
u16 base;
u8 type;
u8 dischg_sel;
u8 soft_start_sel;
int sc_irq;
int sc_count;
int ocp_irq;
int ocp_irq_count;
int fatal_count;
};
struct labibb_regulator_data {
@ -47,10 +105,579 @@ struct labibb_regulator_data {
const struct regulator_desc *desc;
};
static int qcom_labibb_ocp_hw_enable(struct regulator_dev *rdev)
{
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
int ret;
/* Clear irq latch status to avoid spurious event */
ret = regmap_update_bits(rdev->regmap,
vreg->base + REG_LABIBB_INT_LATCHED_CLR,
LABIBB_INT_VREG_OK, 1);
if (ret)
return ret;
/* Enable OCP HW interrupt */
return regmap_update_bits(rdev->regmap,
vreg->base + REG_LABIBB_INT_EN_SET,
LABIBB_INT_VREG_OK, 1);
}
static int qcom_labibb_ocp_hw_disable(struct regulator_dev *rdev)
{
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
return regmap_update_bits(rdev->regmap,
vreg->base + REG_LABIBB_INT_EN_CLR,
LABIBB_INT_VREG_OK, 1);
}
/**
* qcom_labibb_check_ocp_status - Check the Over-Current Protection status
* @vreg: Main driver structure
*
* This function checks the STATUS1 register for the VREG_OK bit: if it is
* set, then there is no Over-Current event.
*
* Returns: Zero if there is no over-current, 1 if in over-current or
* negative number for error
*/
static int qcom_labibb_check_ocp_status(struct labibb_regulator *vreg)
{
u32 cur_status;
int ret;
ret = regmap_read(vreg->rdev->regmap, vreg->base + REG_LABIBB_STATUS1,
&cur_status);
if (ret)
return ret;
return !(cur_status & LABIBB_STATUS1_VREG_OK_BIT);
}
/**
* qcom_labibb_ocp_recovery_worker - Handle OCP event
* @work: OCP work structure
*
* This is the worker function to handle the Over Current Protection
* hardware event; This will check if the hardware is still
* signaling an over-current condition and will eventually stop
* the regulator if such condition is still signaled after
* LABIBB_MAX_OCP_COUNT times.
*
* If the driver that is consuming the regulator did not take action
* for the OCP condition, or the hardware did not stabilize, a cut
* of the LAB and IBB regulators will be forced (regulators will be
* disabled).
*
* As last, if the writes to shut down the LAB/IBB regulators fail
* for more than LABIBB_MAX_FATAL_COUNT, then a kernel panic will be
* triggered, as a last resort to protect the hardware from burning;
* this, however, is expected to never happen, but this is kept to
* try to further ensure that we protect the hardware at all costs.
*/
static void qcom_labibb_ocp_recovery_worker(struct work_struct *work)
{
struct labibb_regulator *vreg;
const struct regulator_ops *ops;
int ret;
vreg = container_of(work, struct labibb_regulator,
ocp_recovery_work.work);
ops = vreg->rdev->desc->ops;
if (vreg->ocp_irq_count >= LABIBB_MAX_OCP_COUNT) {
/*
* If we tried to disable the regulator multiple times but
* we kept failing, there's only one last hope to save our
* hardware from the death: raise a kernel bug, reboot and
* hope that the bootloader kindly saves us. This, though
* is done only as paranoid checking, because failing the
* regmap write to disable the vreg is almost impossible,
* since we got here after multiple regmap R/W.
*/
BUG_ON(vreg->fatal_count > LABIBB_MAX_FATAL_COUNT);
dev_err(&vreg->rdev->dev, "LABIBB: CRITICAL: Disabling regulator\n");
/* Disable the regulator immediately to avoid damage */
ret = ops->disable(vreg->rdev);
if (ret) {
vreg->fatal_count++;
goto reschedule;
}
enable_irq(vreg->ocp_irq);
vreg->fatal_count = 0;
return;
}
ret = qcom_labibb_check_ocp_status(vreg);
if (ret != 0) {
vreg->ocp_irq_count++;
goto reschedule;
}
ret = qcom_labibb_ocp_hw_enable(vreg->rdev);
if (ret) {
/* We cannot trust it without OCP enabled. */
dev_err(vreg->dev, "Cannot enable OCP IRQ\n");
vreg->ocp_irq_count++;
goto reschedule;
}
enable_irq(vreg->ocp_irq);
/* Everything went fine: reset the OCP count! */
vreg->ocp_irq_count = 0;
return;
reschedule:
mod_delayed_work(system_wq, &vreg->ocp_recovery_work,
msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS));
}
/**
* qcom_labibb_ocp_isr - Interrupt routine for OverCurrent Protection
* @irq: Interrupt number
* @chip: Main driver structure
*
* Over Current Protection (OCP) will signal to the client driver
* that an over-current event has happened and then will schedule
* a recovery worker.
*
* Disabling and eventually re-enabling the regulator is expected
* to be done by the driver, as some hardware may be triggering an
* over-current condition only at first initialization or it may
* be expected only for a very brief amount of time, after which
* the attached hardware may be expected to stabilize its current
* draw.
*
* Returns: IRQ_HANDLED for success or IRQ_NONE for failure.
*/
static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip)
{
struct labibb_regulator *vreg = chip;
const struct regulator_ops *ops = vreg->rdev->desc->ops;
int ret;
/* If the regulator is not enabled, this is a fake event */
if (!ops->is_enabled(vreg->rdev))
return 0;
/* If we tried to recover for too many times it's not getting better */
if (vreg->ocp_irq_count > LABIBB_MAX_OCP_COUNT)
return IRQ_NONE;
/*
* If we (unlikely) can't read this register, to prevent hardware
* damage at all costs, we assume that the overcurrent event was
* real; Moreover, if the status register is not signaling OCP,
* it was a spurious event, so it's all ok.
*/
ret = qcom_labibb_check_ocp_status(vreg);
if (ret == 0) {
vreg->ocp_irq_count = 0;
goto end;
}
vreg->ocp_irq_count++;
/*
* Disable the interrupt temporarily, or it will fire continuously;
* we will re-enable it in the recovery worker function.
*/
disable_irq_nosync(irq);
/* Warn the user for overcurrent */
dev_warn(vreg->dev, "Over-Current interrupt fired!\n");
/* Disable the interrupt to avoid hogging */
ret = qcom_labibb_ocp_hw_disable(vreg->rdev);
if (ret)
goto end;
/* Signal overcurrent event to drivers */
regulator_notifier_call_chain(vreg->rdev,
REGULATOR_EVENT_OVER_CURRENT, NULL);
end:
/* Schedule the recovery work */
schedule_delayed_work(&vreg->ocp_recovery_work,
msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS));
if (ret)
return IRQ_NONE;
return IRQ_HANDLED;
}
static int qcom_labibb_set_ocp(struct regulator_dev *rdev)
{
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
char *ocp_irq_name;
u32 irq_flags = IRQF_ONESHOT;
int irq_trig_low, ret;
/* If there is no OCP interrupt, there's nothing to set */
if (vreg->ocp_irq <= 0)
return -EINVAL;
ocp_irq_name = devm_kasprintf(vreg->dev, GFP_KERNEL, "%s-over-current",
vreg->desc.name);
if (!ocp_irq_name)
return -ENOMEM;
/* IRQ polarities - LAB: trigger-low, IBB: trigger-high */
switch (vreg->type) {
case QCOM_LAB_TYPE:
irq_flags |= IRQF_TRIGGER_LOW;
irq_trig_low = 1;
break;
case QCOM_IBB_TYPE:
irq_flags |= IRQF_TRIGGER_HIGH;
irq_trig_low = 0;
break;
default:
return -EINVAL;
}
/* Activate OCP HW level interrupt */
ret = regmap_update_bits(rdev->regmap,
vreg->base + REG_LABIBB_INT_SET_TYPE,
LABIBB_INT_VREG_OK,
LABIBB_INT_VREG_TYPE_LEVEL);
if (ret)
return ret;
/* Set OCP interrupt polarity */
ret = regmap_update_bits(rdev->regmap,
vreg->base + REG_LABIBB_INT_POLARITY_HIGH,
LABIBB_INT_VREG_OK, !irq_trig_low);
if (ret)
return ret;
ret = regmap_update_bits(rdev->regmap,
vreg->base + REG_LABIBB_INT_POLARITY_LOW,
LABIBB_INT_VREG_OK, irq_trig_low);
if (ret)
return ret;
ret = qcom_labibb_ocp_hw_enable(rdev);
if (ret)
return ret;
return devm_request_threaded_irq(vreg->dev, vreg->ocp_irq, NULL,
qcom_labibb_ocp_isr, irq_flags,
ocp_irq_name, vreg);
}
/**
* qcom_labibb_check_sc_status - Check the Short Circuit Protection status
* @vreg: Main driver structure
*
* This function checks the STATUS1 register on both LAB and IBB regulators
* for the ShortCircuit bit: if it is set on *any* of them, then we have
* experienced a short-circuit event.
*
* Returns: Zero if there is no short-circuit, 1 if in short-circuit or
* negative number for error
*/
static int qcom_labibb_check_sc_status(struct labibb_regulator *vreg)
{
u32 ibb_status, ibb_reg, lab_status, lab_reg;
int ret;
/* We have to work on both regulators due to PBS... */
lab_reg = ibb_reg = vreg->base + REG_LABIBB_STATUS1;
if (vreg->type == QCOM_LAB_TYPE)
ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET;
else
lab_reg += PMI8998_IBB_LAB_REG_OFFSET;
ret = regmap_read(vreg->rdev->regmap, lab_reg, &lab_status);
if (ret)
return ret;
ret = regmap_read(vreg->rdev->regmap, ibb_reg, &ibb_status);
if (ret)
return ret;
return !!(lab_status & LABIBB_STATUS1_SC_BIT) ||
!!(ibb_status & LABIBB_STATUS1_SC_BIT);
}
/**
* qcom_labibb_sc_recovery_worker - Handle Short Circuit event
* @work: SC work structure
*
* This is the worker function to handle the Short Circuit Protection
* hardware event; This will check if the hardware is still
* signaling a short-circuit condition and will eventually never
* re-enable the regulator if such condition is still signaled after
* LABIBB_MAX_SC_COUNT times.
*
* If the driver that is consuming the regulator did not take action
* for the SC condition, or the hardware did not stabilize, this
* worker will stop rescheduling, leaving the regulators disabled
* as already done by the Portable Batch System (PBS).
*
* Returns: IRQ_HANDLED for success or IRQ_NONE for failure.
*/
static void qcom_labibb_sc_recovery_worker(struct work_struct *work)
{
struct labibb_regulator *vreg;
const struct regulator_ops *ops;
u32 lab_reg, ibb_reg, lab_val, ibb_val, val;
bool pbs_cut = false;
int i, sc, ret;
vreg = container_of(work, struct labibb_regulator,
sc_recovery_work.work);
ops = vreg->rdev->desc->ops;
/*
* If we tried to check the regulator status multiple times but we
* kept failing, then just bail out, as the Portable Batch System
* (PBS) will disable the vregs for us, preventing hardware damage.
*/
if (vreg->fatal_count > LABIBB_MAX_FATAL_COUNT)
return;
/* Too many short-circuit events. Throw in the towel. */
if (vreg->sc_count > LABIBB_MAX_SC_COUNT)
return;
/*
* The Portable Batch System (PBS) automatically disables LAB
* and IBB when a short-circuit event is detected, so we have to
* check and work on both of them at the same time.
*/
lab_reg = ibb_reg = vreg->base + REG_LABIBB_ENABLE_CTL;
if (vreg->type == QCOM_LAB_TYPE)
ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET;
else
lab_reg += PMI8998_IBB_LAB_REG_OFFSET;
sc = qcom_labibb_check_sc_status(vreg);
if (sc)
goto reschedule;
for (i = 0; i < LABIBB_MAX_SC_COUNT; i++) {
ret = regmap_read(vreg->regmap, lab_reg, &lab_val);
if (ret) {
vreg->fatal_count++;
goto reschedule;
}
ret = regmap_read(vreg->regmap, ibb_reg, &ibb_val);
if (ret) {
vreg->fatal_count++;
goto reschedule;
}
val = lab_val & ibb_val;
if (!(val & LABIBB_CONTROL_ENABLE)) {
pbs_cut = true;
break;
}
usleep_range(5000, 6000);
}
if (pbs_cut)
goto reschedule;
/*
* If we have reached this point, we either have successfully
* recovered from the SC condition or we had a spurious SC IRQ,
* which means that we can re-enable the regulators, if they
* have ever been disabled by the PBS.
*/
ret = ops->enable(vreg->rdev);
if (ret)
goto reschedule;
/* Everything went fine: reset the OCP count! */
vreg->sc_count = 0;
enable_irq(vreg->sc_irq);
return;
reschedule:
/*
* Now that we have done basic handling of the short-circuit,
* reschedule this worker in the regular system workqueue, as
* taking action is not truly urgent anymore.
*/
vreg->sc_count++;
mod_delayed_work(system_wq, &vreg->sc_recovery_work,
msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS));
}
/**
* qcom_labibb_sc_isr - Interrupt routine for Short Circuit Protection
* @irq: Interrupt number
* @chip: Main driver structure
*
* Short Circuit Protection (SCP) will signal to the client driver
* that a regulation-out event has happened and then will schedule
* a recovery worker.
*
* The LAB and IBB regulators will be automatically disabled by the
* Portable Batch System (PBS) and they will be enabled again by
* the worker function if the hardware stops signaling the short
* circuit event.
*
* Returns: IRQ_HANDLED for success or IRQ_NONE for failure.
*/
static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip)
{
struct labibb_regulator *vreg = chip;
if (vreg->sc_count > LABIBB_MAX_SC_COUNT)
return IRQ_NONE;
/* Warn the user for short circuit */
dev_warn(vreg->dev, "Short-Circuit interrupt fired!\n");
/*
* Disable the interrupt temporarily, or it will fire continuously;
* we will re-enable it in the recovery worker function.
*/
disable_irq_nosync(irq);
/* Signal out of regulation event to drivers */
regulator_notifier_call_chain(vreg->rdev,
REGULATOR_EVENT_REGULATION_OUT, NULL);
/* Schedule the short-circuit handling as high-priority work */
mod_delayed_work(system_highpri_wq, &vreg->sc_recovery_work,
msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS));
return IRQ_HANDLED;
}
static int qcom_labibb_set_current_limit(struct regulator_dev *rdev,
int min_uA, int max_uA)
{
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
struct regulator_desc *desc = &vreg->desc;
struct labibb_current_limits *lim = &vreg->uA_limits;
u32 mask, val;
int i, ret, sel = -1;
if (min_uA < lim->uA_min || max_uA < lim->uA_min)
return -EINVAL;
for (i = 0; i < desc->n_current_limits; i++) {
int uA_limit = (lim->uA_step * i) + lim->uA_min;
if (max_uA >= uA_limit && min_uA <= uA_limit)
sel = i;
}
if (sel < 0)
return -EINVAL;
/* Current limit setting needs secure access */
ret = regmap_write(vreg->regmap, vreg->base + REG_LABIBB_SEC_ACCESS,
LABIBB_SEC_UNLOCK_CODE);
if (ret)
return ret;
mask = desc->csel_mask | lim->ovr_val;
mask |= LABIBB_CURRENT_LIMIT_EN;
val = (u32)sel | lim->ovr_val;
val |= LABIBB_CURRENT_LIMIT_EN;
return regmap_update_bits(vreg->regmap, desc->csel_reg, mask, val);
}
static int qcom_labibb_get_current_limit(struct regulator_dev *rdev)
{
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
struct regulator_desc *desc = &vreg->desc;
struct labibb_current_limits *lim = &vreg->uA_limits;
unsigned int cur_step;
int ret;
ret = regmap_read(vreg->regmap, desc->csel_reg, &cur_step);
if (ret)
return ret;
cur_step &= desc->csel_mask;
return (cur_step * lim->uA_step) + lim->uA_min;
}
static int qcom_labibb_set_soft_start(struct regulator_dev *rdev)
{
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
u32 val = 0;
if (vreg->type == QCOM_IBB_TYPE)
val = vreg->dischg_sel;
else
val = vreg->soft_start_sel;
return regmap_write(rdev->regmap, rdev->desc->soft_start_reg, val);
}
static int qcom_labibb_get_table_sel(const int *table, int sz, u32 value)
{
int i;
for (i = 0; i < sz; i++)
if (table[i] == value)
return i;
return -EINVAL;
}
/* IBB discharge resistor values in KOhms */
static const int dischg_resistor_values[] = { 300, 64, 32, 16 };
/* Soft start time in microseconds */
static const int soft_start_values[] = { 200, 400, 600, 800 };
static int qcom_labibb_of_parse_cb(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *config)
{
struct labibb_regulator *vreg = config->driver_data;
u32 dischg_kohms, soft_start_time;
int ret;
ret = of_property_read_u32(np, "qcom,discharge-resistor-kohms",
&dischg_kohms);
if (ret)
dischg_kohms = 300;
ret = qcom_labibb_get_table_sel(dischg_resistor_values,
ARRAY_SIZE(dischg_resistor_values),
dischg_kohms);
if (ret < 0)
return ret;
vreg->dischg_sel = (u8)ret;
ret = of_property_read_u32(np, "qcom,soft-start-us",
&soft_start_time);
if (ret)
soft_start_time = 200;
ret = qcom_labibb_get_table_sel(soft_start_values,
ARRAY_SIZE(soft_start_values),
soft_start_time);
if (ret < 0)
return ret;
vreg->soft_start_sel = (u8)ret;
return 0;
}
static const struct regulator_ops qcom_labibb_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,
.map_voltage = regulator_map_voltage_linear,
.set_active_discharge = regulator_set_active_discharge_regmap,
.set_pull_down = regulator_set_pull_down_regmap,
.set_current_limit = qcom_labibb_set_current_limit,
.get_current_limit = qcom_labibb_get_current_limit,
.set_soft_start = qcom_labibb_set_soft_start,
.set_over_current_protection = qcom_labibb_set_ocp,
};
static const struct regulator_desc pmi8998_lab_desc = {
@ -59,10 +686,25 @@ static const struct regulator_desc pmi8998_lab_desc = {
.enable_val = LABIBB_CONTROL_ENABLE,
.enable_time = LAB_ENABLE_TIME,
.poll_enabled_time = LABIBB_POLL_ENABLED_TIME,
.soft_start_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_SOFT_START_CTL),
.pull_down_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_PD_CTL),
.pull_down_mask = LAB_PD_CTL_MASK,
.pull_down_val_on = LAB_PD_CTL_STRONG_PULL,
.vsel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE),
.vsel_mask = LAB_VOLTAGE_SET_MASK,
.apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE),
.apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN,
.csel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_CURRENT_LIMIT),
.csel_mask = LAB_CURRENT_LIMIT_MASK,
.n_current_limits = 8,
.off_on_delay = LABIBB_OFF_ON_DELAY,
.owner = THIS_MODULE,
.type = REGULATOR_VOLTAGE,
.min_uV = 4600000,
.uV_step = 100000,
.n_voltages = 16,
.ops = &qcom_labibb_ops,
.of_parse_cb = qcom_labibb_of_parse_cb,
};
static const struct regulator_desc pmi8998_ibb_desc = {
@ -71,10 +713,29 @@ static const struct regulator_desc pmi8998_ibb_desc = {
.enable_val = LABIBB_CONTROL_ENABLE,
.enable_time = IBB_ENABLE_TIME,
.poll_enabled_time = LABIBB_POLL_ENABLED_TIME,
.soft_start_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_SOFT_START_CTL),
.active_discharge_off = 0,
.active_discharge_on = IBB_CTL_1_DISCHARGE_EN,
.active_discharge_mask = IBB_CTL_1_DISCHARGE_EN,
.active_discharge_reg = (PMI8998_IBB_REG_BASE + REG_IBB_PWRUP_PWRDN_CTL_1),
.pull_down_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_PD_CTL),
.pull_down_mask = IBB_PD_CTL_MASK,
.pull_down_val_on = IBB_PD_CTL_HALF_STRENGTH | IBB_PD_CTL_EN,
.vsel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE),
.vsel_mask = IBB_VOLTAGE_SET_MASK,
.apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE),
.apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN,
.csel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_CURRENT_LIMIT),
.csel_mask = IBB_CURRENT_LIMIT_MASK,
.n_current_limits = 32,
.off_on_delay = LABIBB_OFF_ON_DELAY,
.owner = THIS_MODULE,
.type = REGULATOR_VOLTAGE,
.min_uV = 1400000,
.uV_step = 100000,
.n_voltages = 64,
.ops = &qcom_labibb_ops,
.of_parse_cb = qcom_labibb_of_parse_cb,
};
static const struct labibb_regulator_data pmi8998_labibb_data[] = {
@ -94,7 +755,7 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev)
struct labibb_regulator *vreg;
struct device *dev = &pdev->dev;
struct regulator_config cfg = {};
struct device_node *reg_node;
const struct of_device_id *match;
const struct labibb_regulator_data *reg_data;
struct regmap *reg_regmap;
@ -112,6 +773,8 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev)
return -ENODEV;
for (reg_data = match->data; reg_data->name; reg_data++) {
char *sc_irq_name;
int irq = 0;
/* Validate if the type of regulator is indeed
* what's mentioned in DT.
@ -134,10 +797,61 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev)
if (!vreg)
return -ENOMEM;
sc_irq_name = devm_kasprintf(dev, GFP_KERNEL,
"%s-short-circuit",
reg_data->name);
if (!sc_irq_name)
return -ENOMEM;
reg_node = of_get_child_by_name(pdev->dev.of_node,
reg_data->name);
if (!reg_node)
return -EINVAL;
/* The Short Circuit interrupt is critical */
irq = of_irq_get_byname(reg_node, "sc-err");
if (irq <= 0) {
if (irq == 0)
irq = -EINVAL;
return dev_err_probe(vreg->dev, irq,
"Short-circuit irq not found.\n");
}
vreg->sc_irq = irq;
/* OverCurrent Protection IRQ is optional */
irq = of_irq_get_byname(reg_node, "ocp");
vreg->ocp_irq = irq;
vreg->ocp_irq_count = 0;
of_node_put(reg_node);
vreg->regmap = reg_regmap;
vreg->dev = dev;
vreg->base = reg_data->base;
vreg->type = reg_data->type;
INIT_DELAYED_WORK(&vreg->sc_recovery_work,
qcom_labibb_sc_recovery_worker);
if (vreg->ocp_irq > 0)
INIT_DELAYED_WORK(&vreg->ocp_recovery_work,
qcom_labibb_ocp_recovery_worker);
switch (vreg->type) {
case QCOM_LAB_TYPE:
/* LAB Limits: 200-1600mA */
vreg->uA_limits.uA_min = 200000;
vreg->uA_limits.uA_step = 200000;
vreg->uA_limits.ovr_val = LAB_CURRENT_LIMIT_OVERRIDE_EN;
break;
case QCOM_IBB_TYPE:
/* IBB Limits: 0-1550mA */
vreg->uA_limits.uA_min = 0;
vreg->uA_limits.uA_step = 50000;
vreg->uA_limits.ovr_val = 0; /* No override bit */
break;
default:
return -EINVAL;
}
memcpy(&vreg->desc, reg_data->desc, sizeof(vreg->desc));
vreg->desc.of_match = reg_data->name;
@ -155,6 +869,14 @@ static int qcom_labibb_regulator_probe(struct platform_device *pdev)
reg_data->name, ret);
return PTR_ERR(vreg->rdev);
}
ret = devm_request_threaded_irq(vreg->dev, vreg->sc_irq, NULL,
qcom_labibb_sc_isr,
IRQF_ONESHOT |
IRQF_TRIGGER_RISING,
sc_irq_name, vreg);
if (ret)
return ret;
}
return 0;

View File

@ -732,6 +732,15 @@ static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = {
.of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
};
static const struct rpmh_vreg_hw_data pmic5_hfsmps515_1 = {
.regulator_type = VRM,
.ops = &rpmh_regulator_vrm_ops,
.voltage_range = REGULATOR_LINEAR_RANGE(900000, 0, 4, 16000),
.n_voltages = 5,
.pmic_mode_map = pmic_mode_map_pmic5_smps,
.of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
};
static const struct rpmh_vreg_hw_data pmic5_bob = {
.regulator_type = VRM,
.ops = &rpmh_regulator_vrm_bypass_ops,
@ -932,6 +941,19 @@ static const struct rpmh_vreg_init_data pm8009_vreg_data[] = {
{},
};
static const struct rpmh_vreg_init_data pm8009_1_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515_1, "vdd-s2"),
RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"),
RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"),
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"),
RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"),
RPMH_VREG("ldo7", "ldo%s6", &pmic5_pldo_lv, "vdd-l7"),
{},
};
static const struct rpmh_vreg_init_data pm6150_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
@ -1057,6 +1079,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
.compatible = "qcom,pm8009-rpmh-regulators",
.data = pm8009_vreg_data,
},
{
.compatible = "qcom,pm8009-1-rpmh-regulators",
.data = pm8009_1_vreg_data,
},
{
.compatible = "qcom,pm8150-rpmh-regulators",
.data = pm8150_vreg_data,
@ -1089,6 +1115,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
.compatible = "qcom,pm6150l-rpmh-regulators",
.data = pm6150l_vreg_data,
},
{
.compatible = "qcom,pmc8180-rpmh-regulators",
.data = pm8150_vreg_data,
},
{
.compatible = "qcom,pmc8180c-rpmh-regulators",
.data = pm8150l_vreg_data,
},
{
.compatible = "qcom,pmx55-rpmh-regulators",
.data = pmx55_vreg_data,

View File

@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
enum {
DSV_OUT_VLCM = 0,
DSV_OUT_VPOS,
DSV_OUT_VNEG,
DSV_OUT_MAX
};
#define RT4831_REG_DSVEN 0x09
#define RT4831_REG_VLCM 0x0c
#define RT4831_REG_VPOS 0x0d
#define RT4831_REG_VNEG 0x0e
#define RT4831_REG_FLAGS 0x0f
#define RT4831_VOLT_MASK GENMASK(5, 0)
#define RT4831_DSVMODE_SHIFT 5
#define RT4831_DSVMODE_MASK GENMASK(7, 5)
#define RT4831_POSADEN_MASK BIT(4)
#define RT4831_NEGADEN_MASK BIT(3)
#define RT4831_POSEN_MASK BIT(2)
#define RT4831_NEGEN_MASK BIT(1)
#define RT4831_OTP_MASK BIT(6)
#define RT4831_LCMOVP_MASK BIT(5)
#define RT4831_VPOSSCP_MASK BIT(3)
#define RT4831_VNEGSCP_MASK BIT(2)
#define DSV_MODE_NORMAL (0x4 << RT4831_DSVMODE_SHIFT)
#define DSV_MODE_BYPASS (0x6 << RT4831_DSVMODE_SHIFT)
#define STEP_UV 50000
#define VLCM_MIN_UV 4000000
#define VLCM_MAX_UV 7150000
#define VLCM_N_VOLTAGES ((VLCM_MAX_UV - VLCM_MIN_UV) / STEP_UV + 1)
#define VPN_MIN_UV 4000000
#define VPN_MAX_UV 6500000
#define VPN_N_VOLTAGES ((VPN_MAX_UV - VPN_MIN_UV) / STEP_UV + 1)
static int rt4831_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 val, events = 0;
int ret;
ret = regmap_read(regmap, RT4831_REG_FLAGS, &val);
if (ret)
return ret;
if (val & RT4831_OTP_MASK)
events |= REGULATOR_ERROR_OVER_TEMP;
if (rid == DSV_OUT_VLCM && (val & RT4831_LCMOVP_MASK))
events |= REGULATOR_ERROR_OVER_CURRENT;
if (rid == DSV_OUT_VPOS && (val & RT4831_VPOSSCP_MASK))
events |= REGULATOR_ERROR_OVER_CURRENT;
if (rid == DSV_OUT_VNEG && (val & RT4831_VNEGSCP_MASK))
events |= REGULATOR_ERROR_OVER_CURRENT;
*flags = events;
return 0;
}
static const struct regulator_ops rt4831_dsvlcm_ops = {
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_bypass = regulator_set_bypass_regmap,
.get_bypass = regulator_get_bypass_regmap,
.get_error_flags = rt4831_get_error_flags,
};
static const struct regulator_ops rt4831_dsvpn_ops = {
.list_voltage = regulator_list_voltage_linear,
.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,
.get_error_flags = rt4831_get_error_flags,
};
static const struct regulator_desc rt4831_regulator_descs[] = {
{
.name = "DSVLCM",
.ops = &rt4831_dsvlcm_ops,
.of_match = of_match_ptr("DSVLCM"),
.regulators_node = of_match_ptr("regulators"),
.type = REGULATOR_VOLTAGE,
.id = DSV_OUT_VLCM,
.n_voltages = VLCM_N_VOLTAGES,
.min_uV = VLCM_MIN_UV,
.uV_step = STEP_UV,
.vsel_reg = RT4831_REG_VLCM,
.vsel_mask = RT4831_VOLT_MASK,
.bypass_reg = RT4831_REG_DSVEN,
.bypass_val_on = DSV_MODE_BYPASS,
.bypass_val_off = DSV_MODE_NORMAL,
},
{
.name = "DSVP",
.ops = &rt4831_dsvpn_ops,
.of_match = of_match_ptr("DSVP"),
.regulators_node = of_match_ptr("regulators"),
.type = REGULATOR_VOLTAGE,
.id = DSV_OUT_VPOS,
.n_voltages = VPN_N_VOLTAGES,
.min_uV = VPN_MIN_UV,
.uV_step = STEP_UV,
.vsel_reg = RT4831_REG_VPOS,
.vsel_mask = RT4831_VOLT_MASK,
.enable_reg = RT4831_REG_DSVEN,
.enable_mask = RT4831_POSEN_MASK,
.active_discharge_reg = RT4831_REG_DSVEN,
.active_discharge_mask = RT4831_POSADEN_MASK,
},
{
.name = "DSVN",
.ops = &rt4831_dsvpn_ops,
.of_match = of_match_ptr("DSVN"),
.regulators_node = of_match_ptr("regulators"),
.type = REGULATOR_VOLTAGE,
.id = DSV_OUT_VNEG,
.n_voltages = VPN_N_VOLTAGES,
.min_uV = VPN_MIN_UV,
.uV_step = STEP_UV,
.vsel_reg = RT4831_REG_VNEG,
.vsel_mask = RT4831_VOLT_MASK,
.enable_reg = RT4831_REG_DSVEN,
.enable_mask = RT4831_NEGEN_MASK,
.active_discharge_reg = RT4831_REG_DSVEN,
.active_discharge_mask = RT4831_NEGADEN_MASK,
}
};
static int rt4831_regulator_probe(struct platform_device *pdev)
{
struct regmap *regmap;
struct regulator_dev *rdev;
struct regulator_config config = {};
int i, ret;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (IS_ERR(regmap)) {
dev_err(&pdev->dev, "Failed to init regmap\n");
return PTR_ERR(regmap);
}
/* Configure DSV mode to normal by default */
ret = regmap_update_bits(regmap, RT4831_REG_DSVEN, RT4831_DSVMODE_MASK, DSV_MODE_NORMAL);
if (ret) {
dev_err(&pdev->dev, "Failed to configure dsv mode to normal\n");
return ret;
}
config.dev = pdev->dev.parent;
config.regmap = regmap;
for (i = 0; i < DSV_OUT_MAX; i++) {
rdev = devm_regulator_register(&pdev->dev, rt4831_regulator_descs + i, &config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "Failed to register %d regulator\n", i);
return PTR_ERR(rdev);
}
}
return 0;
}
static const struct platform_device_id rt4831_regulator_match[] = {
{ "rt4831-regulator", 0 },
{}
};
MODULE_DEVICE_TABLE(platform, rt4831_regulator_match);
static struct platform_driver rt4831_regulator_driver = {
.driver = {
.name = "rt4831-regulator",
},
.id_table = rt4831_regulator_match,
.probe = rt4831_regulator_probe,
};
module_platform_driver(rt4831_regulator_driver);
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_LICENSE("GPL v2");

View File

@ -544,14 +544,18 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev,
rdata = devm_kcalloc(&pdev->dev,
pdata->num_regulators, sizeof(*rdata),
GFP_KERNEL);
if (!rdata)
if (!rdata) {
of_node_put(regulators_np);
return -ENOMEM;
}
rmode = devm_kcalloc(&pdev->dev,
pdata->num_regulators, sizeof(*rmode),
GFP_KERNEL);
if (!rmode)
if (!rmode) {
of_node_put(regulators_np);
return -ENOMEM;
}
pdata->regulators = rdata;
pdata->opmode = rmode;
@ -573,10 +577,13 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev,
"s5m8767,pmic-ext-control",
GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
"s5m8767");
if (PTR_ERR(rdata->ext_control_gpiod) == -ENOENT)
if (PTR_ERR(rdata->ext_control_gpiod) == -ENOENT) {
rdata->ext_control_gpiod = NULL;
else if (IS_ERR(rdata->ext_control_gpiod))
} else if (IS_ERR(rdata->ext_control_gpiod)) {
of_node_put(reg_np);
of_node_put(regulators_np);
return PTR_ERR(rdata->ext_control_gpiod);
}
rdata->id = i;
rdata->initdata = of_get_regulator_init_data(

View File

@ -368,7 +368,6 @@ struct ab8500 {
int it_latchhier_num;
};
struct ab8500_regulator_platform_data;
struct ab8500_codec_platform_data;
struct ab8500_sysctrl_platform_data;
@ -376,11 +375,9 @@ struct ab8500_sysctrl_platform_data;
* struct ab8500_platform_data - AB8500 platform data
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
* @init: board-specific initialization after detection of ab8500
* @regulator: machine-specific constraints for regulators
*/
struct ab8500_platform_data {
void (*init) (struct ab8500 *);
struct ab8500_regulator_platform_data *regulator;
struct ab8500_codec_platform_data *codec;
struct ab8500_sysctrl_platform_data *sysctrl;
};

View File

@ -1,166 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) ST-Ericsson SA 2010
*
* Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
* Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
* Daniel Willerud <daniel.willerud@stericsson.com> for ST-Ericsson
*/
#ifndef __LINUX_MFD_AB8500_REGULATOR_H
#define __LINUX_MFD_AB8500_REGULATOR_H
#include <linux/platform_device.h>
/* AB8500 regulators */
enum ab8500_regulator_id {
AB8500_LDO_AUX1,
AB8500_LDO_AUX2,
AB8500_LDO_AUX3,
AB8500_LDO_INTCORE,
AB8500_LDO_TVOUT,
AB8500_LDO_AUDIO,
AB8500_LDO_ANAMIC1,
AB8500_LDO_ANAMIC2,
AB8500_LDO_DMIC,
AB8500_LDO_ANA,
AB8500_NUM_REGULATORS,
};
/* AB8505 regulators */
enum ab8505_regulator_id {
AB8505_LDO_AUX1,
AB8505_LDO_AUX2,
AB8505_LDO_AUX3,
AB8505_LDO_AUX4,
AB8505_LDO_AUX5,
AB8505_LDO_AUX6,
AB8505_LDO_INTCORE,
AB8505_LDO_ADC,
AB8505_LDO_AUDIO,
AB8505_LDO_ANAMIC1,
AB8505_LDO_ANAMIC2,
AB8505_LDO_AUX8,
AB8505_LDO_ANA,
AB8505_NUM_REGULATORS,
};
/* AB8500 and AB8505 register initialization */
struct ab8500_regulator_reg_init {
int id;
u8 mask;
u8 value;
};
#define INIT_REGULATOR_REGISTER(_id, _mask, _value) \
{ \
.id = _id, \
.mask = _mask, \
.value = _value, \
}
/* AB8500 registers */
enum ab8500_regulator_reg {
AB8500_REGUREQUESTCTRL2,
AB8500_REGUREQUESTCTRL3,
AB8500_REGUREQUESTCTRL4,
AB8500_REGUSYSCLKREQ1HPVALID1,
AB8500_REGUSYSCLKREQ1HPVALID2,
AB8500_REGUHWHPREQ1VALID1,
AB8500_REGUHWHPREQ1VALID2,
AB8500_REGUHWHPREQ2VALID1,
AB8500_REGUHWHPREQ2VALID2,
AB8500_REGUSWHPREQVALID1,
AB8500_REGUSWHPREQVALID2,
AB8500_REGUSYSCLKREQVALID1,
AB8500_REGUSYSCLKREQVALID2,
AB8500_REGUMISC1,
AB8500_VAUDIOSUPPLY,
AB8500_REGUCTRL1VAMIC,
AB8500_VPLLVANAREGU,
AB8500_VREFDDR,
AB8500_EXTSUPPLYREGU,
AB8500_VAUX12REGU,
AB8500_VRF1VAUX3REGU,
AB8500_VAUX1SEL,
AB8500_VAUX2SEL,
AB8500_VRF1VAUX3SEL,
AB8500_REGUCTRL2SPARE,
AB8500_REGUCTRLDISCH,
AB8500_REGUCTRLDISCH2,
AB8500_NUM_REGULATOR_REGISTERS,
};
/* AB8505 registers */
enum ab8505_regulator_reg {
AB8505_REGUREQUESTCTRL1,
AB8505_REGUREQUESTCTRL2,
AB8505_REGUREQUESTCTRL3,
AB8505_REGUREQUESTCTRL4,
AB8505_REGUSYSCLKREQ1HPVALID1,
AB8505_REGUSYSCLKREQ1HPVALID2,
AB8505_REGUHWHPREQ1VALID1,
AB8505_REGUHWHPREQ1VALID2,
AB8505_REGUHWHPREQ2VALID1,
AB8505_REGUHWHPREQ2VALID2,
AB8505_REGUSWHPREQVALID1,
AB8505_REGUSWHPREQVALID2,
AB8505_REGUSYSCLKREQVALID1,
AB8505_REGUSYSCLKREQVALID2,
AB8505_REGUVAUX4REQVALID,
AB8505_REGUMISC1,
AB8505_VAUDIOSUPPLY,
AB8505_REGUCTRL1VAMIC,
AB8505_VSMPSAREGU,
AB8505_VSMPSBREGU,
AB8505_VSAFEREGU, /* NOTE! PRCMU register */
AB8505_VPLLVANAREGU,
AB8505_EXTSUPPLYREGU,
AB8505_VAUX12REGU,
AB8505_VRF1VAUX3REGU,
AB8505_VSMPSASEL1,
AB8505_VSMPSASEL2,
AB8505_VSMPSASEL3,
AB8505_VSMPSBSEL1,
AB8505_VSMPSBSEL2,
AB8505_VSMPSBSEL3,
AB8505_VSAFESEL1, /* NOTE! PRCMU register */
AB8505_VSAFESEL2, /* NOTE! PRCMU register */
AB8505_VSAFESEL3, /* NOTE! PRCMU register */
AB8505_VAUX1SEL,
AB8505_VAUX2SEL,
AB8505_VRF1VAUX3SEL,
AB8505_VAUX4REQCTRL,
AB8505_VAUX4REGU,
AB8505_VAUX4SEL,
AB8505_REGUCTRLDISCH,
AB8505_REGUCTRLDISCH2,
AB8505_REGUCTRLDISCH3,
AB8505_CTRLVAUX5,
AB8505_CTRLVAUX6,
AB8505_NUM_REGULATOR_REGISTERS,
};
/* AB8500 external regulators */
struct ab8500_ext_regulator_cfg {
bool hwreq; /* requires hw mode or high power mode */
};
enum ab8500_ext_regulator_id {
AB8500_EXT_SUPPLY1,
AB8500_EXT_SUPPLY2,
AB8500_EXT_SUPPLY3,
AB8500_NUM_EXT_REGULATORS,
};
/* AB8500 regulator platform data */
struct ab8500_regulator_platform_data {
int num_reg_init;
struct ab8500_regulator_reg_init *reg_init;
int num_regulator;
struct regulator_init_data *regulator;
int num_ext_regulator;
struct regulator_init_data *ext_regulator;
};
#endif

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __LINUX_REGULATOR_MT6315_H
#define __LINUX_REGULATOR_MT6315_H
#define MT6315_RP 3
#define MT6315_PP 6
#define MT6315_SP 7
enum {
MT6315_VBUCK1 = 0,
MT6315_VBUCK2,
MT6315_VBUCK3,
MT6315_VBUCK4,
MT6315_VBUCK_MAX,
};
/* Register */
#define MT6315_TOP2_ELR7 0x139
#define MT6315_TOP_TMA_KEY 0x39F
#define MT6315_TOP_TMA_KEY_H 0x3A0
#define MT6315_BUCK_TOP_CON0 0x1440
#define MT6315_BUCK_TOP_CON1 0x1443
#define MT6315_BUCK_TOP_ELR0 0x1449
#define MT6315_BUCK_TOP_ELR2 0x144B
#define MT6315_BUCK_TOP_ELR4 0x144D
#define MT6315_BUCK_TOP_ELR6 0x144F
#define MT6315_VBUCK1_DBG0 0x1499
#define MT6315_VBUCK1_DBG4 0x149D
#define MT6315_VBUCK2_DBG0 0x1519
#define MT6315_VBUCK2_DBG4 0x151D
#define MT6315_VBUCK3_DBG0 0x1599
#define MT6315_VBUCK3_DBG4 0x159D
#define MT6315_VBUCK4_DBG0 0x1619
#define MT6315_VBUCK4_DBG4 0x161D
#define MT6315_BUCK_TOP_4PHASE_ANA_CON42 0x16B1
#define PROTECTION_KEY_H 0x9C
#define PROTECTION_KEY 0xEA
#endif /* __LINUX_REGULATOR_MT6315_H */

View File

@ -216,4 +216,11 @@ enum {
#define IRQ_THERM_105 0x02
#define IRQ_THERM_125 0x01
/* PCA9450_REG_RESET_CTRL bits */
#define WDOG_B_CFG_MASK 0xC0
#define WDOG_B_CFG_NONE 0x00
#define WDOG_B_CFG_WARM 0x40
#define WDOG_B_CFG_COLD_LDO12 0x80
#define WDOG_B_CFG_COLD 0xC0
#endif /* __LINUX_REG_PCA9450_H__ */

View File

@ -128,7 +128,7 @@ EXPORT_SYMBOL_GPL(linear_range_get_value_array);
* @selector: address where found selector value is updated
* @found: flag to indicate that given value was in the range
*
* Return selector which which range value is closest match for given
* Return selector for which range value is closest match for given
* input value. Value is matching if it is equal or smaller than given
* value. If given value is in the range, then @found is set true.
*
@ -168,11 +168,11 @@ EXPORT_SYMBOL_GPL(linear_range_get_selector_low);
* @selector: address where found selector value is updated
* @found: flag to indicate that given value was in the range
*
* Scan array of ranges for selector which which range value matches given
* Scan array of ranges for selector for which range value matches given
* input value. Value is matching if it is equal or smaller than given
* value. If given value is found to be in a range scanning is stopped and
* @found is set true. If a range with values smaller than given value is found
* but the range max is being smaller than given value, then the ranges
* but the range max is being smaller than given value, then the range's
* biggest selector is updated to @selector but scanning ranges is continued
* and @found is set to false.
*
@ -209,7 +209,7 @@ EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array);
* @selector: address where found selector value is updated
* @found: flag to indicate that given value was in the range
*
* Return selector which which range value is closest match for given
* Return selector for which range value is closest match for given
* input value. Value is matching if it is equal or higher than given
* value. If given value is in the range, then @found is set true.
*