mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Merge remote-tracking branch 'regulator/for-5.12' into regulator-next
This commit is contained in:
commit
f03e2a72e5
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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$":
|
||||
|
@ -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"
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -122,7 +122,7 @@
|
||||
|
||||
&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 @@
|
||||
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>;
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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(®->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,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_err(®->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,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_err(®->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, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->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(®->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 = ®ulator_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 = ®ulator_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 = ®ulator_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 = ®ulator_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 = ®ulator_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 = ®ulator_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 = ®ulator_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 = ®ulator_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 = ®ulator_ops_external,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "BUCK",
|
||||
.id = AB3100_BUCK,
|
||||
.ops = ®ulator_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");
|
@ -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,
|
||||
|
@ -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.
|
||||
|
539
drivers/regulator/atc260x-regulator.c
Normal file
539
drivers/regulator/atc260x-regulator.c
Normal 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,
|
||||
®ulators[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");
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -1554,7 +1554,7 @@ err_out:
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
299
drivers/regulator/mt6315-regulator.c
Normal file
299
drivers/regulator/mt6315-regulator.c
Normal 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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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");
|
215
drivers/regulator/mtk-dvfsrc-regulator.c
Normal file
215
drivers/regulator/mtk-dvfsrc-regulator.c
Normal 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 = ®ulator_mt8183_data,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8192-dvfsrc",
|
||||
.data = ®ulator_mt6873_data,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6873-dvfsrc",
|
||||
.data = ®ulator_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");
|
@ -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");
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
198
drivers/regulator/rt4831-regulator.c
Normal file
198
drivers/regulator/rt4831-regulator.c
Normal 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");
|
@ -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(
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
44
include/linux/regulator/mt6315-regulator.h
Normal file
44
include/linux/regulator/mt6315-regulator.h
Normal 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 */
|
@ -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__ */
|
||||
|
@ -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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user