regulator: Updates for v3.11

Very quiet release here, as well as the usual driver specific updates
 only a couple of new things:
 
  - New drivers for TI ABB LDOs and MAX77693 PMICs.
  - Support for enabling bypass mode support via device tree.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJR0v3PAAoJELSic+t+oim9GJgQAJCT+PBV5HsaYc7lOmJRmCI/
 VHlw/9X8/Q4yv3XHtX0MqXNjzLESS1IdqDp4QS0Hax58+WCdpti7EjtP2uHRqRko
 8zIFgzFutTqWYTTHNMu3MOptH24OrwSOWeHWjRtbDG5q2oIQfOMT2zoFyXc5ZM+W
 1e5p7m90NKc3CQcrscDQVP5gN+wr2EBV01vFGkmbTn6O6h+igpzlgfeSALyab8hS
 QvMZ9KqNyVhJ/WtZXTn37DqjPsbLpHmlDS7MSoWNT/VDXBqlavRv+5CGfyXm2Zba
 p7hN6GXuj8nO1gq4aq7IOtJn9ZjVyPFKyeTm8eMKk0bAHPKnBD0U+/ZMoG7nAiLP
 pd7Tvr3YUQuuVa+W/Z3anFZhm2G/USZ/uTSVYGPBUhUsH/P0XJjDVYGepbv/nxO8
 UT/TUElTfu7Oo44DiDKE/0TLp+L7jRHb38ZL50kBlQIFkEdRShitinnH0ebw/gMw
 Q/w7U2H8QIbSZy1bvwiB868xV6K64KdUgHoczK4WgsNitNDMCqaZB5d5fO3YWhZC
 T8ocnCWtzMKXT3jVxqTvlmaKUXuCeb5d1B16mAexz9gW0Ley/ZFIQfNz+GJd+Yd/
 upUQg/PkRJz73qcARhnnyEg+Q7vRVwPvCypuW54lNxmtscJnmL80HS5qapoUcdeQ
 NGansYsnCSMBC/1O9G0B
 =JgB7
 -----END PGP SIGNATURE-----

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

Pull regulator updates from Mark Brown:
 "Very quiet release here, as well as the usual driver specific updates
  only a couple of new things:

   - New drivers for TI ABB LDOs and MAX77693 PMICs
   - Support for enabling bypass mode support via device tree"

* tag 'regulator-v3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (23 commits)
  regulator: max77693: Remove NULL test for rmatch[i].init_data
  regulator: max77693: Fix trivial typo
  regulator: ab8500-ext: Staticize local symbols
  regulator: max77693: Add max77693 regualtor driver.
  regulator: max8973: fix a typo in documentation
  regulator: max8973: initial DT support
  regulators: max8973: fix multiple instance support
  regulator: of: Added a property to indicate bypass mode support
  regulator: ti-abb: Convert to use devm_ioremap_resource
  regulator: tps62360: Fix crash in i2c_driver .probe
  regulator: ab8500: Provide supply names for the AUX regulators
  regulator: ab8500-ext: Enable for Device Tree
  regulator: ab8500-ext: Register as a device in its own right
  regulator: ab8500-ext: Provide a set_voltage call-back operation
  regulator: ab8500: Ensure AB8500 external registers are probed first
  regulator: core: add regulator_get_linear_step()
  regulator: lp397x: use devm_kzalloc() to make cleanup paths simpler
  regulator: lp872x: support the device tree feature
  regulator: Remove unnecessary include of linux/delay.h from regulator drivers
  regulator: isl6271a: Use NULL instead of 0
  ...
This commit is contained in:
Linus Torvalds 2013-07-03 11:56:38 -07:00
commit 76f7a102c0
41 changed files with 1911 additions and 94 deletions

View File

@ -0,0 +1,55 @@
Maxim MAX77693 multi-function device
MAX77693 is a Multifunction device with the following submodules:
- PMIC,
- CHARGER,
- LED,
- MUIC,
- HAPTIC
It is interfaced to host controller using i2c.
This document describes the bindings for the mfd device.
Required properties:
- compatible : Must be "maxim,max77693".
- reg : Specifies the i2c slave address of PMIC block.
- interrupts : This i2c device has an IRQ line connected to the main SoC.
- interrupt-parent : The parent interrupt controller.
Optional properties:
- regulators : The regulators of max77693 have to be instantiated under subnod
named "regulators" using the following format.
regulators {
regualtor-compatible = ESAFEOUT1/ESAFEOUT2/CHARGER
standard regulator constratints[*].
};
[*] refer Documentation/devicetree/bindings/regulator/regulator.txt
Example:
max77693@66 {
compatible = "maxim,max77693";
reg = <0x66>;
interrupt-parent = <&gpx1>;
interrupts = <5 2>;
regulators {
esafeout@1 {
regulator-compatible = "ESAFEOUT1";
regulator-name = "ESAFEOUT1";
regulator-boot-on;
};
esafeout@2 {
regulator-compatible = "ESAFEOUT2";
regulator-name = "ESAFEOUT2";
};
charger@0 {
regulator-compatible = "CHARGER";
regulator-name = "CHARGER";
regulator-min-microamp = <60000>;
regulator-max-microamp = <2580000>;
regulator-boot-on;
};
};
};

View File

@ -0,0 +1,160 @@
Binding for TI/National Semiconductor LP872x Driver
Required properties:
- compatible: "ti,lp8720" or "ti,lp8725"
- reg: I2C slave address. 0x7d = LP8720, 0x7a = LP8725
Optional properties:
- ti,general-config: the value of LP872X_GENERAL_CFG register (u8)
(LP8720)
bit[2]: BUCK output voltage control by external DVS pin or register
1 = external pin, 0 = bit7 of register 08h
bit[1]: sleep control by external DVS pin or register
1 = external pin, 0 = bit6 of register 08h
bit[0]: time step unit(usec). 1 = 25, 0 = 50
(LP8725)
bit[7:6]: time step unit(usec). 00 = 32, 01 = 64, 10 = 128, 11 = 256
bit[4]: BUCK2 enable control. 1 = enable, 0 = disable
bit[3]: BUCK2 output voltage register address. 1 = 0Ah, 0 = 0Bh
bit[2]: BUCK1 output voltage control by external DVS pin or register
1 = register 08h, 0 = DVS
bit[1]: LDO sleep control. 1 = sleep mode, 0 = normal
bit[0]: BUCK1 enable control, 1 = enable, 0 = disable
For more details, please see the datasheet.
- ti,update-config: define it when LP872X_GENERAL_CFG register should be set
- ti,dvs-gpio: GPIO specifier for external DVS pin control of LP872x devices.
- ti,dvs-vsel: DVS selector. 0 = SEL_V1, 1 = SEL_V2.
- ti,dvs-state: initial DVS pin state. 0 = DVS_LOW, 1 = DVS_HIGH.
Sub nodes for regulator_init_data
LP8720 has maximum 6 nodes. (child name: ldo1 ~ 5 and buck)
LP8725 has maximum 9 nodes. (child name: ldo1 ~ 5, lilo1,2 and buck1,2)
For more details, please see the following binding document.
(Documentation/devicetree/bindings/regulator/regulator.txt)
Datasheet
- LP8720: http://www.ti.com/lit/ds/symlink/lp8720.pdf
- LP8725: http://www.ti.com/lit/ds/symlink/lp8725.pdf
Example 1) LP8720
lp8720@7d {
compatible = "ti,lp8720";
reg = <0x7d>;
/* external DVS pin used, timestep is 25usec */
ti,general-config = /bits/ 8 <0x03>;
ti,update-config;
/*
* The dvs-gpio depends on the processor environment.
* For example, following GPIO specifier means GPIO134 in OMAP4.
*/
ti,dvs-gpio = <&gpio5 6 0>;
ti,dvs-vsel = /bits/ 8 <1>; /* SEL_V2 */
ti,dvs-state = /bits/ 8 <1>; /* DVS_HIGH */
vaf: ldo1 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
};
vmmc: ldo2 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
};
vcam_io: ldo3 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
};
vcam_core: ldo4 {
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <2850000>;
regulator-boot-on;
};
vcam: ldo5 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
};
vcc: buck {
regulator-name = "VBUCK";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <2300000>;
};
};
Example 2) LP8725
lp8725@7a {
compatible = "ti,lp8725";
reg = <0x7a>;
/* Enable BUCK1,2, no DVS, normal LDO mode, timestep is 256usec */
ti,general-config = /bits/ 8 <0xdd>;
ti,update-config;
vcam_io: ldo1 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
};
vcam_core: ldo2 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
};
vcam: ldo3 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
};
vcmmb_io: ldo4 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
};
vcmmb_core: ldo5 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
};
vaux1: lilo1 {
regulator-name = "VAUX1";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
};
vaux2: lilo2 {
regulator-name = "VAUX2";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
};
vcc1: buck1 {
regulator-name = "VBUCK1";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3000000>;
regulator-min-microamp = <460000>;
regulator-max-microamp = <1370000>;
regulator-boot-on;
};
vcc2: buck2 {
regulator-name = "VBUCK2";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3000000>;
regulator-min-microamp = <460000>;
regulator-max-microamp = <1370000>;
regulator-boot-on;
};
};

View File

@ -0,0 +1,21 @@
* Maxim MAX8973 Voltage Regulator
Required properties:
- compatible: must be "maxim,max8973"
- reg: the i2c slave address of the regulator. It should be 0x1b.
Any standard regulator properties can be used to configure the single max8973
DCDC.
Example:
max8973@1b {
compatible = "maxim,max8973";
reg = <0x1b>;
regulator-min-microvolt = <935000>;
regulator-max-microvolt = <1200000>;
regulator-boot-on;
regulator-always-on;
};

View File

@ -9,6 +9,7 @@ Optional properties:
- regulator-max-microamp: largest current consumers may set - regulator-max-microamp: largest current consumers may set
- regulator-always-on: boolean, regulator should never be disabled - regulator-always-on: boolean, regulator should never be disabled
- regulator-boot-on: bootloader/firmware enabled regulator - regulator-boot-on: bootloader/firmware enabled regulator
- regulator-allow-bypass: allow the regulator to go into bypass mode
- <name>-supply: phandle to the parent supply/regulator node - <name>-supply: phandle to the parent supply/regulator node
- regulator-ramp-delay: ramp delay for regulator(in uV/uS) - regulator-ramp-delay: ramp delay for regulator(in uV/uS)

View File

@ -0,0 +1,128 @@
Adaptive Body Bias(ABB) SoC internal LDO regulator for Texas Instruments SoCs
Required Properties:
- compatible: Should be one of:
- "ti,abb-v1" for older SoCs like OMAP3
- "ti,abb-v2" for newer SoCs like OMAP4, OMAP5
- reg: Address and length of the register set for the device. It contains
the information of registers in the same order as described by reg-names
- reg-names: Should contain the reg names
- "base-address" - contains base address of ABB module
- "int-address" - contains address of interrupt register for ABB module
(also see Optional properties)
- #address-cell: should be 0
- #size-cell: should be 0
- clocks: should point to the clock node used by ABB module
- ti,settling-time: Settling time in uSecs from SoC documentation for ABB module
to settle down(target time for SR2_WTCNT_VALUE).
- ti,clock-cycles: SoC specific data about count of system ti,clock-cycles used for
computing settling time from SoC Documentation for ABB module(clock
cycles for SR2_WTCNT_VALUE).
- ti,tranxdone-status-mask: Mask to the int-register to write-to-clear mask
indicating LDO tranxdone (operation complete).
- ti,abb_info: An array of 6-tuples u32 items providing information about ABB
configuration needed per operational voltage of the device.
Each item consists of the following in the same order:
volt: voltage in uV - Only used to index ABB information.
ABB mode: one of the following:
0-bypass
1-Forward Body Bias(FBB)
3-Reverse Body Bias(RBB)
efuse: (see Optional properties)
RBB enable efuse Mask: (See Optional properties)
FBB enable efuse Mask: (See Optional properties)
Vset value efuse Mask: (See Optional properties)
NOTE: If more than 1 entry is present, then regulator is setup to change
voltage, allowing for various modes to be selected indexed off
the regulator. Further, ABB LDOs are considered always-on by
default.
Optional Properties:
- reg-names: In addition to the required properties, the following are optional
- "efuse-address" - Contains efuse base address used to pick up ABB info.
- "ldo-address" - Contains address of ABB LDO overide register address.
"efuse-address" is required for this.
- ti,ldovbb-vset-mask - Required if ldo-address is set, mask for LDO override
register to provide override vset value.
- ti,ldovbb-override-mask - Required if ldo-address is set, mask for LDO
override register to enable override vset value.
- ti,abb_opp_sel: Addendum to the description in required properties
efuse: Mandatory if 'efuse-address' register is defined. Provides offset
from efuse-address to pick up ABB characteristics. Set to 0 if
'efuse-address' is not defined.
RBB enable efuse Mask: Optional if 'efuse-address' register is defined.
'ABB mode' is force set to RBB mode if value at "efuse-address"
+ efuse maps to RBB mask. Set to 0 to ignore this.
FBB enable efuse Mask: Optional if 'efuse-address' register is defined.
'ABB mode' is force set to FBB mode if value at "efuse-address"
+ efuse maps to FBB mask (valid only if RBB mask does not match)
Set to 0 to ignore this.
Vset value efuse Mask: Mandatory if ldo-address is set. Picks up from
efuse the value to set in 'ti,ldovbb-vset-mask' at ldo-address.
Example #1: Simplest configuration (no efuse data, hard coded ABB table):
abb_x: regulator-abb-x {
compatible = "ti,abb-v1";
regulator-name = "abb_x";
#address-cell = <0>;
#size-cells = <0>;
reg = <0x483072f0 0x8>, <0x48306818 0x4>;
reg-names = "base-address", "int-address";
ti,tranxdone-status-mask = <0x4000000>;
clocks = <&sysclk>;
ti,settling-time = <30>;
ti,clock-cycles = <8>;
ti,abb_info = <
/* uV ABB efuse rbb_m fbb_m vset_m */
1012500 0 0 0 0 0 /* Bypass */
1200000 3 0 0 0 0 /* RBB mandatory */
1320000 1 0 0 0 0 /* FBB mandatory */
>;
};
Example #2: Efuse bits contain ABB mode setting (no LDO override capability)
abb_y: regulator-abb-y {
compatible = "ti,abb-v2";
regulator-name = "abb_y";
#address-cell = <0>;
#size-cells = <0>;
reg = <0x4a307bd0 0x8>, <0x4a306014 0x4>, <0x4A002268 0x8>;
reg-names = "base-address", "int-address", "efuse-address";
ti,tranxdone-status-mask = <0x4000000>;
clocks = <&sysclk>;
ti,settling-time = <50>;
ti,clock-cycles = <16>;
ti,abb_info = <
/* uV ABB efuse rbb_m fbb_m vset_m */
975000 0 0 0 0 0 /* Bypass */
1012500 0 0 0x40000 0 0 /* RBB optional */
1200000 0 0x4 0 0x40000 0 /* FBB optional */
1320000 1 0 0 0 0 /* FBB mandatory */
>;
};
Example #3: Efuse bits contain ABB mode setting and LDO override capability
abb_z: regulator-abb-z {
compatible = "ti,abb-v2";
regulator-name = "abb_z";
#address-cell = <0>;
#size-cells = <0>;
reg = <0x4ae07ce4 0x8>, <0x4ae06010 0x4>,
<0x4a002194 0x8>, <0x4ae0C314 0x4>;
reg-names = "base-address", "int-address",
"efuse-address", "ldo-address";
ti,tranxdone-status-mask = <0x8000000>;
/* LDOVBBMM_MUX_CTRL */
ti,ldovbb-override-mask = <0x400>;
/* LDOVBBMM_VSET_OUT */
ti,ldovbb-vset-mask = <0x1F>;
clocks = <&sysclk>;
ti,settling-time = <50>;
ti,clock-cycles = <16>;
ti,abb_info = <
/* uV ABB efuse rbb_m fbb_m vset_m */
975000 0 0 0 0 0 /* Bypass */
1200000 0 0x4 0 0x40000 0x1f00 /* FBB optional, vset */
>;
};

View File

@ -406,7 +406,6 @@ static int pm8607_regulator_remove(struct platform_device *pdev)
{ {
struct pm8607_regulator_info *info = platform_get_drvdata(pdev); struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(info->regulator); regulator_unregister(info->regulator);
return 0; return 0;
} }

View File

@ -250,6 +250,15 @@ config REGULATOR_MAX77686
via I2C bus. The provided regulator is suitable for via I2C bus. The provided regulator is suitable for
Exynos-4 chips to control VARM and VINT voltages. Exynos-4 chips to control VARM and VINT voltages.
config REGULATOR_MAX77693
tristate "Maxim MAX77693 regulator"
depends on MFD_MAX77693
help
This driver controls a Maxim 77693 regulator via I2C bus.
The regulators include two LDOs, 'SAFEOUT1', 'SAFEOUT2'
and one current regulator 'CHARGER'. This is suitable for
Exynos-4x12 chips.
config REGULATOR_PCAP config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver" tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP depends on EZX_PCAP
@ -472,6 +481,16 @@ config REGULATOR_TWL4030
This driver supports the voltage regulators provided by This driver supports the voltage regulators provided by
this family of companion chips. this family of companion chips.
config REGULATOR_TI_ABB
bool "TI Adaptive Body Bias on-chip LDO"
depends on ARCH_OMAP
help
Select this option to support Texas Instruments' on-chip Adaptive Body
Bias (ABB) LDO regulators. It is recommended that this option be
enabled on required TI SoC. Certain Operating Performance Points
on TI SoCs may be unstable without enabling this as it provides
device specific optimized bias to allow/optimize functionality.
config REGULATOR_VEXPRESS config REGULATOR_VEXPRESS
tristate "Versatile Express regulators" tristate "Versatile Express regulators"
depends on VEXPRESS_CONFIG depends on VEXPRESS_CONFIG

View File

@ -12,7 +12,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o ab8500-ext.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
@ -41,6 +41,7 @@ obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
@ -63,6 +64,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o

View File

@ -16,9 +16,11 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/abx500.h> #include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500.h>
#include <linux/regulator/ab8500.h> #include <linux/regulator/ab8500.h>
@ -229,6 +231,28 @@ static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev)
return ret; return ret;
} }
static int ab8500_ext_set_voltage(struct regulator_dev *rdev, int min_uV,
int max_uV, unsigned *selector)
{
struct regulation_constraints *regu_constraints = rdev->constraints;
if (!regu_constraints) {
dev_err(rdev_get_dev(rdev), "No regulator constraints\n");
return -EINVAL;
}
if (regu_constraints->min_uV == min_uV &&
regu_constraints->max_uV == max_uV)
return 0;
dev_err(rdev_get_dev(rdev),
"Requested min %duV max %duV != constrained min %duV max %duV\n",
min_uV, max_uV,
regu_constraints->min_uV, regu_constraints->max_uV);
return -EINVAL;
}
static int ab8500_ext_list_voltage(struct regulator_dev *rdev, static int ab8500_ext_list_voltage(struct regulator_dev *rdev,
unsigned selector) unsigned selector)
{ {
@ -252,6 +276,7 @@ static struct regulator_ops ab8500_ext_regulator_ops = {
.is_enabled = ab8500_ext_regulator_is_enabled, .is_enabled = ab8500_ext_regulator_is_enabled,
.set_mode = ab8500_ext_regulator_set_mode, .set_mode = ab8500_ext_regulator_set_mode,
.get_mode = ab8500_ext_regulator_get_mode, .get_mode = ab8500_ext_regulator_get_mode,
.set_voltage = ab8500_ext_set_voltage,
.list_voltage = ab8500_ext_list_voltage, .list_voltage = ab8500_ext_list_voltage,
}; };
@ -310,18 +335,37 @@ static struct ab8500_ext_regulator_info
}, },
}; };
int ab8500_ext_regulator_init(struct platform_device *pdev) static struct of_regulator_match ab8500_ext_regulator_match[] = {
{ .name = "ab8500_ext1", .driver_data = (void *) AB8500_EXT_SUPPLY1, },
{ .name = "ab8500_ext2", .driver_data = (void *) AB8500_EXT_SUPPLY2, },
{ .name = "ab8500_ext3", .driver_data = (void *) AB8500_EXT_SUPPLY3, },
};
static int ab8500_ext_regulator_probe(struct platform_device *pdev)
{ {
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
struct ab8500_platform_data *ppdata; struct ab8500_platform_data *ppdata;
struct ab8500_regulator_platform_data *pdata; struct ab8500_regulator_platform_data *pdata;
struct device_node *np = pdev->dev.of_node;
struct regulator_config config = { }; struct regulator_config config = { };
int i, err; int i, err;
if (np) {
err = of_regulator_match(&pdev->dev, np,
ab8500_ext_regulator_match,
ARRAY_SIZE(ab8500_ext_regulator_match));
if (err < 0) {
dev_err(&pdev->dev,
"Error parsing regulator init data: %d\n", err);
return err;
}
}
if (!ab8500) { if (!ab8500) {
dev_err(&pdev->dev, "null mfd parent\n"); dev_err(&pdev->dev, "null mfd parent\n");
return -EINVAL; return -EINVAL;
} }
ppdata = dev_get_platdata(ab8500->dev); ppdata = dev_get_platdata(ab8500->dev);
if (!ppdata) { if (!ppdata) {
dev_err(&pdev->dev, "null parent pdata\n"); dev_err(&pdev->dev, "null parent pdata\n");
@ -362,8 +406,11 @@ int ab8500_ext_regulator_init(struct platform_device *pdev)
pdata->ext_regulator[i].driver_data; pdata->ext_regulator[i].driver_data;
config.dev = &pdev->dev; config.dev = &pdev->dev;
config.init_data = &pdata->ext_regulator[i];
config.driver_data = info; config.driver_data = info;
config.of_node = ab8500_ext_regulator_match[i].of_node;
config.init_data = (np) ?
ab8500_ext_regulator_match[i].init_data :
&pdata->ext_regulator[i];
/* register regulator with framework */ /* register regulator with framework */
info->rdev = regulator_register(&info->desc, &config); info->rdev = regulator_register(&info->desc, &config);
@ -386,7 +433,7 @@ int ab8500_ext_regulator_init(struct platform_device *pdev)
return 0; return 0;
} }
void ab8500_ext_regulator_exit(struct platform_device *pdev) static int ab8500_ext_regulator_remove(struct platform_device *pdev)
{ {
int i; int i;
@ -399,8 +446,37 @@ void ab8500_ext_regulator_exit(struct platform_device *pdev)
regulator_unregister(info->rdev); regulator_unregister(info->rdev);
} }
return 0;
} }
static struct platform_driver ab8500_ext_regulator_driver = {
.probe = ab8500_ext_regulator_probe,
.remove = ab8500_ext_regulator_remove,
.driver = {
.name = "ab8500-ext-regulator",
.owner = THIS_MODULE,
},
};
static int __init ab8500_ext_regulator_init(void)
{
int ret;
ret = platform_driver_register(&ab8500_ext_regulator_driver);
if (ret)
pr_err("Failed to register ab8500 ext regulator: %d\n", ret);
return ret;
}
subsys_initcall(ab8500_ext_regulator_init);
static void __exit ab8500_ext_regulator_exit(void)
{
platform_driver_unregister(&ab8500_ext_regulator_driver);
}
module_exit(ab8500_ext_regulator_exit);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>"); MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
MODULE_DESCRIPTION("AB8500 external regulator driver"); MODULE_DESCRIPTION("AB8500 external regulator driver");

View File

@ -719,6 +719,7 @@ static struct ab8500_regulator_info
.n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
.volt_table = ldo_vauxn_voltages, .volt_table = ldo_vauxn_voltages,
.enable_time = 200, .enable_time = 200,
.supply_name = "vin",
}, },
.load_lp_uA = 5000, .load_lp_uA = 5000,
.update_bank = 0x04, .update_bank = 0x04,
@ -741,6 +742,7 @@ static struct ab8500_regulator_info
.n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
.volt_table = ldo_vauxn_voltages, .volt_table = ldo_vauxn_voltages,
.enable_time = 200, .enable_time = 200,
.supply_name = "vin",
}, },
.load_lp_uA = 5000, .load_lp_uA = 5000,
.update_bank = 0x04, .update_bank = 0x04,
@ -763,6 +765,7 @@ static struct ab8500_regulator_info
.n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages),
.volt_table = ldo_vaux3_voltages, .volt_table = ldo_vaux3_voltages,
.enable_time = 450, .enable_time = 450,
.supply_name = "vin",
}, },
.load_lp_uA = 5000, .load_lp_uA = 5000,
.update_bank = 0x04, .update_bank = 0x04,
@ -3156,22 +3159,12 @@ static int ab8500_regulator_probe(struct platform_device *pdev)
return err; return err;
} }
if (!is_ab8505(ab8500)) {
/* register external regulators (before Vaux1, 2 and 3) */
err = ab8500_ext_regulator_init(pdev);
if (err)
return err;
}
/* register all regulators */ /* register all regulators */
for (i = 0; i < abx500_regulator.info_size; i++) { for (i = 0; i < abx500_regulator.info_size; i++) {
err = ab8500_regulator_register(pdev, &pdata->regulator[i], err = ab8500_regulator_register(pdev, &pdata->regulator[i],
i, NULL); i, NULL);
if (err < 0) { if (err < 0)
if (!is_ab8505(ab8500))
ab8500_ext_regulator_exit(pdev);
return err; return err;
}
} }
return 0; return 0;
@ -3180,7 +3173,6 @@ static int ab8500_regulator_probe(struct platform_device *pdev)
static int ab8500_regulator_remove(struct platform_device *pdev) static int ab8500_regulator_remove(struct platform_device *pdev)
{ {
int i, err; int i, err;
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
for (i = 0; i < abx500_regulator.info_size; i++) { for (i = 0; i < abx500_regulator.info_size; i++) {
struct ab8500_regulator_info *info = NULL; struct ab8500_regulator_info *info = NULL;
@ -3192,10 +3184,6 @@ static int ab8500_regulator_remove(struct platform_device *pdev)
regulator_unregister(info->regulator); regulator_unregister(info->regulator);
} }
/* remove external regulators (after Vaux1, 2 and 3) */
if (!is_ab8505(ab8500))
ab8500_ext_regulator_exit(pdev);
/* remove regulator debug */ /* remove regulator debug */
err = ab8500_regulator_debug_exit(pdev); err = ab8500_regulator_debug_exit(pdev);
if (err) if (err)

View File

@ -2137,6 +2137,21 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector)
} }
EXPORT_SYMBOL_GPL(regulator_list_voltage); EXPORT_SYMBOL_GPL(regulator_list_voltage);
/**
* regulator_get_linear_step - return the voltage step size between VSEL values
* @regulator: regulator source
*
* Returns the voltage step size between VSEL values for linear
* regulators, or return 0 if the regulator isn't a linear regulator.
*/
unsigned int regulator_get_linear_step(struct regulator *regulator)
{
struct regulator_dev *rdev = regulator->rdev;
return rdev->desc->uV_step;
}
EXPORT_SYMBOL_GPL(regulator_get_linear_step);
/** /**
* regulator_is_supported_voltage - check if a voltage range can be supported * regulator_is_supported_voltage - check if a voltage range can be supported
* *

View File

@ -130,7 +130,7 @@ static int isl6271a_probe(struct i2c_client *i2c,
if (i == 0) if (i == 0)
config.init_data = init_data; config.init_data = init_data;
else else
config.init_data = 0; config.init_data = NULL;
config.driver_data = pmic; config.driver_data = pmic;
pmic->rdev[i] = regulator_register(&isl_rd[i], &config); pmic->rdev[i] = regulator_register(&isl_rd[i], &config);

View File

@ -434,7 +434,7 @@ static int lp3971_i2c_probe(struct i2c_client *i2c,
return -ENODEV; return -ENODEV;
} }
lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); lp3971 = devm_kzalloc(&i2c->dev, sizeof(struct lp3971), GFP_KERNEL);
if (lp3971 == NULL) if (lp3971 == NULL)
return -ENOMEM; return -ENOMEM;
@ -449,19 +449,15 @@ static int lp3971_i2c_probe(struct i2c_client *i2c,
ret = -ENODEV; ret = -ENODEV;
if (ret < 0) { if (ret < 0) {
dev_err(&i2c->dev, "failed to detect device\n"); dev_err(&i2c->dev, "failed to detect device\n");
goto err_detect; return ret;
} }
ret = setup_regulators(lp3971, pdata); ret = setup_regulators(lp3971, pdata);
if (ret < 0) if (ret < 0)
goto err_detect; return ret;
i2c_set_clientdata(i2c, lp3971); i2c_set_clientdata(i2c, lp3971);
return 0; return 0;
err_detect:
kfree(lp3971);
return ret;
} }
static int lp3971_i2c_remove(struct i2c_client *i2c) static int lp3971_i2c_remove(struct i2c_client *i2c)
@ -473,7 +469,6 @@ static int lp3971_i2c_remove(struct i2c_client *i2c)
regulator_unregister(lp3971->rdev[i]); regulator_unregister(lp3971->rdev[i]);
kfree(lp3971->rdev); kfree(lp3971->rdev);
kfree(lp3971);
return 0; return 0;
} }

View File

@ -528,7 +528,7 @@ static int lp3972_i2c_probe(struct i2c_client *i2c,
return -ENODEV; return -ENODEV;
} }
lp3972 = kzalloc(sizeof(struct lp3972), GFP_KERNEL); lp3972 = devm_kzalloc(&i2c->dev, sizeof(struct lp3972), GFP_KERNEL);
if (!lp3972) if (!lp3972)
return -ENOMEM; return -ENOMEM;
@ -546,19 +546,15 @@ static int lp3972_i2c_probe(struct i2c_client *i2c,
} }
if (ret < 0) { if (ret < 0) {
dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret); dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret);
goto err_detect; return ret;
} }
ret = setup_regulators(lp3972, pdata); ret = setup_regulators(lp3972, pdata);
if (ret < 0) if (ret < 0)
goto err_detect; return ret;
i2c_set_clientdata(i2c, lp3972); i2c_set_clientdata(i2c, lp3972);
return 0; return 0;
err_detect:
kfree(lp3972);
return ret;
} }
static int lp3972_i2c_remove(struct i2c_client *i2c) static int lp3972_i2c_remove(struct i2c_client *i2c)
@ -569,7 +565,6 @@ static int lp3972_i2c_remove(struct i2c_client *i2c)
for (i = 0; i < lp3972->num_regulators; i++) for (i = 0; i < lp3972->num_regulators; i++)
regulator_unregister(lp3972->rdev[i]); regulator_unregister(lp3972->rdev[i]);
kfree(lp3972->rdev); kfree(lp3972->rdev);
kfree(lp3972);
return 0; return 0;
} }

View File

@ -18,6 +18,9 @@
#include <linux/regulator/lp872x.h> #include <linux/regulator/lp872x.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/regulator/of_regulator.h>
/* Registers : LP8720/8725 shared */ /* Registers : LP8720/8725 shared */
#define LP872X_GENERAL_CFG 0x00 #define LP872X_GENERAL_CFG 0x00
@ -723,8 +726,8 @@ static int lp872x_init_dvs(struct lp872x *lp)
gpio = dvs->gpio; gpio = dvs->gpio;
if (!gpio_is_valid(gpio)) { if (!gpio_is_valid(gpio)) {
dev_err(lp->dev, "invalid gpio: %d\n", gpio); dev_warn(lp->dev, "invalid gpio: %d\n", gpio);
return -EINVAL; goto set_default_dvs_mode;
} }
pinstate = dvs->init_state; pinstate = dvs->init_state;
@ -829,6 +832,103 @@ static const struct regmap_config lp872x_regmap_config = {
.max_register = MAX_REGISTERS, .max_register = MAX_REGISTERS,
}; };
#ifdef CONFIG_OF
#define LP872X_VALID_OPMODE (REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL)
static struct of_regulator_match lp8720_matches[] = {
{ .name = "ldo1", .driver_data = (void *)LP8720_ID_LDO1, },
{ .name = "ldo2", .driver_data = (void *)LP8720_ID_LDO2, },
{ .name = "ldo3", .driver_data = (void *)LP8720_ID_LDO3, },
{ .name = "ldo4", .driver_data = (void *)LP8720_ID_LDO4, },
{ .name = "ldo5", .driver_data = (void *)LP8720_ID_LDO5, },
{ .name = "buck", .driver_data = (void *)LP8720_ID_BUCK, },
};
static struct of_regulator_match lp8725_matches[] = {
{ .name = "ldo1", .driver_data = (void *)LP8725_ID_LDO1, },
{ .name = "ldo2", .driver_data = (void *)LP8725_ID_LDO2, },
{ .name = "ldo3", .driver_data = (void *)LP8725_ID_LDO3, },
{ .name = "ldo4", .driver_data = (void *)LP8725_ID_LDO4, },
{ .name = "ldo5", .driver_data = (void *)LP8725_ID_LDO5, },
{ .name = "lilo1", .driver_data = (void *)LP8725_ID_LILO1, },
{ .name = "lilo2", .driver_data = (void *)LP8725_ID_LILO2, },
{ .name = "buck1", .driver_data = (void *)LP8725_ID_BUCK1, },
{ .name = "buck2", .driver_data = (void *)LP8725_ID_BUCK2, },
};
static struct lp872x_platform_data
*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which)
{
struct device_node *np = dev->of_node;
struct lp872x_platform_data *pdata;
struct of_regulator_match *match;
struct regulator_init_data *d;
int num_matches;
int count;
int i;
u8 dvs_state;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto out;
of_property_read_u8(np, "ti,general-config", &pdata->general_config);
if (of_find_property(np, "ti,update-config", NULL))
pdata->update_config = true;
pdata->dvs = devm_kzalloc(dev, sizeof(struct lp872x_dvs), GFP_KERNEL);
if (!pdata->dvs)
goto out;
pdata->dvs->gpio = of_get_named_gpio(np, "ti,dvs-gpio", 0);
of_property_read_u8(np, "ti,dvs-vsel", (u8 *)&pdata->dvs->vsel);
of_property_read_u8(np, "ti,dvs-state", &dvs_state);
pdata->dvs->init_state = dvs_state ? DVS_HIGH : DVS_LOW;
if (of_get_child_count(np) == 0)
goto out;
switch (which) {
case LP8720:
match = lp8720_matches;
num_matches = ARRAY_SIZE(lp8720_matches);
break;
case LP8725:
match = lp8725_matches;
num_matches = ARRAY_SIZE(lp8725_matches);
break;
default:
goto out;
}
count = of_regulator_match(dev, np, match, num_matches);
if (count <= 0)
goto out;
for (i = 0; i < num_matches; i++) {
pdata->regulator_data[i].id = (int)match[i].driver_data;
pdata->regulator_data[i].init_data = match[i].init_data;
/* Operation mode configuration for buck/buck1/buck2 */
if (strncmp(match[i].name, "buck", 4))
continue;
d = pdata->regulator_data[i].init_data;
d->constraints.valid_modes_mask |= LP872X_VALID_OPMODE;
d->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
}
out:
return pdata;
}
#else
static struct lp872x_platform_data
*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which)
{
return NULL;
}
#endif
static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{ {
struct lp872x *lp; struct lp872x *lp;
@ -838,6 +938,10 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
[LP8725] = LP8725_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS,
}; };
if (cl->dev.of_node)
cl->dev.platform_data = lp872x_populate_pdata_from_dt(&cl->dev,
(enum lp872x_id)id->driver_data);
lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL);
if (!lp) if (!lp)
goto err_mem; goto err_mem;
@ -882,6 +986,13 @@ static int lp872x_remove(struct i2c_client *cl)
return 0; return 0;
} }
static const struct of_device_id lp872x_dt_ids[] = {
{ .compatible = "ti,lp8720", },
{ .compatible = "ti,lp8725", },
{ }
};
MODULE_DEVICE_TABLE(of, lp872x_dt_ids);
static const struct i2c_device_id lp872x_ids[] = { static const struct i2c_device_id lp872x_ids[] = {
{"lp8720", LP8720}, {"lp8720", LP8720},
{"lp8725", LP8725}, {"lp8725", LP8725},
@ -893,6 +1004,7 @@ static struct i2c_driver lp872x_driver = {
.driver = { .driver = {
.name = "lp872x", .name = "lp872x",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(lp872x_dt_ids),
}, },
.probe = lp872x_probe, .probe = lp872x_probe,
.remove = lp872x_remove, .remove = lp872x_remove,

View File

@ -19,7 +19,6 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>

View File

@ -533,7 +533,6 @@ static int lp8788_buck_remove(struct platform_device *pdev)
{ {
struct lp8788_buck *buck = platform_get_drvdata(pdev); struct lp8788_buck *buck = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(buck->regulator); regulator_unregister(buck->regulator);
return 0; return 0;

View File

@ -561,7 +561,6 @@ static int lp8788_dldo_remove(struct platform_device *pdev)
{ {
struct lp8788_ldo *ldo = platform_get_drvdata(pdev); struct lp8788_ldo *ldo = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(ldo->regulator); regulator_unregister(ldo->regulator);
return 0; return 0;
@ -622,7 +621,6 @@ static int lp8788_aldo_remove(struct platform_device *pdev)
{ {
struct lp8788_ldo *ldo = platform_get_drvdata(pdev); struct lp8788_ldo *ldo = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(ldo->regulator); regulator_unregister(ldo->regulator);
return 0; return 0;

View File

@ -24,7 +24,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -0,0 +1,322 @@
/*
* max77693.c - Regulator driver for the Maxim 77693
*
* Copyright (C) 2013 Samsung Electronics
* Jonghwa Lee <jonghwa3.lee@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This driver is based on max77686.c
*/
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/export.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/max77693.h>
#include <linux/mfd/max77693-private.h>
#include <linux/regulator/of_regulator.h>
#define CHGIN_ILIM_STEP_20mA 20000
struct max77693_pmic_dev {
struct device *dev;
struct max77693_dev *iodev;
int num_regulators;
struct regulator_dev **rdev;
};
/* CHARGER regulator ops */
/* CHARGER regulator uses two bits for enabling */
static int max77693_chg_is_enabled(struct regulator_dev *rdev)
{
int ret;
u8 val;
ret = max77693_read_reg(rdev->regmap, rdev->desc->enable_reg, &val);
if (ret)
return ret;
return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask;
}
/*
* CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA
* 0x00, 0x01, 0x2, 0x03 = 60 mA
* 0x04 ~ 0x7E = (60 + (X - 3) * 20) mA
*/
static int max77693_chg_get_current_limit(struct regulator_dev *rdev)
{
unsigned int chg_min_uA = rdev->constraints->min_uA;
unsigned int chg_max_uA = rdev->constraints->max_uA;
u8 reg, sel;
unsigned int val;
int ret;
ret = max77693_read_reg(rdev->regmap,
MAX77693_CHG_REG_CHG_CNFG_09, &reg);
if (ret < 0)
return ret;
sel = reg & CHG_CNFG_09_CHGIN_ILIM_MASK;
/* the first four codes for charger current are all 60mA */
if (sel <= 3)
sel = 0;
else
sel -= 3;
val = chg_min_uA + CHGIN_ILIM_STEP_20mA * sel;
if (val > chg_max_uA)
return -EINVAL;
return val;
}
static int max77693_chg_set_current_limit(struct regulator_dev *rdev,
int min_uA, int max_uA)
{
unsigned int chg_min_uA = rdev->constraints->min_uA;
int sel = 0;
while (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel < min_uA)
sel++;
if (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel > max_uA)
return -EINVAL;
/* the first four codes for charger current are all 60mA */
sel += 3;
return max77693_write_reg(rdev->regmap,
MAX77693_CHG_REG_CHG_CNFG_09, sel);
}
/* end of CHARGER regulator ops */
static const unsigned int max77693_safeout_table[] = {
4850000,
4900000,
4950000,
3300000,
};
static struct regulator_ops max77693_safeout_ops = {
.list_voltage = regulator_list_voltage_table,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
static struct regulator_ops max77693_charger_ops = {
.is_enabled = max77693_chg_is_enabled,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_current_limit = max77693_chg_get_current_limit,
.set_current_limit = max77693_chg_set_current_limit,
};
#define regulator_desc_esafeout(_num) { \
.name = "ESAFEOUT"#_num, \
.id = MAX77693_ESAFEOUT##_num, \
.n_voltages = 4, \
.ops = &max77693_safeout_ops, \
.type = REGULATOR_VOLTAGE, \
.volt_table = max77693_safeout_table, \
.vsel_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \
.vsel_mask = SAFEOUT_CTRL_SAFEOUT##_num##_MASK, \
.enable_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \
.enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \
}
static struct regulator_desc regulators[] = {
regulator_desc_esafeout(1),
regulator_desc_esafeout(2),
{
.name = "CHARGER",
.id = MAX77693_CHARGER,
.ops = &max77693_charger_ops,
.type = REGULATOR_CURRENT,
.owner = THIS_MODULE,
.enable_reg = MAX77693_CHG_REG_CHG_CNFG_00,
.enable_mask = CHG_CNFG_00_CHG_MASK |
CHG_CNFG_00_BUCK_MASK,
},
};
#ifdef CONFIG_OF
static int max77693_pmic_dt_parse_rdata(struct device *dev,
struct max77693_regulator_data **rdata)
{
struct device_node *np;
struct of_regulator_match *rmatch;
struct max77693_regulator_data *tmp;
int i, matched = 0;
np = of_find_node_by_name(dev->parent->of_node, "regulators");
if (!np)
return -EINVAL;
rmatch = devm_kzalloc(dev,
sizeof(*rmatch) * ARRAY_SIZE(regulators), GFP_KERNEL);
if (!rmatch)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(regulators); i++)
rmatch[i].name = regulators[i].name;
matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(regulators));
if (matched <= 0)
return matched;
*rdata = devm_kzalloc(dev, sizeof(**rdata) * matched, GFP_KERNEL);
if (!(*rdata))
return -ENOMEM;
tmp = *rdata;
for (i = 0; i < matched; i++) {
tmp->initdata = rmatch[i].init_data;
tmp->of_node = rmatch[i].of_node;
tmp->id = regulators[i].id;
tmp++;
}
return matched;
}
#else
static int max77693_pmic_dt_parse_rdata(struct device *dev,
struct max77693_regulator_data **rdata)
{
return 0;
}
#endif /* CONFIG_OF */
static int max77693_pmic_init_rdata(struct device *dev,
struct max77693_regulator_data **rdata)
{
struct max77693_platform_data *pdata;
int num_regulators = 0;
pdata = dev_get_platdata(dev->parent);
if (pdata) {
*rdata = pdata->regulators;
num_regulators = pdata->num_regulators;
}
if (!(*rdata) && dev->parent->of_node)
num_regulators = max77693_pmic_dt_parse_rdata(dev, rdata);
return num_regulators;
}
static int max77693_pmic_probe(struct platform_device *pdev)
{
struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max77693_pmic_dev *max77693_pmic;
struct max77693_regulator_data *rdata = NULL;
int num_rdata, i, ret;
struct regulator_config config;
num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata);
if (!rdata || num_rdata <= 0) {
dev_err(&pdev->dev, "No init data supplied.\n");
return -ENODEV;
}
max77693_pmic = devm_kzalloc(&pdev->dev,
sizeof(struct max77693_pmic_dev),
GFP_KERNEL);
if (!max77693_pmic)
return -ENOMEM;
max77693_pmic->rdev = devm_kzalloc(&pdev->dev,
sizeof(struct regulator_dev *) * num_rdata,
GFP_KERNEL);
if (!max77693_pmic->rdev)
return -ENOMEM;
max77693_pmic->dev = &pdev->dev;
max77693_pmic->iodev = iodev;
max77693_pmic->num_regulators = num_rdata;
config.dev = &pdev->dev;
config.regmap = iodev->regmap;
config.driver_data = max77693_pmic;
platform_set_drvdata(pdev, max77693_pmic);
for (i = 0; i < max77693_pmic->num_regulators; i++) {
int id = rdata[i].id;
config.init_data = rdata[i].initdata;
config.of_node = rdata[i].of_node;
max77693_pmic->rdev[i] = regulator_register(&regulators[id],
&config);
if (IS_ERR(max77693_pmic->rdev[i])) {
ret = PTR_ERR(max77693_pmic->rdev[i]);
dev_err(max77693_pmic->dev,
"Failed to initialize regulator-%d\n", id);
max77693_pmic->rdev[i] = NULL;
goto err;
}
}
return 0;
err:
while (--i >= 0)
regulator_unregister(max77693_pmic->rdev[i]);
return ret;
}
static int max77693_pmic_remove(struct platform_device *pdev)
{
struct max77693_pmic_dev *max77693_pmic = platform_get_drvdata(pdev);
struct regulator_dev **rdev = max77693_pmic->rdev;
int i;
for (i = 0; i < max77693_pmic->num_regulators; i++)
if (rdev[i])
regulator_unregister(rdev[i]);
return 0;
}
static const struct platform_device_id max77693_pmic_id[] = {
{"max77693-pmic", 0},
{},
};
MODULE_DEVICE_TABLE(platform, max77693_pmic_id);
static struct platform_driver max77693_pmic_driver = {
.driver = {
.name = "max77693-pmic",
.owner = THIS_MODULE,
},
.probe = max77693_pmic_probe,
.remove = max77693_pmic_remove,
.id_table = max77693_pmic_id,
};
module_platform_driver(max77693_pmic_driver);
MODULE_DESCRIPTION("MAXIM MAX77693 regulator driver");
MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
MODULE_LICENSE("GPL");

View File

@ -327,7 +327,6 @@ static int max8925_regulator_remove(struct platform_device *pdev)
{ {
struct regulator_dev *rdev = platform_get_drvdata(pdev); struct regulator_dev *rdev = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(rdev); regulator_unregister(rdev);
return 0; return 0;

View File

@ -26,10 +26,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/regulator/max8973-regulator.h> #include <linux/regulator/max8973-regulator.h>
#include <linux/regulator/of_regulator.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -100,6 +102,7 @@ struct max8973_chip {
int curr_vout_reg; int curr_vout_reg;
int curr_gpio_val; int curr_gpio_val;
bool valid_dvs_gpio; bool valid_dvs_gpio;
struct regulator_ops ops;
}; };
/* /*
@ -240,7 +243,7 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev)
REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
} }
static struct regulator_ops max8973_dcdc_ops = { static const struct regulator_ops max8973_dcdc_ops = {
.get_voltage_sel = max8973_dcdc_get_voltage_sel, .get_voltage_sel = max8973_dcdc_get_voltage_sel,
.set_voltage_sel = max8973_dcdc_set_voltage_sel, .set_voltage_sel = max8973_dcdc_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear, .list_voltage = regulator_list_voltage_linear,
@ -369,7 +372,8 @@ static int max8973_probe(struct i2c_client *client,
int ret; int ret;
pdata = client->dev.platform_data; pdata = client->dev.platform_data;
if (!pdata) {
if (!pdata && !client->dev.of_node) {
dev_err(&client->dev, "No Platform data"); dev_err(&client->dev, "No Platform data");
return -EIO; return -EIO;
} }
@ -388,30 +392,36 @@ static int max8973_probe(struct i2c_client *client,
} }
i2c_set_clientdata(client, max); i2c_set_clientdata(client, max);
max->ops = max8973_dcdc_ops;
max->dev = &client->dev; max->dev = &client->dev;
max->desc.name = id->name; max->desc.name = id->name;
max->desc.id = 0; max->desc.id = 0;
max->desc.ops = &max8973_dcdc_ops; max->desc.ops = &max->ops;
max->desc.type = REGULATOR_VOLTAGE; max->desc.type = REGULATOR_VOLTAGE;
max->desc.owner = THIS_MODULE; max->desc.owner = THIS_MODULE;
max->desc.min_uV = MAX8973_MIN_VOLATGE; max->desc.min_uV = MAX8973_MIN_VOLATGE;
max->desc.uV_step = MAX8973_VOLATGE_STEP; max->desc.uV_step = MAX8973_VOLATGE_STEP;
max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE; max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE;
if (!pdata->enable_ext_control) { if (!pdata || !pdata->enable_ext_control) {
max->desc.enable_reg = MAX8973_VOUT; max->desc.enable_reg = MAX8973_VOUT;
max->desc.enable_mask = MAX8973_VOUT_ENABLE; max->desc.enable_mask = MAX8973_VOUT_ENABLE;
max8973_dcdc_ops.enable = regulator_enable_regmap; max->ops.enable = regulator_enable_regmap;
max8973_dcdc_ops.disable = regulator_disable_regmap; max->ops.disable = regulator_disable_regmap;
max8973_dcdc_ops.is_enabled = regulator_is_enabled_regmap; max->ops.is_enabled = regulator_is_enabled_regmap;
}
if (pdata) {
max->dvs_gpio = pdata->dvs_gpio;
max->enable_external_control = pdata->enable_ext_control;
max->curr_gpio_val = pdata->dvs_def_state;
max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
} else {
max->dvs_gpio = -EINVAL;
max->curr_vout_reg = MAX8973_VOUT;
} }
max->enable_external_control = pdata->enable_ext_control;
max->dvs_gpio = pdata->dvs_gpio;
max->curr_gpio_val = pdata->dvs_def_state;
max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
max->lru_index[0] = max->curr_vout_reg; max->lru_index[0] = max->curr_vout_reg;
max->valid_dvs_gpio = false;
if (gpio_is_valid(max->dvs_gpio)) { if (gpio_is_valid(max->dvs_gpio)) {
int gpio_flags; int gpio_flags;
@ -437,16 +447,21 @@ static int max8973_probe(struct i2c_client *client,
max->lru_index[i] = i; max->lru_index[i] = i;
max->lru_index[0] = max->curr_vout_reg; max->lru_index[0] = max->curr_vout_reg;
max->lru_index[max->curr_vout_reg] = 0; max->lru_index[max->curr_vout_reg] = 0;
} else {
max->valid_dvs_gpio = false;
} }
ret = max8973_init_dcdc(max, pdata); if (pdata) {
if (ret < 0) { ret = max8973_init_dcdc(max, pdata);
dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret); if (ret < 0) {
return ret; dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
return ret;
}
} }
config.dev = &client->dev; config.dev = &client->dev;
config.init_data = pdata->reg_init_data; config.init_data = pdata ? pdata->reg_init_data :
of_get_regulator_init_data(&client->dev, client->dev.of_node);
config.driver_data = max; config.driver_data = max;
config.of_node = client->dev.of_node; config.of_node = client->dev.of_node;
config.regmap = max->regmap; config.regmap = max->regmap;

View File

@ -466,8 +466,6 @@ static int mc13783_regulator_remove(struct platform_device *pdev)
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
int i; int i;
platform_set_drvdata(pdev, NULL);
for (i = 0; i < priv->num_regulators; i++) for (i = 0; i < priv->num_regulators; i++)
regulator_unregister(priv->regulators[i]); regulator_unregister(priv->regulators[i]);

View File

@ -636,8 +636,6 @@ static int mc13892_regulator_remove(struct platform_device *pdev)
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
int i; int i;
platform_set_drvdata(pdev, NULL);
for (i = 0; i < priv->num_regulators; i++) for (i = 0; i < priv->num_regulators; i++)
regulator_unregister(priv->regulators[i]); regulator_unregister(priv->regulators[i]);

View File

@ -61,6 +61,9 @@ static void of_get_regulation_constraints(struct device_node *np,
else /* status change should be possible if not always on. */ else /* status change should be possible if not always on. */
constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
if (of_property_read_bool(np, "regulator-allow-bypass"))
constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL);
if (ramp_delay) if (ramp_delay)
constraints->ramp_delay = be32_to_cpu(*ramp_delay); constraints->ramp_delay = be32_to_cpu(*ramp_delay);

View File

@ -260,7 +260,6 @@ static int pcap_regulator_remove(struct platform_device *pdev)
struct regulator_dev *rdev = platform_get_drvdata(pdev); struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev); regulator_unregister(rdev);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -106,7 +106,6 @@ static int pcf50633_regulator_remove(struct platform_device *pdev)
{ {
struct regulator_dev *rdev = platform_get_drvdata(pdev); struct regulator_dev *rdev = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(rdev); regulator_unregister(rdev);
return 0; return 0;

View File

@ -12,7 +12,6 @@
*/ */
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -0,0 +1,910 @@
/*
* Texas Instruments SoC Adaptive Body Bias(ABB) Regulator
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Mike Turquette <mturquette@ti.com>
*
* Copyright (C) 2012-2013 Texas Instruments, Inc.
* Andrii Tseglytskyi <andrii.tseglytskyi@ti.com>
* Nishanth Menon <nm@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
/*
* ABB LDO operating states:
* NOMINAL_OPP: bypasses the ABB LDO
* FAST_OPP: sets ABB LDO to Forward Body-Bias
* SLOW_OPP: sets ABB LDO to Reverse Body-Bias
*/
#define TI_ABB_NOMINAL_OPP 0
#define TI_ABB_FAST_OPP 1
#define TI_ABB_SLOW_OPP 3
/**
* struct ti_abb_info - ABB information per voltage setting
* @opp_sel: one of TI_ABB macro
* @vset: (optional) vset value that LDOVBB needs to be overriden with.
*
* Array of per voltage entries organized in the same order as regulator_desc's
* volt_table list. (selector is used to index from this array)
*/
struct ti_abb_info {
u32 opp_sel;
u32 vset;
};
/**
* struct ti_abb_reg - Register description for ABB block
* @setup_reg: setup register offset from base
* @control_reg: control register offset from base
* @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask
* @fbb_sel_mask: setup register- FBB sel mask
* @rbb_sel_mask: setup register- RBB sel mask
* @sr2_en_mask: setup register- enable mask
* @opp_change_mask: control register - mask to trigger LDOVBB change
* @opp_sel_mask: control register - mask for mode to operate
*/
struct ti_abb_reg {
u32 setup_reg;
u32 control_reg;
/* Setup register fields */
u32 sr2_wtcnt_value_mask;
u32 fbb_sel_mask;
u32 rbb_sel_mask;
u32 sr2_en_mask;
/* Control register fields */
u32 opp_change_mask;
u32 opp_sel_mask;
};
/**
* struct ti_abb - ABB instance data
* @rdesc: regulator descriptor
* @clk: clock(usually sysclk) supplying ABB block
* @base: base address of ABB block
* @int_base: interrupt register base address
* @efuse_base: (optional) efuse base address for ABB modes
* @ldo_base: (optional) LDOVBB vset override base address
* @regs: pointer to struct ti_abb_reg for ABB block
* @txdone_mask: mask on int_base for tranxdone interrupt
* @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB
* vset with value from efuse
* @ldovbb_vset_mask: mask to ldo_base for providing the VSET override
* @info: array to per voltage ABB configuration
* @current_info_idx: current index to info
* @settling_time: SoC specific settling time for LDO VBB
*/
struct ti_abb {
struct regulator_desc rdesc;
struct clk *clk;
void __iomem *base;
void __iomem *int_base;
void __iomem *efuse_base;
void __iomem *ldo_base;
const struct ti_abb_reg *regs;
u32 txdone_mask;
u32 ldovbb_override_mask;
u32 ldovbb_vset_mask;
struct ti_abb_info *info;
int current_info_idx;
u32 settling_time;
};
/**
* ti_abb_rmw() - handy wrapper to set specific register bits
* @mask: mask for register field
* @value: value shifted to mask location and written
* @offset: offset of register
* @base: base address
*
* Return: final register value (may be unused)
*/
static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset,
void __iomem *base)
{
u32 val;
val = readl(base + offset);
val &= ~mask;
val |= (value << __ffs(mask)) & mask;
writel(val, base + offset);
return val;
}
/**
* ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status
* @abb: pointer to the abb instance
*
* Return: true or false
*/
static inline bool ti_abb_check_txdone(const struct ti_abb *abb)
{
return !!(readl(abb->int_base) & abb->txdone_mask);
}
/**
* ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status
* @abb: pointer to the abb instance
*/
static inline void ti_abb_clear_txdone(const struct ti_abb *abb)
{
writel(abb->txdone_mask, abb->int_base);
};
/**
* ti_abb_wait_tranx() - waits for ABB tranxdone event
* @dev: device
* @abb: pointer to the abb instance
*
* Return: 0 on success or -ETIMEDOUT if the event is not cleared on time.
*/
static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb)
{
int timeout = 0;
bool status;
while (timeout++ <= abb->settling_time) {
status = ti_abb_check_txdone(abb);
if (status)
break;
udelay(1);
}
if (timeout > abb->settling_time) {
dev_warn_ratelimited(dev,
"%s:TRANXDONE timeout(%duS) int=0x%08x\n",
__func__, timeout, readl(abb->int_base));
return -ETIMEDOUT;
}
return 0;
}
/**
* ti_abb_clear_all_txdone() - clears ABB tranxdone event
* @dev: device
* @abb: pointer to the abb instance
*
* Return: 0 on success or -ETIMEDOUT if the event is not cleared on time.
*/
static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb)
{
int timeout = 0;
bool status;
while (timeout++ <= abb->settling_time) {
ti_abb_clear_txdone(abb);
status = ti_abb_check_txdone(abb);
if (!status)
break;
udelay(1);
}
if (timeout > abb->settling_time) {
dev_warn_ratelimited(dev,
"%s:TRANXDONE timeout(%duS) int=0x%08x\n",
__func__, timeout, readl(abb->int_base));
return -ETIMEDOUT;
}
return 0;
}
/**
* ti_abb_program_ldovbb() - program LDOVBB register for override value
* @dev: device
* @abb: pointer to the abb instance
* @info: ABB info to program
*/
static void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb,
struct ti_abb_info *info)
{
u32 val;
val = readl(abb->ldo_base);
/* clear up previous values */
val &= ~(abb->ldovbb_override_mask | abb->ldovbb_vset_mask);
switch (info->opp_sel) {
case TI_ABB_SLOW_OPP:
case TI_ABB_FAST_OPP:
val |= abb->ldovbb_override_mask;
val |= info->vset << __ffs(abb->ldovbb_vset_mask);
break;
}
writel(val, abb->ldo_base);
}
/**
* ti_abb_set_opp() - Setup ABB and LDO VBB for required bias
* @rdev: regulator device
* @abb: pointer to the abb instance
* @info: ABB info to program
*
* Return: 0 on success or appropriate error value when fails
*/
static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb,
struct ti_abb_info *info)
{
const struct ti_abb_reg *regs = abb->regs;
struct device *dev = &rdev->dev;
int ret;
ret = ti_abb_clear_all_txdone(dev, abb);
if (ret)
goto out;
ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg,
abb->base);
switch (info->opp_sel) {
case TI_ABB_SLOW_OPP:
ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base);
break;
case TI_ABB_FAST_OPP:
ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base);
break;
}
/* program next state of ABB ldo */
ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg,
abb->base);
/* program LDO VBB vset override if needed */
if (abb->ldo_base)
ti_abb_program_ldovbb(dev, abb, info);
/* Initiate ABB ldo change */
ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base);
/* Wait for ABB LDO to complete transition to new Bias setting */
ret = ti_abb_wait_txdone(dev, abb);
if (ret)
goto out;
ret = ti_abb_clear_all_txdone(dev, abb);
if (ret)
goto out;
out:
return ret;
}
/**
* ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO
* @rdev: regulator device
* @sel: selector to index into required ABB LDO settings (maps to
* regulator descriptor's volt_table)
*
* Return: 0 on success or appropriate error value when fails
*/
static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
{
const struct regulator_desc *desc = rdev->desc;
struct ti_abb *abb = rdev_get_drvdata(rdev);
struct device *dev = &rdev->dev;
struct ti_abb_info *info, *oinfo;
int ret = 0;
if (!abb) {
dev_err_ratelimited(dev, "%s: No regulator drvdata\n",
__func__);
return -ENODEV;
}
if (!desc->n_voltages || !abb->info) {
dev_err_ratelimited(dev,
"%s: No valid voltage table entries?\n",
__func__);
return -EINVAL;
}
if (sel >= desc->n_voltages) {
dev_err(dev, "%s: sel idx(%d) >= n_voltages(%d)\n", __func__,
sel, desc->n_voltages);
return -EINVAL;
}
/* If we are in the same index as we were, nothing to do here! */
if (sel == abb->current_info_idx) {
dev_dbg(dev, "%s: Already at sel=%d\n", __func__, sel);
return ret;
}
/* If data is exactly the same, then just update index, no change */
info = &abb->info[sel];
oinfo = &abb->info[abb->current_info_idx];
if (!memcmp(info, oinfo, sizeof(*info))) {
dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__,
sel, abb->current_info_idx);
goto out;
}
ret = ti_abb_set_opp(rdev, abb, info);
out:
if (!ret)
abb->current_info_idx = sel;
else
dev_err_ratelimited(dev,
"%s: Volt[%d] idx[%d] mode[%d] Fail(%d)\n",
__func__, desc->volt_table[sel], sel,
info->opp_sel, ret);
return ret;
}
/**
* ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting
* @rdev: regulator device
*
* Return: 0 on success or appropriate error value when fails
*/
static int ti_abb_get_voltage_sel(struct regulator_dev *rdev)
{
const struct regulator_desc *desc = rdev->desc;
struct ti_abb *abb = rdev_get_drvdata(rdev);
struct device *dev = &rdev->dev;
if (!abb) {
dev_err_ratelimited(dev, "%s: No regulator drvdata\n",
__func__);
return -ENODEV;
}
if (!desc->n_voltages || !abb->info) {
dev_err_ratelimited(dev,
"%s: No valid voltage table entries?\n",
__func__);
return -EINVAL;
}
if (abb->current_info_idx >= (int)desc->n_voltages) {
dev_err(dev, "%s: Corrupted data? idx(%d) >= n_voltages(%d)\n",
__func__, abb->current_info_idx, desc->n_voltages);
return -EINVAL;
}
return abb->current_info_idx;
}
/**
* ti_abb_init_timings() - setup ABB clock timing for the current platform
* @dev: device
* @abb: pointer to the abb instance
*
* Return: 0 if timing is updated, else returns error result.
*/
static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb)
{
u32 clock_cycles;
u32 clk_rate, sr2_wt_cnt_val, cycle_rate;
const struct ti_abb_reg *regs = abb->regs;
int ret;
char *pname = "ti,settling-time";
/* read device tree properties */
ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time);
if (ret) {
dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
return ret;
}
/* ABB LDO cannot be settle in 0 time */
if (!abb->settling_time) {
dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
return -EINVAL;
}
pname = "ti,clock-cycles";
ret = of_property_read_u32(dev->of_node, pname, &clock_cycles);
if (ret) {
dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
return ret;
}
/* ABB LDO cannot be settle in 0 clock cycles */
if (!clock_cycles) {
dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
return -EINVAL;
}
abb->clk = devm_clk_get(dev, NULL);
if (IS_ERR(abb->clk)) {
ret = PTR_ERR(abb->clk);
dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret);
return ret;
}
/*
* SR2_WTCNT_VALUE is the settling time for the ABB ldo after a
* transition and must be programmed with the correct time at boot.
* The value programmed into the register is the number of SYS_CLK
* clock cycles that match a given wall time profiled for the ldo.
* This value depends on:
* settling time of ldo in micro-seconds (varies per OMAP family)
* # of clock cycles per SYS_CLK period (varies per OMAP family)
* the SYS_CLK frequency in MHz (varies per board)
* The formula is:
*
* ldo settling time (in micro-seconds)
* SR2_WTCNT_VALUE = ------------------------------------------
* (# system clock cycles) * (sys_clk period)
*
* Put another way:
*
* SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate))
*
* To avoid dividing by zero multiply both "# clock cycles" and
* "settling time" by 10 such that the final result is the one we want.
*/
/* Convert SYS_CLK rate to MHz & prevent divide by zero */
clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000);
/* Calculate cycle rate */
cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate);
/* Calulate SR2_WTCNT_VALUE */
sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate);
dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__,
clk_get_rate(abb->clk), sr2_wt_cnt_val);
ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg,
abb->base);
return 0;
}
/**
* ti_abb_init_table() - Initialize ABB table from device tree
* @dev: device
* @abb: pointer to the abb instance
* @rinit_data: regulator initdata
*
* Return: 0 on success or appropriate error value when fails
*/
static int ti_abb_init_table(struct device *dev, struct ti_abb *abb,
struct regulator_init_data *rinit_data)
{
struct ti_abb_info *info;
const struct property *prop;
const __be32 *abb_info;
const u32 num_values = 6;
char *pname = "ti,abb_info";
u32 num_entries, i;
unsigned int *volt_table;
int min_uV = INT_MAX, max_uV = 0;
struct regulation_constraints *c = &rinit_data->constraints;
prop = of_find_property(dev->of_node, pname, NULL);
if (!prop) {
dev_err(dev, "No '%s' property?\n", pname);
return -ENODEV;
}
if (!prop->value) {
dev_err(dev, "Empty '%s' property?\n", pname);
return -ENODATA;
}
/*
* Each abb_info is a set of n-tuple, where n is num_values, consisting
* of voltage and a set of detection logic for ABB information for that
* voltage to apply.
*/
num_entries = prop->length / sizeof(u32);
if (!num_entries || (num_entries % num_values)) {
dev_err(dev, "All '%s' list entries need %d vals\n", pname,
num_values);
return -EINVAL;
}
num_entries /= num_values;
info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL);
if (!info) {
dev_err(dev, "Can't allocate info table for '%s' property\n",
pname);
return -ENOMEM;
}
abb->info = info;
volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries,
GFP_KERNEL);
if (!volt_table) {
dev_err(dev, "Can't allocate voltage table for '%s' property\n",
pname);
return -ENOMEM;
}
abb->rdesc.n_voltages = num_entries;
abb->rdesc.volt_table = volt_table;
/* We do not know where the OPP voltage is at the moment */
abb->current_info_idx = -EINVAL;
abb_info = prop->value;
for (i = 0; i < num_entries; i++, info++, volt_table++) {
u32 efuse_offset, rbb_mask, fbb_mask, vset_mask;
u32 efuse_val;
/* NOTE: num_values should equal to entries picked up here */
*volt_table = be32_to_cpup(abb_info++);
info->opp_sel = be32_to_cpup(abb_info++);
efuse_offset = be32_to_cpup(abb_info++);
rbb_mask = be32_to_cpup(abb_info++);
fbb_mask = be32_to_cpup(abb_info++);
vset_mask = be32_to_cpup(abb_info++);
dev_dbg(dev,
"[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n",
i, *volt_table, info->opp_sel, efuse_offset, rbb_mask,
fbb_mask, vset_mask);
/* Find min/max for voltage set */
if (min_uV > *volt_table)
min_uV = *volt_table;
if (max_uV < *volt_table)
max_uV = *volt_table;
if (!abb->efuse_base) {
/* Ignore invalid data, but warn to help cleanup */
if (efuse_offset || rbb_mask || fbb_mask || vset_mask)
dev_err(dev, "prop '%s': v=%d,bad efuse/mask\n",
pname, *volt_table);
goto check_abb;
}
efuse_val = readl(abb->efuse_base + efuse_offset);
/* Use ABB recommendation from Efuse */
if (efuse_val & rbb_mask)
info->opp_sel = TI_ABB_SLOW_OPP;
else if (efuse_val & fbb_mask)
info->opp_sel = TI_ABB_FAST_OPP;
else if (rbb_mask || fbb_mask)
info->opp_sel = TI_ABB_NOMINAL_OPP;
dev_dbg(dev,
"[%d]v=%d efusev=0x%x final ABB=%d\n",
i, *volt_table, efuse_val, info->opp_sel);
/* Use recommended Vset bits from Efuse */
if (!abb->ldo_base) {
if (vset_mask)
dev_err(dev, "prop'%s':v=%d vst=%x LDO base?\n",
pname, *volt_table, vset_mask);
continue;
}
info->vset = efuse_val & vset_mask >> __ffs(vset_mask);
dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset);
check_abb:
switch (info->opp_sel) {
case TI_ABB_NOMINAL_OPP:
case TI_ABB_FAST_OPP:
case TI_ABB_SLOW_OPP:
/* Valid values */
break;
default:
dev_err(dev, "%s:[%d]v=%d, ABB=%d is invalid! Abort!\n",
__func__, i, *volt_table, info->opp_sel);
return -EINVAL;
}
}
/* Setup the min/max voltage constraints from the supported list */
c->min_uV = min_uV;
c->max_uV = max_uV;
return 0;
}
static struct regulator_ops ti_abb_reg_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = ti_abb_set_voltage_sel,
.get_voltage_sel = ti_abb_get_voltage_sel,
};
/* Default ABB block offsets, IF this changes in future, create new one */
static const struct ti_abb_reg abb_regs_v1 = {
/* WARNING: registers are wrongly documented in TRM */
.setup_reg = 0x04,
.control_reg = 0x00,
.sr2_wtcnt_value_mask = (0xff << 8),
.fbb_sel_mask = (0x01 << 2),
.rbb_sel_mask = (0x01 << 1),
.sr2_en_mask = (0x01 << 0),
.opp_change_mask = (0x01 << 2),
.opp_sel_mask = (0x03 << 0),
};
static const struct ti_abb_reg abb_regs_v2 = {
.setup_reg = 0x00,
.control_reg = 0x04,
.sr2_wtcnt_value_mask = (0xff << 8),
.fbb_sel_mask = (0x01 << 2),
.rbb_sel_mask = (0x01 << 1),
.sr2_en_mask = (0x01 << 0),
.opp_change_mask = (0x01 << 2),
.opp_sel_mask = (0x03 << 0),
};
static const struct of_device_id ti_abb_of_match[] = {
{.compatible = "ti,abb-v1", .data = &abb_regs_v1},
{.compatible = "ti,abb-v2", .data = &abb_regs_v2},
{ },
};
MODULE_DEVICE_TABLE(of, ti_abb_of_match);
/**
* ti_abb_probe() - Initialize an ABB ldo instance
* @pdev: ABB platform device
*
* Initializes an individual ABB LDO for required Body-Bias. ABB is used to
* addional bias supply to SoC modules for power savings or mandatory stability
* configuration at certain Operating Performance Points(OPPs).
*
* Return: 0 on success or appropriate error value when fails
*/
static int ti_abb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct resource *res;
struct ti_abb *abb;
struct regulator_init_data *initdata = NULL;
struct regulator_dev *rdev = NULL;
struct regulator_desc *desc;
struct regulation_constraints *c;
struct regulator_config config = { };
char *pname;
int ret = 0;
match = of_match_device(ti_abb_of_match, dev);
if (!match) {
/* We do not expect this to happen */
ret = -ENODEV;
dev_err(dev, "%s: Unable to match device\n", __func__);
goto err;
}
if (!match->data) {
ret = -EINVAL;
dev_err(dev, "%s: Bad data in match\n", __func__);
goto err;
}
abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL);
if (!abb) {
dev_err(dev, "%s: Unable to allocate ABB struct\n", __func__);
ret = -ENOMEM;
goto err;
}
abb->regs = match->data;
/* Map ABB resources */
pname = "base-address";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
if (!res) {
dev_err(dev, "Missing '%s' IO resource\n", pname);
ret = -ENODEV;
goto err;
}
abb->base = devm_ioremap_resource(dev, res);
if (IS_ERR(abb->base)) {
ret = PTR_ERR(abb->base);
goto err;
}
pname = "int-address";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
if (!res) {
dev_err(dev, "Missing '%s' IO resource\n", pname);
ret = -ENODEV;
goto err;
}
/*
* We may have shared interrupt register offsets which are
* write-1-to-clear between domains ensuring exclusivity.
*/
abb->int_base = devm_ioremap_nocache(dev, res->start,
resource_size(res));
if (!abb->int_base) {
dev_err(dev, "Unable to map '%s'\n", pname);
ret = -ENOMEM;
goto err;
}
/* Map Optional resources */
pname = "efuse-address";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
if (!res) {
dev_dbg(dev, "Missing '%s' IO resource\n", pname);
ret = -ENODEV;
goto skip_opt;
}
/*
* We may have shared efuse register offsets which are read-only
* between domains
*/
abb->efuse_base = devm_ioremap_nocache(dev, res->start,
resource_size(res));
if (!abb->efuse_base) {
dev_err(dev, "Unable to map '%s'\n", pname);
ret = -ENOMEM;
goto err;
}
pname = "ldo-address";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
if (!res) {
dev_dbg(dev, "Missing '%s' IO resource\n", pname);
ret = -ENODEV;
goto skip_opt;
}
abb->ldo_base = devm_ioremap_resource(dev, res);
if (IS_ERR(abb->ldo_base)) {
ret = PTR_ERR(abb->ldo_base);
goto err;
}
/* IF ldo_base is set, the following are mandatory */
pname = "ti,ldovbb-override-mask";
ret =
of_property_read_u32(pdev->dev.of_node, pname,
&abb->ldovbb_override_mask);
if (ret) {
dev_err(dev, "Missing '%s' (%d)\n", pname, ret);
goto err;
}
if (!abb->ldovbb_override_mask) {
dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
ret = -EINVAL;
goto err;
}
pname = "ti,ldovbb-vset-mask";
ret =
of_property_read_u32(pdev->dev.of_node, pname,
&abb->ldovbb_vset_mask);
if (ret) {
dev_err(dev, "Missing '%s' (%d)\n", pname, ret);
goto err;
}
if (!abb->ldovbb_vset_mask) {
dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
ret = -EINVAL;
goto err;
}
skip_opt:
pname = "ti,tranxdone-status-mask";
ret =
of_property_read_u32(pdev->dev.of_node, pname,
&abb->txdone_mask);
if (ret) {
dev_err(dev, "Missing '%s' (%d)\n", pname, ret);
goto err;
}
if (!abb->txdone_mask) {
dev_err(dev, "Invalid property:'%s' set as 0!\n", pname);
ret = -EINVAL;
goto err;
}
initdata = of_get_regulator_init_data(dev, pdev->dev.of_node);
if (!initdata) {
ret = -ENOMEM;
dev_err(dev, "%s: Unable to alloc regulator init data\n",
__func__);
goto err;
}
/* init ABB opp_sel table */
ret = ti_abb_init_table(dev, abb, initdata);
if (ret)
goto err;
/* init ABB timing */
ret = ti_abb_init_timings(dev, abb);
if (ret)
goto err;
desc = &abb->rdesc;
desc->name = dev_name(dev);
desc->owner = THIS_MODULE;
desc->type = REGULATOR_VOLTAGE;
desc->ops = &ti_abb_reg_ops;
c = &initdata->constraints;
if (desc->n_voltages > 1)
c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
c->always_on = true;
config.dev = dev;
config.init_data = initdata;
config.driver_data = abb;
config.of_node = pdev->dev.of_node;
rdev = regulator_register(desc, &config);
if (IS_ERR(rdev)) {
ret = PTR_ERR(rdev);
dev_err(dev, "%s: failed to register regulator(%d)\n",
__func__, ret);
goto err;
}
platform_set_drvdata(pdev, rdev);
/* Enable the ldo if not already done by bootloader */
ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base);
return 0;
err:
dev_err(dev, "%s: Failed to initialize(%d)\n", __func__, ret);
return ret;
}
/**
* ti_abb_remove() - cleanups
* @pdev: ABB platform device
*
* Return: 0
*/
static int ti_abb_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev);
return 0;
}
MODULE_ALIAS("platform:ti_abb");
static struct platform_driver ti_abb_driver = {
.probe = ti_abb_probe,
.remove = ti_abb_remove,
.driver = {
.name = "ti_abb",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ti_abb_of_match),
},
};
module_platform_driver(ti_abb_driver);
MODULE_DESCRIPTION("Texas Instruments ABB LDO regulator driver");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_LICENSE("GPL v2");

View File

@ -351,7 +351,6 @@ static int tps62360_probe(struct i2c_client *client,
int chip_id; int chip_id;
pdata = client->dev.platform_data; pdata = client->dev.platform_data;
chip_id = id->driver_data;
if (client->dev.of_node) { if (client->dev.of_node) {
const struct of_device_id *match; const struct of_device_id *match;
@ -364,6 +363,11 @@ static int tps62360_probe(struct i2c_client *client,
chip_id = (int)match->data; chip_id = (int)match->data;
if (!pdata) if (!pdata)
pdata = of_get_tps62360_platform_data(&client->dev); pdata = of_get_tps62360_platform_data(&client->dev);
} else if (id) {
chip_id = id->driver_data;
} else {
dev_err(&client->dev, "No device tree match or id table match found\n");
return -ENODEV;
} }
if (!pdata) { if (!pdata) {
@ -402,7 +406,7 @@ static int tps62360_probe(struct i2c_client *client,
return -ENODEV; return -ENODEV;
} }
tps->desc.name = id->name; tps->desc.name = client->name;
tps->desc.id = 0; tps->desc.id = 0;
tps->desc.ops = &tps62360_dcdc_ops; tps->desc.ops = &tps62360_dcdc_ops;
tps->desc.type = REGULATOR_VOLTAGE; tps->desc.type = REGULATOR_VOLTAGE;

View File

@ -405,8 +405,6 @@ static int tps65217_regulator_remove(struct platform_device *pdev)
for (i = 0; i < TPS65217_NUM_REGULATOR; i++) for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
regulator_unregister(tps->rdev[i]); regulator_unregister(tps->rdev[i]);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -330,8 +330,6 @@ static int regulator_virtual_remove(struct platform_device *pdev)
if (drvdata->enabled) if (drvdata->enabled)
regulator_disable(drvdata->regulator); regulator_disable(drvdata->regulator);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -567,8 +567,6 @@ static int wm831x_buckv_remove(struct platform_device *pdev)
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
struct wm831x *wm831x = dcdc->wm831x; struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL);
free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")),
dcdc); dcdc);
free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")),
@ -714,8 +712,6 @@ static int wm831x_buckp_remove(struct platform_device *pdev)
{ {
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")),
dcdc); dcdc);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
@ -849,8 +845,6 @@ static int wm831x_boostp_remove(struct platform_device *pdev)
{ {
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")),
dcdc); dcdc);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
@ -940,7 +934,6 @@ static int wm831x_epe_remove(struct platform_device *pdev)
{ {
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
return 0; return 0;

View File

@ -225,8 +225,6 @@ static int wm831x_isink_remove(struct platform_device *pdev)
{ {
struct wm831x_isink *isink = platform_get_drvdata(pdev); struct wm831x_isink *isink = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink);
regulator_unregister(isink->regulator); regulator_unregister(isink->regulator);

View File

@ -338,8 +338,6 @@ static int wm831x_gp_ldo_remove(struct platform_device *pdev)
{ {
struct wm831x_ldo *ldo = platform_get_drvdata(pdev); struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
free_irq(wm831x_irq(ldo->wm831x, free_irq(wm831x_irq(ldo->wm831x,
platform_get_irq_byname(pdev, "UV")), ldo); platform_get_irq_byname(pdev, "UV")), ldo);
regulator_unregister(ldo->regulator); regulator_unregister(ldo->regulator);

View File

@ -250,7 +250,6 @@ static int wm8400_regulator_remove(struct platform_device *pdev)
{ {
struct regulator_dev *rdev = platform_get_drvdata(pdev); struct regulator_dev *rdev = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(rdev); regulator_unregister(rdev);
return 0; return 0;

View File

@ -185,8 +185,6 @@ static int wm8994_ldo_remove(struct platform_device *pdev)
{ {
struct wm8994_ldo *ldo = platform_get_drvdata(pdev); struct wm8994_ldo *ldo = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(ldo->regulator); regulator_unregister(ldo->regulator);
return 0; return 0;

View File

@ -85,6 +85,19 @@ enum max77693_pmic_reg {
MAX77693_PMIC_REG_END, MAX77693_PMIC_REG_END,
}; };
/* MAX77693 CHG_CNFG_00 register */
#define CHG_CNFG_00_CHG_MASK 0x1
#define CHG_CNFG_00_BUCK_MASK 0x4
/* MAX77693 CHG_CNFG_09 Register */
#define CHG_CNFG_09_CHGIN_ILIM_MASK 0x7F
/* MAX77693 CHG_CTRL Register */
#define SAFEOUT_CTRL_SAFEOUT1_MASK 0x3
#define SAFEOUT_CTRL_SAFEOUT2_MASK 0xC
#define SAFEOUT_CTRL_ENSAFEOUT1_MASK 0x40
#define SAFEOUT_CTRL_ENSAFEOUT2_MASK 0x80
/* Slave addr = 0x4A: MUIC */ /* Slave addr = 0x4A: MUIC */
enum max77693_muic_reg { enum max77693_muic_reg {
MAX77693_MUIC_REG_ID = 0x00, MAX77693_MUIC_REG_ID = 0x00,

View File

@ -30,6 +30,20 @@
#ifndef __LINUX_MFD_MAX77693_H #ifndef __LINUX_MFD_MAX77693_H
#define __LINUX_MFD_MAX77693_H #define __LINUX_MFD_MAX77693_H
/* MAX77686 regulator IDs */
enum max77693_regulators {
MAX77693_ESAFEOUT1 = 0,
MAX77693_ESAFEOUT2,
MAX77693_CHARGER,
MAX77693_REG_MAX,
};
struct max77693_regulator_data {
int id;
struct regulator_init_data *initdata;
struct device_node *of_node;
};
struct max77693_reg_data { struct max77693_reg_data {
u8 addr; u8 addr;
u8 data; u8 data;
@ -52,6 +66,10 @@ struct max77693_muic_platform_data {
struct max77693_platform_data { struct max77693_platform_data {
int wakeup; int wakeup;
/* regulator data */
struct max77693_regulator_data *regulators;
int num_regulators;
/* muic data */ /* muic data */
struct max77693_muic_platform_data *muic_data; struct max77693_muic_platform_data *muic_data;
}; };

View File

@ -336,8 +336,4 @@ static inline int ab8500_regulator_debug_exit(struct platform_device *pdev)
} }
#endif #endif
/* AB8500 external regulator functions. */
int ab8500_ext_regulator_init(struct platform_device *pdev);
void ab8500_ext_regulator_exit(struct platform_device *pdev);
#endif #endif

View File

@ -165,6 +165,7 @@ int regulator_count_voltages(struct regulator *regulator);
int regulator_list_voltage(struct regulator *regulator, unsigned selector); int regulator_list_voltage(struct regulator *regulator, unsigned selector);
int regulator_is_supported_voltage(struct regulator *regulator, int regulator_is_supported_voltage(struct regulator *regulator,
int min_uV, int max_uV); int min_uV, int max_uV);
unsigned int regulator_get_linear_step(struct regulator *regulator);
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_set_voltage_time(struct regulator *regulator, int regulator_set_voltage_time(struct regulator *regulator,
int old_uV, int new_uV); int old_uV, int new_uV);