Merge remote-tracking branches 'regulator/topic/drivers', 'regulator/topic/enable', 'regulator/topic/fan53555', 'regulator/topic/hi6421' and 'regulator/topic/isl9305' into regulator-next
This commit is contained in:
Коммит
a81bf3c4fc
|
@ -0,0 +1,23 @@
|
|||
Binding for Fairchild FAN53555 regulators
|
||||
|
||||
Required properties:
|
||||
- compatible: one of "fcs,fan53555", "silergy,syr827", "silergy,syr828"
|
||||
- reg: I2C address
|
||||
|
||||
Optional properties:
|
||||
- fcs,suspend-voltage-selector: declare which of the two available
|
||||
voltage selector registers should be used for the suspend
|
||||
voltage. The other one is used for the runtime voltage setting
|
||||
Possible values are either <0> or <1>
|
||||
- vin-supply: regulator supplying the vin pin
|
||||
|
||||
Example:
|
||||
|
||||
regulator@40 {
|
||||
compatible = "fcs,fan53555";
|
||||
regulator-name = "fan53555";
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
vin-supply = <&parent_reg>;
|
||||
fcs,suspend-voltage-selector = <1>;
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
Intersil ISL9305/ISL9305H voltage regulator
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "isl,isl9305" or "isl,isl9305h"
|
||||
- reg: I2C slave address, usually 0x68.
|
||||
- regulators: A node that houses a sub-node for each regulator within the
|
||||
device. Each sub-node is identified using the node's name, with valid
|
||||
values being "dcd1", "dcd2", "ldo1" and "ldo2". The content of each sub-node
|
||||
is defined by the standard binding for regulators; see regulator.txt.
|
||||
- VINDCD1-supply: A phandle to a regulator node supplying VINDCD1.
|
||||
VINDCD2-supply: A phandle to a regulator node supplying VINDCD2.
|
||||
VINLDO1-supply: A phandle to a regulator node supplying VINLDO1.
|
||||
VINLDO2-supply: A phandle to a regulator node supplying VINLDO2.
|
||||
|
||||
Optional properties:
|
||||
- Per-regulator optional properties are defined in regulator.txt
|
||||
|
||||
Example
|
||||
|
||||
pmic: isl9305@68 {
|
||||
compatible = "isl,isl9305";
|
||||
reg = <0x68>;
|
||||
|
||||
VINDCD1-supply = <&system_power>;
|
||||
VINDCD2-supply = <&system_power>;
|
||||
VINLDO1-supply = <&system_power>;
|
||||
VINLDO2-supply = <&system_power>;
|
||||
|
||||
regulators {
|
||||
dcd1 {
|
||||
regulator-name = "VDD_DSP";
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
pwm regulator bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "pwm-regulator"
|
||||
- pwms: OF device-tree PWM specification (see PWM binding pwm.txt)
|
||||
- voltage-table: voltage and duty table, include 2 members in each set of
|
||||
brackets, first one is voltage(unit: uv), the next is duty(unit: percent)
|
||||
|
||||
Any property defined as part of the core regulator binding defined in
|
||||
regulator.txt can also be used.
|
||||
|
||||
Example:
|
||||
pwm_regulator {
|
||||
compatible = "pwm-regulator;
|
||||
pwms = <&pwm1 0 8448 0>;
|
||||
|
||||
voltage-table = <1114000 0>,
|
||||
<1095000 10>,
|
||||
<1076000 20>,
|
||||
<1056000 30>,
|
||||
<1036000 40>,
|
||||
<1016000 50>;
|
||||
|
||||
regulator-min-microvolt = <1016000>;
|
||||
regulator-max-microvolt = <1114000>;
|
||||
regulator-name = "vdd_logic";
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
SKY81452 voltage regulator
|
||||
|
||||
Required properties:
|
||||
- any required generic properties defined in regulator.txt
|
||||
|
||||
Optional properties:
|
||||
- any available generic properties defined in regulator.txt
|
||||
|
||||
Example:
|
||||
|
||||
regulator {
|
||||
/* generic regulator properties */
|
||||
regulator-name = "touch_en";
|
||||
regulator-min-microvolt = <4500000>;
|
||||
regulator-max-microvolt = <8000000>;
|
||||
};
|
|
@ -49,6 +49,7 @@ epson Seiko Epson Corp.
|
|||
est ESTeem Wireless Modems
|
||||
eukrea Eukréa Electromatique
|
||||
excito Excito
|
||||
fcs Fairchild Semiconductor
|
||||
fsl Freescale Semiconductor
|
||||
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
|
||||
gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
|
||||
|
@ -124,6 +125,7 @@ sil Silicon Image
|
|||
silabs Silicon Laboratories
|
||||
simtek
|
||||
sii Seiko Instruments, Inc.
|
||||
silergy Silergy Corp.
|
||||
sirf SiRF Technology, Inc.
|
||||
smsc Standard Microsystems Corporation
|
||||
snps Synopsys, Inc.
|
||||
|
|
|
@ -241,6 +241,23 @@ config REGULATOR_GPIO
|
|||
and the platform has to provide a mapping of GPIO-states
|
||||
to target volts/amps.
|
||||
|
||||
config REGULATOR_HI6421
|
||||
tristate "HiSilicon Hi6421 PMIC voltage regulator support"
|
||||
depends on MFD_HI6421_PMIC && OF
|
||||
help
|
||||
This driver provides support for the voltage regulators on the
|
||||
HiSilicon Hi6421 PMU / Codec IC.
|
||||
Hi6421 is a multi-function device which, on regulator part, provides
|
||||
21 general purpose LDOs, 3 dedicated LDOs, and 5 BUCKs. All
|
||||
of them come with support to either ECO (idle) or sleep mode.
|
||||
|
||||
config REGULATOR_ISL9305
|
||||
tristate "Intersil ISL9305 regulator"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports ISL9305 voltage regulator chip.
|
||||
|
||||
config REGULATOR_ISL6271A
|
||||
tristate "Intersil ISL6271A Power regulator"
|
||||
depends on I2C
|
||||
|
@ -450,6 +467,25 @@ config REGULATOR_PFUZE100
|
|||
Say y here to support the regulators found on the Freescale
|
||||
PFUZE100/PFUZE200 PMIC.
|
||||
|
||||
config REGULATOR_PWM
|
||||
tristate "PWM voltage regulator"
|
||||
depends on PWM
|
||||
help
|
||||
This driver supports PWM controlled voltage regulators. PWM
|
||||
duty cycle can increase or decrease the voltage.
|
||||
|
||||
config REGULATOR_QCOM_RPM
|
||||
tristate "Qualcomm RPM regulator driver"
|
||||
depends on MFD_QCOM_RPM
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
regulators exposed by the Resource Power Manager found in Qualcomm
|
||||
8660, 8960 and 8064 based devices.
|
||||
|
||||
Say M here if you want to include support for the regulators on the
|
||||
Qualcomm RPM as a module. The module will be named
|
||||
"qcom_rpm-regulator".
|
||||
|
||||
config REGULATOR_RC5T583
|
||||
tristate "RICOH RC5T583 Power regulators"
|
||||
depends on MFD_RC5T583
|
||||
|
@ -484,11 +520,16 @@ config REGULATOR_S5M8767
|
|||
via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
|
||||
supports DVS mode with 8bits of output voltage control.
|
||||
|
||||
config REGULATOR_ST_PWM
|
||||
tristate "STMicroelectronics PWM voltage regulator"
|
||||
depends on ARCH_STI
|
||||
config REGULATOR_SKY81452
|
||||
tristate "Skyworks Solutions SKY81452 voltage regulator"
|
||||
depends on SKY81452
|
||||
help
|
||||
This driver supports ST's PWM controlled voltage regulators.
|
||||
This driver supports Skyworks SKY81452 voltage output regulator
|
||||
via I2C bus. SKY81452 has one voltage linear regulator can be
|
||||
programmed from 4.5V to 20V.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called sky81452-regulator.
|
||||
|
||||
config REGULATOR_TI_ABB
|
||||
tristate "TI Adaptive Body Bias on-chip LDO"
|
||||
|
|
|
@ -32,7 +32,9 @@ obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
|
|||
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
|
||||
obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
|
||||
obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
|
||||
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
|
||||
obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
|
||||
obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
|
||||
|
@ -55,8 +57,10 @@ obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o
|
|||
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
|
||||
|
@ -65,7 +69,7 @@ obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
|
||||
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
|
||||
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
|
||||
obj-$(CONFIG_REGULATOR_ST_PWM) += st-pwm.o
|
||||
obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
|
||||
obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
|
||||
|
|
|
@ -839,7 +839,7 @@ static void print_constraints(struct regulator_dev *rdev)
|
|||
static int machine_constraints_voltage(struct regulator_dev *rdev,
|
||||
struct regulation_constraints *constraints)
|
||||
{
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
int ret;
|
||||
|
||||
/* do we need to apply the constraint voltage */
|
||||
|
@ -938,7 +938,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
|
|||
static int machine_constraints_current(struct regulator_dev *rdev,
|
||||
struct regulation_constraints *constraints)
|
||||
{
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
int ret;
|
||||
|
||||
if (!constraints->min_uA && !constraints->max_uA)
|
||||
|
@ -982,7 +982,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
|
|||
const struct regulation_constraints *constraints)
|
||||
{
|
||||
int ret = 0;
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
|
||||
if (constraints)
|
||||
rdev->constraints = kmemdup(constraints, sizeof(*constraints),
|
||||
|
@ -1759,6 +1759,45 @@ static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _regulator_enable_delay - a delay helper function
|
||||
* @delay: time to delay in microseconds
|
||||
*
|
||||
* Delay for the requested amount of time as per the guidelines in:
|
||||
*
|
||||
* Documentation/timers/timers-howto.txt
|
||||
*
|
||||
* The assumption here is that regulators will never be enabled in
|
||||
* atomic context and therefore sleeping functions can be used.
|
||||
*/
|
||||
static void _regulator_enable_delay(unsigned int delay)
|
||||
{
|
||||
unsigned int ms = delay / 1000;
|
||||
unsigned int us = delay % 1000;
|
||||
|
||||
if (ms > 0) {
|
||||
/*
|
||||
* For small enough values, handle super-millisecond
|
||||
* delays in the usleep_range() call below.
|
||||
*/
|
||||
if (ms < 20)
|
||||
us += ms * 1000;
|
||||
else
|
||||
msleep(ms);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the scheduler some room to coalesce with any other
|
||||
* wakeup sources. For delays shorter than 10 us, don't even
|
||||
* bother setting up high-resolution timers and just busy-
|
||||
* loop.
|
||||
*/
|
||||
if (us >= 10)
|
||||
usleep_range(us, us + 100);
|
||||
else
|
||||
udelay(us);
|
||||
}
|
||||
|
||||
static int _regulator_do_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
int ret, delay;
|
||||
|
@ -1774,6 +1813,31 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
|
|||
|
||||
trace_regulator_enable(rdev_get_name(rdev));
|
||||
|
||||
if (rdev->desc->off_on_delay) {
|
||||
/* if needed, keep a distance of off_on_delay from last time
|
||||
* this regulator was disabled.
|
||||
*/
|
||||
unsigned long start_jiffy = jiffies;
|
||||
unsigned long intended, max_delay, remaining;
|
||||
|
||||
max_delay = usecs_to_jiffies(rdev->desc->off_on_delay);
|
||||
intended = rdev->last_off_jiffy + max_delay;
|
||||
|
||||
if (time_before(start_jiffy, intended)) {
|
||||
/* calc remaining jiffies to deal with one-time
|
||||
* timer wrapping.
|
||||
* in case of multiple timer wrapping, either it can be
|
||||
* detected by out-of-range remaining, or it cannot be
|
||||
* detected and we gets a panelty of
|
||||
* _regulator_enable_delay().
|
||||
*/
|
||||
remaining = intended - start_jiffy;
|
||||
if (remaining <= max_delay)
|
||||
_regulator_enable_delay(
|
||||
jiffies_to_usecs(remaining));
|
||||
}
|
||||
}
|
||||
|
||||
if (rdev->ena_pin) {
|
||||
ret = regulator_ena_gpio_ctrl(rdev, true);
|
||||
if (ret < 0)
|
||||
|
@ -1792,40 +1856,7 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
|
|||
* together. */
|
||||
trace_regulator_enable_delay(rdev_get_name(rdev));
|
||||
|
||||
/*
|
||||
* Delay for the requested amount of time as per the guidelines in:
|
||||
*
|
||||
* Documentation/timers/timers-howto.txt
|
||||
*
|
||||
* The assumption here is that regulators will never be enabled in
|
||||
* atomic context and therefore sleeping functions can be used.
|
||||
*/
|
||||
if (delay) {
|
||||
unsigned int ms = delay / 1000;
|
||||
unsigned int us = delay % 1000;
|
||||
|
||||
if (ms > 0) {
|
||||
/*
|
||||
* For small enough values, handle super-millisecond
|
||||
* delays in the usleep_range() call below.
|
||||
*/
|
||||
if (ms < 20)
|
||||
us += ms * 1000;
|
||||
else
|
||||
msleep(ms);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the scheduler some room to coalesce with any other
|
||||
* wakeup sources. For delays shorter than 10 us, don't even
|
||||
* bother setting up high-resolution timers and just busy-
|
||||
* loop.
|
||||
*/
|
||||
if (us >= 10)
|
||||
usleep_range(us, us + 100);
|
||||
else
|
||||
udelay(us);
|
||||
}
|
||||
_regulator_enable_delay(delay);
|
||||
|
||||
trace_regulator_enable_complete(rdev_get_name(rdev));
|
||||
|
||||
|
@ -1919,6 +1950,12 @@ static int _regulator_do_disable(struct regulator_dev *rdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* cares about last_off_jiffy only if off_on_delay is required by
|
||||
* device.
|
||||
*/
|
||||
if (rdev->desc->off_on_delay)
|
||||
rdev->last_off_jiffy = jiffies;
|
||||
|
||||
trace_regulator_disable_complete(rdev_get_name(rdev));
|
||||
|
||||
return 0;
|
||||
|
@ -2208,9 +2245,9 @@ EXPORT_SYMBOL_GPL(regulator_count_voltages);
|
|||
*/
|
||||
int regulator_list_voltage(struct regulator *regulator, unsigned selector)
|
||||
{
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
int ret;
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
int ret;
|
||||
|
||||
if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1 && !selector)
|
||||
return rdev->desc->fixed_uV;
|
||||
|
@ -2270,8 +2307,8 @@ int regulator_get_hardware_vsel_register(struct regulator *regulator,
|
|||
unsigned *vsel_reg,
|
||||
unsigned *vsel_mask)
|
||||
{
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
|
||||
if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -2297,8 +2334,8 @@ EXPORT_SYMBOL_GPL(regulator_get_hardware_vsel_register);
|
|||
int regulator_list_hardware_vsel(struct regulator *regulator,
|
||||
unsigned selector)
|
||||
{
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
|
||||
if (selector >= rdev->desc->n_voltages)
|
||||
return -EINVAL;
|
||||
|
@ -2572,8 +2609,8 @@ EXPORT_SYMBOL_GPL(regulator_set_voltage);
|
|||
int regulator_set_voltage_time(struct regulator *regulator,
|
||||
int old_uV, int new_uV)
|
||||
{
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
int old_sel = -1;
|
||||
int new_sel = -1;
|
||||
int voltage;
|
||||
|
@ -3336,9 +3373,9 @@ EXPORT_SYMBOL_GPL(regulator_mode_to_status);
|
|||
*/
|
||||
static int add_regulator_attributes(struct regulator_dev *rdev)
|
||||
{
|
||||
struct device *dev = &rdev->dev;
|
||||
struct regulator_ops *ops = rdev->desc->ops;
|
||||
int status = 0;
|
||||
struct device *dev = &rdev->dev;
|
||||
const struct regulator_ops *ops = rdev->desc->ops;
|
||||
int status = 0;
|
||||
|
||||
/* some attributes need specific methods to be displayed */
|
||||
if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
|
||||
|
@ -3516,12 +3553,17 @@ regulator_register(const struct regulator_desc *regulator_desc,
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
init_data = config->init_data;
|
||||
|
||||
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
|
||||
if (rdev == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init_data = regulator_of_get_init_data(dev, regulator_desc,
|
||||
&rdev->dev.of_node);
|
||||
if (!init_data) {
|
||||
init_data = config->init_data;
|
||||
rdev->dev.of_node = of_node_get(config->of_node);
|
||||
}
|
||||
|
||||
mutex_lock(®ulator_list_mutex);
|
||||
|
||||
mutex_init(&rdev->mutex);
|
||||
|
@ -3548,7 +3590,6 @@ regulator_register(const struct regulator_desc *regulator_desc,
|
|||
|
||||
/* register with sysfs */
|
||||
rdev->dev.class = ®ulator_class;
|
||||
rdev->dev.of_node = of_node_get(config->of_node);
|
||||
rdev->dev.parent = dev;
|
||||
dev_set_name(&rdev->dev, "regulator.%d",
|
||||
atomic_inc_return(®ulator_no) - 1);
|
||||
|
@ -3905,7 +3946,7 @@ core_initcall(regulator_init);
|
|||
static int __init regulator_init_complete(void)
|
||||
{
|
||||
struct regulator_dev *rdev;
|
||||
struct regulator_ops *ops;
|
||||
const struct regulator_ops *ops;
|
||||
struct regulation_constraints *c;
|
||||
int enabled, ret;
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -50,6 +52,11 @@
|
|||
|
||||
#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */
|
||||
|
||||
enum fan53555_vendor {
|
||||
FAN53555_VENDOR_FAIRCHILD = 0,
|
||||
FAN53555_VENDOR_SILERGY,
|
||||
};
|
||||
|
||||
/* IC Type */
|
||||
enum {
|
||||
FAN53555_CHIP_ID_00 = 0,
|
||||
|
@ -60,7 +67,12 @@ enum {
|
|||
FAN53555_CHIP_ID_05,
|
||||
};
|
||||
|
||||
enum {
|
||||
SILERGY_SYR82X = 8,
|
||||
};
|
||||
|
||||
struct fan53555_device_info {
|
||||
enum fan53555_vendor vendor;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct regulator_desc desc;
|
||||
|
@ -135,6 +147,38 @@ static unsigned int fan53555_get_mode(struct regulator_dev *rdev)
|
|||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static int slew_rates[] = {
|
||||
64000,
|
||||
32000,
|
||||
16000,
|
||||
8000,
|
||||
4000,
|
||||
2000,
|
||||
1000,
|
||||
500,
|
||||
};
|
||||
|
||||
static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
|
||||
{
|
||||
struct fan53555_device_info *di = rdev_get_drvdata(rdev);
|
||||
int regval = -1, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(slew_rates); i++) {
|
||||
if (ramp <= slew_rates[i])
|
||||
regval = i;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (regval < 0) {
|
||||
dev_err(di->dev, "unsupported ramp value %d\n", ramp);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_update_bits(di->regmap, FAN53555_CONTROL,
|
||||
CTL_SLEW_MASK, regval << CTL_SLEW_SHIFT);
|
||||
}
|
||||
|
||||
static struct regulator_ops fan53555_regulator_ops = {
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
|
@ -146,32 +190,11 @@ static struct regulator_ops fan53555_regulator_ops = {
|
|||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.set_mode = fan53555_set_mode,
|
||||
.get_mode = fan53555_get_mode,
|
||||
.set_ramp_delay = fan53555_set_ramp,
|
||||
};
|
||||
|
||||
/* For 00,01,03,05 options:
|
||||
* VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
|
||||
* For 04 option:
|
||||
* VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
|
||||
* */
|
||||
static int fan53555_device_setup(struct fan53555_device_info *di,
|
||||
struct fan53555_platform_data *pdata)
|
||||
static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
|
||||
{
|
||||
unsigned int reg, data, mask;
|
||||
|
||||
/* Setup voltage control register */
|
||||
switch (pdata->sleep_vsel_id) {
|
||||
case FAN53555_VSEL_ID_0:
|
||||
di->sleep_reg = FAN53555_VSEL0;
|
||||
di->vol_reg = FAN53555_VSEL1;
|
||||
break;
|
||||
case FAN53555_VSEL_ID_1:
|
||||
di->sleep_reg = FAN53555_VSEL1;
|
||||
di->vol_reg = FAN53555_VSEL0;
|
||||
break;
|
||||
default:
|
||||
dev_err(di->dev, "Invalid VSEL ID!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Init voltage range and step */
|
||||
switch (di->chip_id) {
|
||||
case FAN53555_CHIP_ID_00:
|
||||
|
@ -187,18 +210,68 @@ static int fan53555_device_setup(struct fan53555_device_info *di,
|
|||
break;
|
||||
default:
|
||||
dev_err(di->dev,
|
||||
"Chip ID[%d]\n not supported!\n", di->chip_id);
|
||||
"Chip ID %d not supported!\n", di->chip_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Init slew rate */
|
||||
if (pdata->slew_rate & 0x7)
|
||||
di->slew_rate = pdata->slew_rate;
|
||||
else
|
||||
di->slew_rate = FAN53555_SLEW_RATE_64MV;
|
||||
reg = FAN53555_CONTROL;
|
||||
data = di->slew_rate << CTL_SLEW_SHIFT;
|
||||
mask = CTL_SLEW_MASK;
|
||||
return regmap_update_bits(di->regmap, reg, mask, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fan53555_voltages_setup_silergy(struct fan53555_device_info *di)
|
||||
{
|
||||
/* Init voltage range and step */
|
||||
switch (di->chip_id) {
|
||||
case SILERGY_SYR82X:
|
||||
di->vsel_min = 712500;
|
||||
di->vsel_step = 12500;
|
||||
break;
|
||||
default:
|
||||
dev_err(di->dev,
|
||||
"Chip ID %d not supported!\n", di->chip_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For 00,01,03,05 options:
|
||||
* VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
|
||||
* For 04 option:
|
||||
* VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
|
||||
* */
|
||||
static int fan53555_device_setup(struct fan53555_device_info *di,
|
||||
struct fan53555_platform_data *pdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Setup voltage control register */
|
||||
switch (pdata->sleep_vsel_id) {
|
||||
case FAN53555_VSEL_ID_0:
|
||||
di->sleep_reg = FAN53555_VSEL0;
|
||||
di->vol_reg = FAN53555_VSEL1;
|
||||
break;
|
||||
case FAN53555_VSEL_ID_1:
|
||||
di->sleep_reg = FAN53555_VSEL1;
|
||||
di->vol_reg = FAN53555_VSEL0;
|
||||
break;
|
||||
default:
|
||||
dev_err(di->dev, "Invalid VSEL ID!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (di->vendor) {
|
||||
case FAN53555_VENDOR_FAIRCHILD:
|
||||
ret = fan53555_voltages_setup_fairchild(di);
|
||||
break;
|
||||
case FAN53555_VENDOR_SILERGY:
|
||||
ret = fan53555_voltages_setup_silergy(di);
|
||||
break;
|
||||
default:
|
||||
dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fan53555_regulator_register(struct fan53555_device_info *di,
|
||||
|
@ -207,6 +280,7 @@ static int fan53555_regulator_register(struct fan53555_device_info *di,
|
|||
struct regulator_desc *rdesc = &di->desc;
|
||||
|
||||
rdesc->name = "fan53555-reg";
|
||||
rdesc->supply_name = "vin";
|
||||
rdesc->ops = &fan53555_regulator_ops;
|
||||
rdesc->type = REGULATOR_VOLTAGE;
|
||||
rdesc->n_voltages = FAN53555_NVOLTAGES;
|
||||
|
@ -227,9 +301,46 @@ static struct regmap_config fan53555_regmap_config = {
|
|||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct fan53555_platform_data *pdata;
|
||||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
pdata->regulator = of_get_regulator_init_data(dev, np);
|
||||
|
||||
ret = of_property_read_u32(np, "fcs,suspend-voltage-selector",
|
||||
&tmp);
|
||||
if (!ret)
|
||||
pdata->sleep_vsel_id = tmp;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static const struct of_device_id fan53555_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fcs,fan53555",
|
||||
.data = (void *)FAN53555_VENDOR_FAIRCHILD
|
||||
}, {
|
||||
.compatible = "silergy,syr827",
|
||||
.data = (void *)FAN53555_VENDOR_SILERGY,
|
||||
}, {
|
||||
.compatible = "silergy,syr828",
|
||||
.data = (void *)FAN53555_VENDOR_SILERGY,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fan53555_dt_ids);
|
||||
|
||||
static int fan53555_regulator_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct fan53555_device_info *di;
|
||||
struct fan53555_platform_data *pdata;
|
||||
struct regulator_config config = { };
|
||||
|
@ -237,6 +348,9 @@ static int fan53555_regulator_probe(struct i2c_client *client,
|
|||
int ret;
|
||||
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (!pdata)
|
||||
pdata = fan53555_parse_dt(&client->dev, np);
|
||||
|
||||
if (!pdata || !pdata->regulator) {
|
||||
dev_err(&client->dev, "Platform data not found!\n");
|
||||
return -ENODEV;
|
||||
|
@ -247,13 +361,35 @@ static int fan53555_regulator_probe(struct i2c_client *client,
|
|||
if (!di)
|
||||
return -ENOMEM;
|
||||
|
||||
di->regulator = pdata->regulator;
|
||||
if (client->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(fan53555_dt_ids),
|
||||
&client->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
di->vendor = (unsigned long) match->data;
|
||||
} else {
|
||||
/* if no ramp constraint set, get the pdata ramp_delay */
|
||||
if (!di->regulator->constraints.ramp_delay) {
|
||||
int slew_idx = (pdata->slew_rate & 0x7)
|
||||
? pdata->slew_rate : 0;
|
||||
|
||||
di->regulator->constraints.ramp_delay
|
||||
= slew_rates[slew_idx];
|
||||
}
|
||||
|
||||
di->vendor = id->driver_data;
|
||||
}
|
||||
|
||||
di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
|
||||
if (IS_ERR(di->regmap)) {
|
||||
dev_err(&client->dev, "Failed to allocate regmap!\n");
|
||||
return PTR_ERR(di->regmap);
|
||||
}
|
||||
di->dev = &client->dev;
|
||||
di->regulator = pdata->regulator;
|
||||
i2c_set_clientdata(client, di);
|
||||
/* Get chip ID */
|
||||
ret = regmap_read(di->regmap, FAN53555_ID1, &val);
|
||||
|
@ -282,6 +418,8 @@ static int fan53555_regulator_probe(struct i2c_client *client,
|
|||
config.init_data = di->regulator;
|
||||
config.regmap = di->regmap;
|
||||
config.driver_data = di;
|
||||
config.of_node = np;
|
||||
|
||||
ret = fan53555_regulator_register(di, &config);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Failed to register regulator!\n");
|
||||
|
@ -290,13 +428,20 @@ static int fan53555_regulator_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
static const struct i2c_device_id fan53555_id[] = {
|
||||
{"fan53555", -1},
|
||||
{
|
||||
.name = "fan53555",
|
||||
.driver_data = FAN53555_VENDOR_FAIRCHILD
|
||||
}, {
|
||||
.name = "syr82x",
|
||||
.driver_data = FAN53555_VENDOR_SILERGY
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct i2c_driver fan53555_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "fan53555-regulator",
|
||||
.of_match_table = of_match_ptr(fan53555_dt_ids),
|
||||
},
|
||||
.probe = fan53555_regulator_probe,
|
||||
.id_table = fan53555_id,
|
||||
|
|
|
@ -0,0 +1,634 @@
|
|||
/*
|
||||
* Device driver for regulators in Hi6421 IC
|
||||
*
|
||||
* Copyright (c) <2011-2014> HiSilicon Technologies Co., Ltd.
|
||||
* http://www.hisilicon.com
|
||||
* Copyright (c) <2013-2014> Linaro Ltd.
|
||||
* http://www.linaro.org
|
||||
*
|
||||
* Author: Guodong Xu <guodong.xu@linaro.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/mfd/hi6421-pmic.h>
|
||||
|
||||
/*
|
||||
* struct hi6421_regulator_pdata - Hi6421 regulator data of platform device
|
||||
* @lock: mutex to serialize regulator enable
|
||||
*/
|
||||
struct hi6421_regulator_pdata {
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct hi6421_regulator_info - hi6421 regulator information
|
||||
* @desc: regulator description
|
||||
* @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep
|
||||
* @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only
|
||||
*/
|
||||
struct hi6421_regulator_info {
|
||||
struct regulator_desc desc;
|
||||
u8 mode_mask;
|
||||
u32 eco_microamp;
|
||||
};
|
||||
|
||||
/* HI6421 regulators */
|
||||
enum hi6421_regulator_id {
|
||||
HI6421_LDO0,
|
||||
HI6421_LDO1,
|
||||
HI6421_LDO2,
|
||||
HI6421_LDO3,
|
||||
HI6421_LDO4,
|
||||
HI6421_LDO5,
|
||||
HI6421_LDO6,
|
||||
HI6421_LDO7,
|
||||
HI6421_LDO8,
|
||||
HI6421_LDO9,
|
||||
HI6421_LDO10,
|
||||
HI6421_LDO11,
|
||||
HI6421_LDO12,
|
||||
HI6421_LDO13,
|
||||
HI6421_LDO14,
|
||||
HI6421_LDO15,
|
||||
HI6421_LDO16,
|
||||
HI6421_LDO17,
|
||||
HI6421_LDO18,
|
||||
HI6421_LDO19,
|
||||
HI6421_LDO20,
|
||||
HI6421_LDOAUDIO,
|
||||
HI6421_BUCK0,
|
||||
HI6421_BUCK1,
|
||||
HI6421_BUCK2,
|
||||
HI6421_BUCK3,
|
||||
HI6421_BUCK4,
|
||||
HI6421_BUCK5,
|
||||
HI6421_NUM_REGULATORS,
|
||||
};
|
||||
|
||||
#define HI6421_REGULATOR_OF_MATCH(_name, id) \
|
||||
{ \
|
||||
.name = #_name, \
|
||||
.driver_data = (void *) HI6421_##id, \
|
||||
}
|
||||
|
||||
static struct of_regulator_match hi6421_regulator_match[] = {
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout0, LDO0),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout1, LDO1),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout2, LDO2),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout3, LDO3),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout4, LDO4),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout5, LDO5),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout6, LDO6),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout7, LDO7),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout8, LDO8),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout9, LDO9),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout10, LDO10),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout11, LDO11),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout12, LDO12),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout13, LDO13),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout14, LDO14),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout15, LDO15),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout16, LDO16),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout17, LDO17),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout18, LDO18),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout19, LDO19),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout20, LDO20),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_vout_audio, LDOAUDIO),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_buck0, BUCK0),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_buck1, BUCK1),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_buck2, BUCK2),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_buck3, BUCK3),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_buck4, BUCK4),
|
||||
HI6421_REGULATOR_OF_MATCH(hi6421_buck5, BUCK5),
|
||||
};
|
||||
|
||||
/* LDO 0, 4~7, 9~14, 16~20 have same voltage table. */
|
||||
static const unsigned int ldo_0_voltages[] = {
|
||||
1500000, 1800000, 2400000, 2500000,
|
||||
2600000, 2700000, 2850000, 3000000,
|
||||
};
|
||||
|
||||
/* LDO 8, 15 have same voltage table. */
|
||||
static const unsigned int ldo_8_voltages[] = {
|
||||
1500000, 1800000, 2400000, 2600000,
|
||||
2700000, 2850000, 3000000, 3300000,
|
||||
};
|
||||
|
||||
/* Ranges are sorted in ascending order. */
|
||||
static const struct regulator_linear_range ldo_audio_volt_range[] = {
|
||||
REGULATOR_LINEAR_RANGE(2800000, 0, 3, 50000),
|
||||
REGULATOR_LINEAR_RANGE(3000000, 4, 7, 100000),
|
||||
};
|
||||
|
||||
static const unsigned int buck_3_voltages[] = {
|
||||
950000, 1050000, 1100000, 1117000,
|
||||
1134000, 1150000, 1167000, 1200000,
|
||||
};
|
||||
|
||||
static const unsigned int buck_4_voltages[] = {
|
||||
1150000, 1200000, 1250000, 1350000,
|
||||
1700000, 1800000, 1900000, 2000000,
|
||||
};
|
||||
|
||||
static const unsigned int buck_5_voltages[] = {
|
||||
1150000, 1200000, 1250000, 1350000,
|
||||
1600000, 1700000, 1800000, 1900000,
|
||||
};
|
||||
|
||||
static const struct regulator_ops hi6421_ldo_ops;
|
||||
static const struct regulator_ops hi6421_ldo_linear_ops;
|
||||
static const struct regulator_ops hi6421_ldo_linear_range_ops;
|
||||
static const struct regulator_ops hi6421_buck012_ops;
|
||||
static const struct regulator_ops hi6421_buck345_ops;
|
||||
|
||||
#define HI6421_LDO_ENABLE_TIME (350)
|
||||
/*
|
||||
* _id - LDO id name string
|
||||
* v_table - voltage table
|
||||
* vreg - voltage select register
|
||||
* vmask - voltage select mask
|
||||
* ereg - enable register
|
||||
* emask - enable mask
|
||||
* odelay - off/on delay time in uS
|
||||
* ecomask - eco mode mask
|
||||
* ecoamp - eco mode load uppler limit in uA
|
||||
*/
|
||||
#define HI6421_LDO(_id, v_table, vreg, vmask, ereg, emask, \
|
||||
odelay, ecomask, ecoamp) \
|
||||
[HI6421_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.ops = &hi6421_ldo_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.n_voltages = ARRAY_SIZE(v_table), \
|
||||
.volt_table = v_table, \
|
||||
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
||||
.vsel_mask = vmask, \
|
||||
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
||||
.enable_mask = emask, \
|
||||
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
||||
.off_on_delay = odelay, \
|
||||
}, \
|
||||
.mode_mask = ecomask, \
|
||||
.eco_microamp = ecoamp, \
|
||||
}
|
||||
|
||||
/* HI6421 LDO1~3 are linear voltage regulators at fixed uV_step
|
||||
*
|
||||
* _id - LDO id name string
|
||||
* _min_uV - minimum voltage supported in uV
|
||||
* n_volt - number of votages available
|
||||
* vstep - voltage increase in each linear step in uV
|
||||
* vreg - voltage select register
|
||||
* vmask - voltage select mask
|
||||
* ereg - enable register
|
||||
* emask - enable mask
|
||||
* odelay - off/on delay time in uS
|
||||
* ecomask - eco mode mask
|
||||
* ecoamp - eco mode load uppler limit in uA
|
||||
*/
|
||||
#define HI6421_LDO_LINEAR(_id, _min_uV, n_volt, vstep, vreg, vmask, \
|
||||
ereg, emask, odelay, ecomask, ecoamp) \
|
||||
[HI6421_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.ops = &hi6421_ldo_linear_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = _min_uV, \
|
||||
.n_voltages = n_volt, \
|
||||
.uV_step = vstep, \
|
||||
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
||||
.vsel_mask = vmask, \
|
||||
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
||||
.enable_mask = emask, \
|
||||
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
||||
.off_on_delay = odelay, \
|
||||
}, \
|
||||
.mode_mask = ecomask, \
|
||||
.eco_microamp = ecoamp, \
|
||||
}
|
||||
|
||||
/* HI6421 LDOAUDIO is a linear voltage regulator with two 4-step ranges
|
||||
*
|
||||
* _id - LDO id name string
|
||||
* n_volt - number of votages available
|
||||
* volt_ranges - array of regulator_linear_range
|
||||
* vstep - voltage increase in each linear step in uV
|
||||
* vreg - voltage select register
|
||||
* vmask - voltage select mask
|
||||
* ereg - enable register
|
||||
* emask - enable mask
|
||||
* odelay - off/on delay time in uS
|
||||
* ecomask - eco mode mask
|
||||
* ecoamp - eco mode load uppler limit in uA
|
||||
*/
|
||||
#define HI6421_LDO_LINEAR_RANGE(_id, n_volt, volt_ranges, vreg, vmask, \
|
||||
ereg, emask, odelay, ecomask, ecoamp) \
|
||||
[HI6421_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.ops = &hi6421_ldo_linear_range_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.n_voltages = n_volt, \
|
||||
.linear_ranges = volt_ranges, \
|
||||
.n_linear_ranges = ARRAY_SIZE(volt_ranges), \
|
||||
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
||||
.vsel_mask = vmask, \
|
||||
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
||||
.enable_mask = emask, \
|
||||
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
||||
.off_on_delay = odelay, \
|
||||
}, \
|
||||
.mode_mask = ecomask, \
|
||||
.eco_microamp = ecoamp, \
|
||||
}
|
||||
|
||||
/* HI6421 BUCK0/1/2 are linear voltage regulators at fixed uV_step
|
||||
*
|
||||
* _id - BUCK0/1/2 id name string
|
||||
* vreg - voltage select register
|
||||
* vmask - voltage select mask
|
||||
* ereg - enable register
|
||||
* emask - enable mask
|
||||
* sleepmask - mask of sleep mode
|
||||
* etime - enable time
|
||||
* odelay - off/on delay time in uS
|
||||
*/
|
||||
#define HI6421_BUCK012(_id, vreg, vmask, ereg, emask, sleepmask, \
|
||||
etime, odelay) \
|
||||
[HI6421_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.ops = &hi6421_buck012_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.min_uV = 700000, \
|
||||
.n_voltages = 128, \
|
||||
.uV_step = 7086, \
|
||||
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
||||
.vsel_mask = vmask, \
|
||||
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
||||
.enable_mask = emask, \
|
||||
.enable_time = etime, \
|
||||
.off_on_delay = odelay, \
|
||||
}, \
|
||||
.mode_mask = sleepmask, \
|
||||
}
|
||||
|
||||
/* HI6421 BUCK3/4/5 share similar configurations as LDOs, with exception
|
||||
* that it supports SLEEP mode, so has different .ops.
|
||||
*
|
||||
* _id - LDO id name string
|
||||
* v_table - voltage table
|
||||
* vreg - voltage select register
|
||||
* vmask - voltage select mask
|
||||
* ereg - enable register
|
||||
* emask - enable mask
|
||||
* odelay - off/on delay time in uS
|
||||
* sleepmask - mask of sleep mode
|
||||
*/
|
||||
#define HI6421_BUCK345(_id, v_table, vreg, vmask, ereg, emask, \
|
||||
odelay, sleepmask) \
|
||||
[HI6421_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.ops = &hi6421_buck345_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.n_voltages = ARRAY_SIZE(v_table), \
|
||||
.volt_table = v_table, \
|
||||
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
||||
.vsel_mask = vmask, \
|
||||
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
||||
.enable_mask = emask, \
|
||||
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
||||
.off_on_delay = odelay, \
|
||||
}, \
|
||||
.mode_mask = sleepmask, \
|
||||
}
|
||||
|
||||
/* HI6421 regulator information */
|
||||
static struct hi6421_regulator_info
|
||||
hi6421_regulator_info[HI6421_NUM_REGULATORS] = {
|
||||
HI6421_LDO(LDO0, ldo_0_voltages, 0x20, 0x07, 0x20, 0x10,
|
||||
10000, 0x20, 8000),
|
||||
HI6421_LDO_LINEAR(LDO1, 1700000, 4, 100000, 0x21, 0x03, 0x21, 0x10,
|
||||
10000, 0x20, 5000),
|
||||
HI6421_LDO_LINEAR(LDO2, 1050000, 8, 50000, 0x22, 0x07, 0x22, 0x10,
|
||||
20000, 0x20, 8000),
|
||||
HI6421_LDO_LINEAR(LDO3, 1050000, 8, 50000, 0x23, 0x07, 0x23, 0x10,
|
||||
20000, 0x20, 8000),
|
||||
HI6421_LDO(LDO4, ldo_0_voltages, 0x24, 0x07, 0x24, 0x10,
|
||||
20000, 0x20, 8000),
|
||||
HI6421_LDO(LDO5, ldo_0_voltages, 0x25, 0x07, 0x25, 0x10,
|
||||
20000, 0x20, 8000),
|
||||
HI6421_LDO(LDO6, ldo_0_voltages, 0x26, 0x07, 0x26, 0x10,
|
||||
20000, 0x20, 8000),
|
||||
HI6421_LDO(LDO7, ldo_0_voltages, 0x27, 0x07, 0x27, 0x10,
|
||||
20000, 0x20, 5000),
|
||||
HI6421_LDO(LDO8, ldo_8_voltages, 0x28, 0x07, 0x28, 0x10,
|
||||
20000, 0x20, 8000),
|
||||
HI6421_LDO(LDO9, ldo_0_voltages, 0x29, 0x07, 0x29, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO10, ldo_0_voltages, 0x2a, 0x07, 0x2a, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO11, ldo_0_voltages, 0x2b, 0x07, 0x2b, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO12, ldo_0_voltages, 0x2c, 0x07, 0x2c, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO13, ldo_0_voltages, 0x2d, 0x07, 0x2d, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO14, ldo_0_voltages, 0x2e, 0x07, 0x2e, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO15, ldo_8_voltages, 0x2f, 0x07, 0x2f, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO16, ldo_0_voltages, 0x30, 0x07, 0x30, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO17, ldo_0_voltages, 0x31, 0x07, 0x31, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO18, ldo_0_voltages, 0x32, 0x07, 0x32, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO19, ldo_0_voltages, 0x33, 0x07, 0x33, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO(LDO20, ldo_0_voltages, 0x34, 0x07, 0x34, 0x10,
|
||||
40000, 0x20, 8000),
|
||||
HI6421_LDO_LINEAR_RANGE(LDOAUDIO, 8, ldo_audio_volt_range, 0x36,
|
||||
0x70, 0x36, 0x01, 40000, 0x02, 5000),
|
||||
HI6421_BUCK012(BUCK0, 0x0d, 0x7f, 0x0c, 0x01, 0x10, 400, 20000),
|
||||
HI6421_BUCK012(BUCK1, 0x0f, 0x7f, 0x0e, 0x01, 0x10, 400, 20000),
|
||||
HI6421_BUCK012(BUCK2, 0x11, 0x7f, 0x10, 0x01, 0x10, 350, 100),
|
||||
HI6421_BUCK345(BUCK3, buck_3_voltages, 0x13, 0x07, 0x12, 0x01,
|
||||
20000, 0x10),
|
||||
HI6421_BUCK345(BUCK4, buck_4_voltages, 0x15, 0x07, 0x14, 0x01,
|
||||
20000, 0x10),
|
||||
HI6421_BUCK345(BUCK5, buck_5_voltages, 0x17, 0x07, 0x16, 0x01,
|
||||
20000, 0x10),
|
||||
};
|
||||
|
||||
static int hi6421_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct hi6421_regulator_pdata *pdata;
|
||||
|
||||
pdata = dev_get_drvdata(rdev->dev.parent);
|
||||
/* hi6421 spec requires regulator enablement must be serialized:
|
||||
* - Because when BUCK, LDO switching from off to on, it will have
|
||||
* a huge instantaneous current; so you can not turn on two or
|
||||
* more LDO or BUCKs simultaneously, or it may burn the chip.
|
||||
*/
|
||||
mutex_lock(&pdata->lock);
|
||||
|
||||
/* call regulator regmap helper */
|
||||
regulator_enable_regmap(rdev);
|
||||
|
||||
mutex_unlock(&pdata->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int hi6421_regulator_ldo_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
u32 reg_val;
|
||||
|
||||
regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val);
|
||||
if (reg_val & info->mode_mask)
|
||||
return REGULATOR_MODE_IDLE;
|
||||
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static unsigned int hi6421_regulator_buck_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
u32 reg_val;
|
||||
|
||||
regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val);
|
||||
if (reg_val & info->mode_mask)
|
||||
return REGULATOR_MODE_STANDBY;
|
||||
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static int hi6421_regulator_ldo_set_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
u32 new_mode;
|
||||
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_NORMAL:
|
||||
new_mode = 0;
|
||||
break;
|
||||
case REGULATOR_MODE_IDLE:
|
||||
new_mode = info->mode_mask;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set mode */
|
||||
regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
|
||||
info->mode_mask, new_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
u32 new_mode;
|
||||
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_NORMAL:
|
||||
new_mode = 0;
|
||||
break;
|
||||
case REGULATOR_MODE_STANDBY:
|
||||
new_mode = info->mode_mask;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set mode */
|
||||
regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
|
||||
info->mode_mask, new_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
|
||||
int input_uV, int output_uV, int load_uA)
|
||||
{
|
||||
struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
|
||||
if (load_uA > info->eco_microamp)
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
|
||||
return REGULATOR_MODE_IDLE;
|
||||
}
|
||||
|
||||
static const struct regulator_ops hi6421_ldo_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = hi6421_regulator_enable,
|
||||
.disable = regulator_disable_regmap,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_ascend,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_mode = hi6421_regulator_ldo_get_mode,
|
||||
.set_mode = hi6421_regulator_ldo_set_mode,
|
||||
.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
|
||||
};
|
||||
|
||||
static const struct regulator_ops hi6421_ldo_linear_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = hi6421_regulator_enable,
|
||||
.disable = regulator_disable_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.map_voltage = regulator_map_voltage_linear,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_mode = hi6421_regulator_ldo_get_mode,
|
||||
.set_mode = hi6421_regulator_ldo_set_mode,
|
||||
.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
|
||||
};
|
||||
|
||||
static const struct regulator_ops hi6421_ldo_linear_range_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = hi6421_regulator_enable,
|
||||
.disable = regulator_disable_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.map_voltage = regulator_map_voltage_linear_range,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_mode = hi6421_regulator_ldo_get_mode,
|
||||
.set_mode = hi6421_regulator_ldo_set_mode,
|
||||
.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
|
||||
};
|
||||
|
||||
static const struct regulator_ops hi6421_buck012_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = hi6421_regulator_enable,
|
||||
.disable = regulator_disable_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.map_voltage = regulator_map_voltage_linear,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_mode = hi6421_regulator_buck_get_mode,
|
||||
.set_mode = hi6421_regulator_buck_set_mode,
|
||||
};
|
||||
|
||||
static const struct regulator_ops hi6421_buck345_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = hi6421_regulator_enable,
|
||||
.disable = regulator_disable_regmap,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_ascend,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_mode = hi6421_regulator_buck_get_mode,
|
||||
.set_mode = hi6421_regulator_buck_set_mode,
|
||||
};
|
||||
|
||||
static int hi6421_regulator_register(struct platform_device *pdev,
|
||||
struct regmap *rmap,
|
||||
struct regulator_init_data *init_data,
|
||||
int id, struct device_node *np)
|
||||
{
|
||||
struct hi6421_regulator_info *info = NULL;
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *rdev;
|
||||
|
||||
/* assign per-regulator data */
|
||||
info = &hi6421_regulator_info[id];
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.init_data = init_data;
|
||||
config.driver_data = info;
|
||||
config.regmap = rmap;
|
||||
config.of_node = np;
|
||||
|
||||
/* register regulator with framework */
|
||||
rdev = devm_regulator_register(&pdev->dev, &info->desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(&pdev->dev, "failed to register regulator %s\n",
|
||||
info->desc.name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi6421_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np;
|
||||
struct hi6421_pmic *pmic;
|
||||
struct hi6421_regulator_pdata *pdata;
|
||||
int i, ret = 0;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
mutex_init(&pdata->lock);
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
np = of_get_child_by_name(dev->parent->of_node, "regulators");
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_regulator_match(dev, np,
|
||||
hi6421_regulator_match,
|
||||
ARRAY_SIZE(hi6421_regulator_match));
|
||||
of_node_put(np);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error parsing regulator init data: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmic = dev_get_drvdata(dev->parent);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hi6421_regulator_info); i++) {
|
||||
ret = hi6421_regulator_register(pdev, pmic->regmap,
|
||||
hi6421_regulator_match[i].init_data, i,
|
||||
hi6421_regulator_match[i].of_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hi6421_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "hi6421-regulator",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = hi6421_regulator_probe,
|
||||
};
|
||||
module_platform_driver(hi6421_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
|
||||
MODULE_DESCRIPTION("Hi6421 regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -35,4 +35,8 @@ struct regulator {
|
|||
struct dentry *debugfs;
|
||||
};
|
||||
|
||||
struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
|
||||
const struct regulator_desc *desc,
|
||||
struct device_node **node);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* isl9305 - Intersil ISL9305 DCDC regulator
|
||||
*
|
||||
* Copyright 2014 Linaro Ltd
|
||||
*
|
||||
* Author: Mark Brown <broonie@kernel.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/isl9305.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Registers
|
||||
*/
|
||||
#define ISL9305_DCD1OUT 0x0
|
||||
#define ISL9305_DCD2OUT 0x1
|
||||
#define ISL9305_LDO1OUT 0x2
|
||||
#define ISL9305_LDO2OUT 0x3
|
||||
#define ISL9305_DCD_PARAMETER 0x4
|
||||
#define ISL9305_SYSTEM_PARAMETER 0x5
|
||||
#define ISL9305_DCD_SRCTL 0x6
|
||||
|
||||
#define ISL9305_MAX_REG ISL9305_DCD_SRCTL
|
||||
|
||||
/*
|
||||
* DCD_PARAMETER
|
||||
*/
|
||||
#define ISL9305_DCD_PHASE 0x40
|
||||
#define ISL9305_DCD2_ULTRA 0x20
|
||||
#define ISL9305_DCD1_ULTRA 0x10
|
||||
#define ISL9305_DCD2_BLD 0x08
|
||||
#define ISL9305_DCD1_BLD 0x04
|
||||
#define ISL9305_DCD2_MODE 0x02
|
||||
#define ISL9305_DCD1_MODE 0x01
|
||||
|
||||
/*
|
||||
* SYSTEM_PARAMETER
|
||||
*/
|
||||
#define ISL9305_I2C_EN 0x40
|
||||
#define ISL9305_DCDPOR_MASK 0x30
|
||||
#define ISL9305_LDO2_EN 0x08
|
||||
#define ISL9305_LDO1_EN 0x04
|
||||
#define ISL9305_DCD2_EN 0x02
|
||||
#define ISL9305_DCD1_EN 0x01
|
||||
|
||||
/*
|
||||
* DCD_SRCTL
|
||||
*/
|
||||
#define ISL9305_DCD2SR_MASK 0xc0
|
||||
#define ISL9305_DCD1SR_MASK 0x07
|
||||
|
||||
static const struct regulator_ops isl9305_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_desc isl9305_regulators[] = {
|
||||
[ISL9305_DCD1] = {
|
||||
.name = "DCD1",
|
||||
.of_match = of_match_ptr("dcd1"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.n_voltages = 0x70,
|
||||
.min_uV = 825000,
|
||||
.uV_step = 25000,
|
||||
.vsel_reg = ISL9305_DCD1OUT,
|
||||
.vsel_mask = 0x7f,
|
||||
.enable_reg = ISL9305_SYSTEM_PARAMETER,
|
||||
.enable_mask = ISL9305_DCD1_EN,
|
||||
.supply_name = "VINDCD1",
|
||||
.ops = &isl9305_ops,
|
||||
},
|
||||
[ISL9305_DCD2] = {
|
||||
.name = "DCD2",
|
||||
.of_match = of_match_ptr("dcd2"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.n_voltages = 0x70,
|
||||
.min_uV = 825000,
|
||||
.uV_step = 25000,
|
||||
.vsel_reg = ISL9305_DCD2OUT,
|
||||
.vsel_mask = 0x7f,
|
||||
.enable_reg = ISL9305_SYSTEM_PARAMETER,
|
||||
.enable_mask = ISL9305_DCD2_EN,
|
||||
.supply_name = "VINDCD2",
|
||||
.ops = &isl9305_ops,
|
||||
},
|
||||
[ISL9305_LDO1] = {
|
||||
.name = "LDO1",
|
||||
.of_match = of_match_ptr("ldo1"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.n_voltages = 0x37,
|
||||
.min_uV = 900000,
|
||||
.uV_step = 50000,
|
||||
.vsel_reg = ISL9305_LDO1OUT,
|
||||
.vsel_mask = 0x3f,
|
||||
.enable_reg = ISL9305_SYSTEM_PARAMETER,
|
||||
.enable_mask = ISL9305_LDO1_EN,
|
||||
.supply_name = "VINLDO1",
|
||||
.ops = &isl9305_ops,
|
||||
},
|
||||
[ISL9305_LDO2] = {
|
||||
.name = "LDO2",
|
||||
.of_match = of_match_ptr("ldo2"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.n_voltages = 0x37,
|
||||
.min_uV = 900000,
|
||||
.uV_step = 50000,
|
||||
.vsel_reg = ISL9305_LDO2OUT,
|
||||
.vsel_mask = 0x3f,
|
||||
.enable_reg = ISL9305_SYSTEM_PARAMETER,
|
||||
.enable_mask = ISL9305_LDO2_EN,
|
||||
.supply_name = "VINLDO2",
|
||||
.ops = &isl9305_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config isl9305_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = ISL9305_MAX_REG,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int isl9305_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regulator_config config = { };
|
||||
struct isl9305_pdata *pdata = i2c->dev.platform_data;
|
||||
struct regulator_dev *rdev;
|
||||
struct regmap *regmap;
|
||||
int i, ret;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &isl9305_regmap);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
config.dev = &i2c->dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(isl9305_regulators); i++) {
|
||||
if (pdata)
|
||||
config.init_data = pdata->init_data[i];
|
||||
else
|
||||
config.init_data = NULL;
|
||||
|
||||
rdev = devm_regulator_register(&i2c->dev,
|
||||
&isl9305_regulators[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
ret = PTR_ERR(rdev);
|
||||
dev_err(&i2c->dev, "Failed to register %s: %d\n",
|
||||
isl9305_regulators[i].name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id isl9305_dt_ids[] = {
|
||||
{ .compatible = "isl,isl9305" },
|
||||
{ .compatible = "isl,isl9305h" },
|
||||
{},
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id isl9305_i2c_id[] = {
|
||||
{ "isl9305", },
|
||||
{ "isl9305h", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isl9305_i2c_id);
|
||||
|
||||
static struct i2c_driver isl9305_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "isl9305",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(isl9305_dt_ids),
|
||||
},
|
||||
.probe = isl9305_i2c_probe,
|
||||
.id_table = isl9305_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(isl9305_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("Mark Brown");
|
||||
MODULE_DESCRIPTION("Intersil ISL9305 DCDC regulator");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -526,6 +526,7 @@ static unsigned int mc13892_vcam_get_mode(struct regulator_dev *rdev)
|
|||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static struct regulator_ops mc13892_vcam_ops;
|
||||
|
||||
static int mc13892_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -582,10 +583,12 @@ static int mc13892_regulator_probe(struct platform_device *pdev)
|
|||
}
|
||||
mc13xxx_unlock(mc13892);
|
||||
|
||||
mc13892_regulators[MC13892_VCAM].desc.ops->set_mode
|
||||
= mc13892_vcam_set_mode;
|
||||
mc13892_regulators[MC13892_VCAM].desc.ops->get_mode
|
||||
= mc13892_vcam_get_mode;
|
||||
/* update mc13892_vcam ops */
|
||||
memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops,
|
||||
sizeof(struct regulator_ops));
|
||||
mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode,
|
||||
mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode,
|
||||
mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops;
|
||||
|
||||
mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators,
|
||||
ARRAY_SIZE(mc13892_regulators));
|
||||
|
|
|
@ -14,8 +14,11 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static void of_get_regulation_constraints(struct device_node *np,
|
||||
struct regulator_init_data **init_data)
|
||||
{
|
||||
|
@ -189,3 +192,51 @@ int of_regulator_match(struct device *dev, struct device_node *node,
|
|||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_regulator_match);
|
||||
|
||||
struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
|
||||
const struct regulator_desc *desc,
|
||||
struct device_node **node)
|
||||
{
|
||||
struct device_node *search, *child;
|
||||
struct regulator_init_data *init_data = NULL;
|
||||
const char *name;
|
||||
|
||||
if (!dev->of_node || !desc->of_match)
|
||||
return NULL;
|
||||
|
||||
if (desc->regulators_node)
|
||||
search = of_get_child_by_name(dev->of_node,
|
||||
desc->regulators_node);
|
||||
else
|
||||
search = dev->of_node;
|
||||
|
||||
if (!search) {
|
||||
dev_err(dev, "Failed to find regulator container node\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for_each_child_of_node(search, child) {
|
||||
name = of_get_property(child, "regulator-compatible", NULL);
|
||||
if (!name)
|
||||
name = child->name;
|
||||
|
||||
if (strcmp(desc->of_match, name))
|
||||
continue;
|
||||
|
||||
init_data = of_get_regulator_init_data(dev, child);
|
||||
if (!init_data) {
|
||||
dev_err(dev,
|
||||
"failed to parse DT for regulator %s\n",
|
||||
child->name);
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_get(child);
|
||||
*node = child;
|
||||
break;
|
||||
}
|
||||
|
||||
of_node_put(search);
|
||||
|
||||
return init_data;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Regulator driver for PWM Regulators
|
||||
*
|
||||
* Copyright (C) 2014 - STMicroelectronics Inc.
|
||||
*
|
||||
* Author: Lee Jones <lee.jones@linaro.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
struct pwm_regulator_data {
|
||||
struct regulator_desc desc;
|
||||
struct pwm_voltages *duty_cycle_table;
|
||||
struct pwm_device *pwm;
|
||||
bool enabled;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct pwm_voltages {
|
||||
unsigned int uV;
|
||||
unsigned int dutycycle;
|
||||
};
|
||||
|
||||
static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
|
||||
{
|
||||
struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
|
||||
|
||||
return drvdata->state;
|
||||
}
|
||||
|
||||
static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
|
||||
unsigned int pwm_reg_period;
|
||||
int dutycycle;
|
||||
int ret;
|
||||
|
||||
pwm_reg_period = pwm_get_period(drvdata->pwm);
|
||||
|
||||
dutycycle = (pwm_reg_period *
|
||||
drvdata->duty_cycle_table[selector].dutycycle) / 100;
|
||||
|
||||
ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "Failed to configure PWM\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->state = selector;
|
||||
|
||||
if (!drvdata->enabled) {
|
||||
ret = pwm_enable(drvdata->pwm);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "Failed to enable PWM\n");
|
||||
return ret;
|
||||
}
|
||||
drvdata->enabled = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pwm_regulator_list_voltage(struct regulator_dev *dev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
|
||||
|
||||
if (selector >= drvdata->desc.n_voltages)
|
||||
return -EINVAL;
|
||||
|
||||
return drvdata->duty_cycle_table[selector].uV;
|
||||
}
|
||||
|
||||
static struct regulator_ops pwm_regulator_voltage_ops = {
|
||||
.set_voltage_sel = pwm_regulator_set_voltage_sel,
|
||||
.get_voltage_sel = pwm_regulator_get_voltage_sel,
|
||||
.list_voltage = pwm_regulator_list_voltage,
|
||||
.map_voltage = regulator_map_voltage_iterate,
|
||||
};
|
||||
|
||||
static const struct regulator_desc pwm_regulator_desc = {
|
||||
.name = "pwm-regulator",
|
||||
.ops = &pwm_regulator_voltage_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.supply_name = "pwm",
|
||||
};
|
||||
|
||||
static int pwm_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pwm_regulator_data *drvdata;
|
||||
struct property *prop;
|
||||
struct regulator_dev *regulator;
|
||||
struct regulator_config config = { };
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int length, ret;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "Device Tree node missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc));
|
||||
|
||||
/* determine the number of voltage-table */
|
||||
prop = of_find_property(np, "voltage-table", &length);
|
||||
if (!prop) {
|
||||
dev_err(&pdev->dev, "No voltage-table\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((length < sizeof(*drvdata->duty_cycle_table)) ||
|
||||
(length % sizeof(*drvdata->duty_cycle_table))) {
|
||||
dev_err(&pdev->dev, "voltage-table length(%d) is invalid\n",
|
||||
length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
|
||||
|
||||
drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev,
|
||||
length, GFP_KERNEL);
|
||||
if (!drvdata->duty_cycle_table)
|
||||
return -ENOMEM;
|
||||
|
||||
/* read voltage table from DT property */
|
||||
ret = of_property_read_u32_array(np, "voltage-table",
|
||||
(u32 *)drvdata->duty_cycle_table,
|
||||
length / sizeof(u32));
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "read voltage-table failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
config.init_data = of_get_regulator_init_data(&pdev->dev, np);
|
||||
if (!config.init_data)
|
||||
return -ENOMEM;
|
||||
|
||||
config.of_node = np;
|
||||
config.dev = &pdev->dev;
|
||||
config.driver_data = drvdata;
|
||||
|
||||
drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(drvdata->pwm)) {
|
||||
dev_err(&pdev->dev, "Failed to get PWM\n");
|
||||
return PTR_ERR(drvdata->pwm);
|
||||
}
|
||||
|
||||
regulator = devm_regulator_register(&pdev->dev,
|
||||
&drvdata->desc, &config);
|
||||
if (IS_ERR(regulator)) {
|
||||
dev_err(&pdev->dev, "Failed to register regulator %s\n",
|
||||
drvdata->desc.name);
|
||||
return PTR_ERR(regulator);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pwm_of_match[] = {
|
||||
{ .compatible = "pwm-regulator" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pwm_of_match);
|
||||
|
||||
static struct platform_driver pwm_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "pwm-regulator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(pwm_of_match),
|
||||
},
|
||||
.probe = pwm_regulator_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(pwm_regulator_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
|
||||
MODULE_DESCRIPTION("PWM Regulator Driver");
|
||||
MODULE_ALIAS("platform:pwm-regulator");
|
|
@ -0,0 +1,798 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Sony Mobile Communications AB.
|
||||
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/mfd/qcom_rpm.h>
|
||||
|
||||
#include <dt-bindings/mfd/qcom-rpm.h>
|
||||
|
||||
#define MAX_REQUEST_LEN 2
|
||||
|
||||
struct request_member {
|
||||
int word;
|
||||
unsigned int mask;
|
||||
int shift;
|
||||
};
|
||||
|
||||
struct rpm_reg_parts {
|
||||
struct request_member mV; /* used if voltage is in mV */
|
||||
struct request_member uV; /* used if voltage is in uV */
|
||||
struct request_member ip; /* peak current in mA */
|
||||
struct request_member pd; /* pull down enable */
|
||||
struct request_member ia; /* average current in mA */
|
||||
struct request_member fm; /* force mode */
|
||||
struct request_member pm; /* power mode */
|
||||
struct request_member pc; /* pin control */
|
||||
struct request_member pf; /* pin function */
|
||||
struct request_member enable_state; /* NCP and switch */
|
||||
struct request_member comp_mode; /* NCP */
|
||||
struct request_member freq; /* frequency: NCP and SMPS */
|
||||
struct request_member freq_clk_src; /* clock source: SMPS */
|
||||
struct request_member hpm; /* switch: control OCP and SS */
|
||||
int request_len;
|
||||
};
|
||||
|
||||
#define FORCE_MODE_IS_2_BITS(reg) \
|
||||
(((reg)->parts->fm.mask >> (reg)->parts->fm.shift) == 3)
|
||||
|
||||
struct qcom_rpm_reg {
|
||||
struct qcom_rpm *rpm;
|
||||
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
struct regulator_desc desc;
|
||||
const struct rpm_reg_parts *parts;
|
||||
|
||||
int resource;
|
||||
u32 val[MAX_REQUEST_LEN];
|
||||
|
||||
int uV;
|
||||
int is_enabled;
|
||||
|
||||
bool supports_force_mode_auto;
|
||||
bool supports_force_mode_bypass;
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8660_ldo_parts = {
|
||||
.request_len = 2,
|
||||
.mV = { 0, 0x00000FFF, 0 },
|
||||
.ip = { 0, 0x00FFF000, 12 },
|
||||
.fm = { 0, 0x03000000, 24 },
|
||||
.pc = { 0, 0x3C000000, 26 },
|
||||
.pf = { 0, 0xC0000000, 30 },
|
||||
.pd = { 1, 0x00000001, 0 },
|
||||
.ia = { 1, 0x00001FFE, 1 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8660_smps_parts = {
|
||||
.request_len = 2,
|
||||
.mV = { 0, 0x00000FFF, 0 },
|
||||
.ip = { 0, 0x00FFF000, 12 },
|
||||
.fm = { 0, 0x03000000, 24 },
|
||||
.pc = { 0, 0x3C000000, 26 },
|
||||
.pf = { 0, 0xC0000000, 30 },
|
||||
.pd = { 1, 0x00000001, 0 },
|
||||
.ia = { 1, 0x00001FFE, 1 },
|
||||
.freq = { 1, 0x001FE000, 13 },
|
||||
.freq_clk_src = { 1, 0x00600000, 21 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8660_switch_parts = {
|
||||
.request_len = 1,
|
||||
.enable_state = { 0, 0x00000001, 0 },
|
||||
.pd = { 0, 0x00000002, 1 },
|
||||
.pc = { 0, 0x0000003C, 2 },
|
||||
.pf = { 0, 0x000000C0, 6 },
|
||||
.hpm = { 0, 0x00000300, 8 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8660_ncp_parts = {
|
||||
.request_len = 1,
|
||||
.mV = { 0, 0x00000FFF, 0 },
|
||||
.enable_state = { 0, 0x00001000, 12 },
|
||||
.comp_mode = { 0, 0x00002000, 13 },
|
||||
.freq = { 0, 0x003FC000, 14 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8960_ldo_parts = {
|
||||
.request_len = 2,
|
||||
.uV = { 0, 0x007FFFFF, 0 },
|
||||
.pd = { 0, 0x00800000, 23 },
|
||||
.pc = { 0, 0x0F000000, 24 },
|
||||
.pf = { 0, 0xF0000000, 28 },
|
||||
.ip = { 1, 0x000003FF, 0 },
|
||||
.ia = { 1, 0x000FFC00, 10 },
|
||||
.fm = { 1, 0x00700000, 20 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8960_smps_parts = {
|
||||
.request_len = 2,
|
||||
.uV = { 0, 0x007FFFFF, 0 },
|
||||
.pd = { 0, 0x00800000, 23 },
|
||||
.pc = { 0, 0x0F000000, 24 },
|
||||
.pf = { 0, 0xF0000000, 28 },
|
||||
.ip = { 1, 0x000003FF, 0 },
|
||||
.ia = { 1, 0x000FFC00, 10 },
|
||||
.fm = { 1, 0x00700000, 20 },
|
||||
.pm = { 1, 0x00800000, 23 },
|
||||
.freq = { 1, 0x1F000000, 24 },
|
||||
.freq_clk_src = { 1, 0x60000000, 29 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8960_switch_parts = {
|
||||
.request_len = 1,
|
||||
.enable_state = { 0, 0x00000001, 0 },
|
||||
.pd = { 0, 0x00000002, 1 },
|
||||
.pc = { 0, 0x0000003C, 2 },
|
||||
.pf = { 0, 0x000003C0, 6 },
|
||||
.hpm = { 0, 0x00000C00, 10 },
|
||||
};
|
||||
|
||||
static const struct rpm_reg_parts rpm8960_ncp_parts = {
|
||||
.request_len = 1,
|
||||
.uV = { 0, 0x007FFFFF, 0 },
|
||||
.enable_state = { 0, 0x00800000, 23 },
|
||||
.comp_mode = { 0, 0x01000000, 24 },
|
||||
.freq = { 0, 0x3E000000, 25 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Physically available PMIC regulator voltage ranges
|
||||
*/
|
||||
static const struct regulator_linear_range pldo_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE( 750000, 0, 59, 12500),
|
||||
REGULATOR_LINEAR_RANGE(1500000, 60, 123, 25000),
|
||||
REGULATOR_LINEAR_RANGE(3100000, 124, 160, 50000),
|
||||
};
|
||||
|
||||
static const struct regulator_linear_range nldo_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
|
||||
};
|
||||
|
||||
static const struct regulator_linear_range nldo1200_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE( 375000, 0, 59, 6250),
|
||||
REGULATOR_LINEAR_RANGE( 750000, 60, 123, 12500),
|
||||
};
|
||||
|
||||
static const struct regulator_linear_range smps_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
|
||||
REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
|
||||
REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
|
||||
};
|
||||
|
||||
static const struct regulator_linear_range ftsmps_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE( 350000, 0, 6, 50000),
|
||||
REGULATOR_LINEAR_RANGE( 700000, 7, 63, 12500),
|
||||
REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000),
|
||||
};
|
||||
|
||||
static const struct regulator_linear_range ncp_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000),
|
||||
};
|
||||
|
||||
static int rpm_reg_write(struct qcom_rpm_reg *vreg,
|
||||
const struct request_member *req,
|
||||
const int value)
|
||||
{
|
||||
if (WARN_ON((value << req->shift) & ~req->mask))
|
||||
return -EINVAL;
|
||||
|
||||
vreg->val[req->word] &= ~req->mask;
|
||||
vreg->val[req->word] |= value << req->shift;
|
||||
|
||||
return qcom_rpm_write(vreg->rpm,
|
||||
vreg->resource,
|
||||
vreg->val,
|
||||
vreg->parts->request_len);
|
||||
}
|
||||
|
||||
static int rpm_reg_set_mV_sel(struct regulator_dev *rdev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->mV;
|
||||
int ret = 0;
|
||||
int uV;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
uV = regulator_list_voltage_linear_range(rdev, selector);
|
||||
if (uV < 0)
|
||||
return uV;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
vreg->uV = uV;
|
||||
if (vreg->is_enabled)
|
||||
ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_set_uV_sel(struct regulator_dev *rdev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->uV;
|
||||
int ret = 0;
|
||||
int uV;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
uV = regulator_list_voltage_linear_range(rdev, selector);
|
||||
if (uV < 0)
|
||||
return uV;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
vreg->uV = uV;
|
||||
if (vreg->is_enabled)
|
||||
ret = rpm_reg_write(vreg, req, vreg->uV);
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
|
||||
return vreg->uV;
|
||||
}
|
||||
|
||||
static int rpm_reg_mV_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->mV;
|
||||
int ret;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
|
||||
if (!ret)
|
||||
vreg->is_enabled = 1;
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_uV_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->uV;
|
||||
int ret;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
ret = rpm_reg_write(vreg, req, vreg->uV);
|
||||
if (!ret)
|
||||
vreg->is_enabled = 1;
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_switch_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->enable_state;
|
||||
int ret;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
ret = rpm_reg_write(vreg, req, 1);
|
||||
if (!ret)
|
||||
vreg->is_enabled = 1;
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_mV_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->mV;
|
||||
int ret;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
ret = rpm_reg_write(vreg, req, 0);
|
||||
if (!ret)
|
||||
vreg->is_enabled = 0;
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_uV_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->uV;
|
||||
int ret;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
ret = rpm_reg_write(vreg, req, 0);
|
||||
if (!ret)
|
||||
vreg->is_enabled = 0;
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_switch_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
const struct rpm_reg_parts *parts = vreg->parts;
|
||||
const struct request_member *req = &parts->enable_state;
|
||||
int ret;
|
||||
|
||||
if (req->mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&vreg->lock);
|
||||
ret = rpm_reg_write(vreg, req, 0);
|
||||
if (!ret)
|
||||
vreg->is_enabled = 0;
|
||||
mutex_unlock(&vreg->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rpm_reg_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
|
||||
|
||||
return vreg->is_enabled;
|
||||
}
|
||||
|
||||
static struct regulator_ops uV_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
|
||||
.set_voltage_sel = rpm_reg_set_uV_sel,
|
||||
.get_voltage = rpm_reg_get_voltage,
|
||||
|
||||
.enable = rpm_reg_uV_enable,
|
||||
.disable = rpm_reg_uV_disable,
|
||||
.is_enabled = rpm_reg_is_enabled,
|
||||
};
|
||||
|
||||
static struct regulator_ops mV_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
|
||||
.set_voltage_sel = rpm_reg_set_mV_sel,
|
||||
.get_voltage = rpm_reg_get_voltage,
|
||||
|
||||
.enable = rpm_reg_mV_enable,
|
||||
.disable = rpm_reg_mV_disable,
|
||||
.is_enabled = rpm_reg_is_enabled,
|
||||
};
|
||||
|
||||
static struct regulator_ops switch_ops = {
|
||||
.enable = rpm_reg_switch_enable,
|
||||
.disable = rpm_reg_switch_disable,
|
||||
.is_enabled = rpm_reg_is_enabled,
|
||||
};
|
||||
|
||||
/*
|
||||
* PM8058 regulators
|
||||
*/
|
||||
static const struct qcom_rpm_reg pm8058_pldo = {
|
||||
.desc.linear_ranges = pldo_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
|
||||
.desc.n_voltages = 161,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = false,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8058_nldo = {
|
||||
.desc.linear_ranges = nldo_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
|
||||
.desc.n_voltages = 64,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = false,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8058_smps = {
|
||||
.desc.linear_ranges = smps_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
|
||||
.desc.n_voltages = 154,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_smps_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = false,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8058_ncp = {
|
||||
.desc.linear_ranges = ncp_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
|
||||
.desc.n_voltages = 32,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_ncp_parts,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8058_switch = {
|
||||
.desc.ops = &switch_ops,
|
||||
.parts = &rpm8660_switch_parts,
|
||||
};
|
||||
|
||||
/*
|
||||
* PM8901 regulators
|
||||
*/
|
||||
static const struct qcom_rpm_reg pm8901_pldo = {
|
||||
.desc.linear_ranges = pldo_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
|
||||
.desc.n_voltages = 161,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = true,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8901_nldo = {
|
||||
.desc.linear_ranges = nldo_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
|
||||
.desc.n_voltages = 64,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = true,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8901_ftsmps = {
|
||||
.desc.linear_ranges = ftsmps_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
|
||||
.desc.n_voltages = 101,
|
||||
.desc.ops = &mV_ops,
|
||||
.parts = &rpm8660_smps_parts,
|
||||
.supports_force_mode_auto = true,
|
||||
.supports_force_mode_bypass = false,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8901_switch = {
|
||||
.desc.ops = &switch_ops,
|
||||
.parts = &rpm8660_switch_parts,
|
||||
};
|
||||
|
||||
/*
|
||||
* PM8921 regulators
|
||||
*/
|
||||
static const struct qcom_rpm_reg pm8921_pldo = {
|
||||
.desc.linear_ranges = pldo_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
|
||||
.desc.n_voltages = 161,
|
||||
.desc.ops = &uV_ops,
|
||||
.parts = &rpm8960_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = true,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8921_nldo = {
|
||||
.desc.linear_ranges = nldo_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
|
||||
.desc.n_voltages = 64,
|
||||
.desc.ops = &uV_ops,
|
||||
.parts = &rpm8960_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = true,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8921_nldo1200 = {
|
||||
.desc.linear_ranges = nldo1200_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(nldo1200_ranges),
|
||||
.desc.n_voltages = 124,
|
||||
.desc.ops = &uV_ops,
|
||||
.parts = &rpm8960_ldo_parts,
|
||||
.supports_force_mode_auto = false,
|
||||
.supports_force_mode_bypass = true,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8921_smps = {
|
||||
.desc.linear_ranges = smps_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
|
||||
.desc.n_voltages = 154,
|
||||
.desc.ops = &uV_ops,
|
||||
.parts = &rpm8960_smps_parts,
|
||||
.supports_force_mode_auto = true,
|
||||
.supports_force_mode_bypass = false,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8921_ftsmps = {
|
||||
.desc.linear_ranges = ftsmps_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
|
||||
.desc.n_voltages = 101,
|
||||
.desc.ops = &uV_ops,
|
||||
.parts = &rpm8960_smps_parts,
|
||||
.supports_force_mode_auto = true,
|
||||
.supports_force_mode_bypass = false,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8921_ncp = {
|
||||
.desc.linear_ranges = ncp_ranges,
|
||||
.desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
|
||||
.desc.n_voltages = 32,
|
||||
.desc.ops = &uV_ops,
|
||||
.parts = &rpm8960_ncp_parts,
|
||||
};
|
||||
|
||||
static const struct qcom_rpm_reg pm8921_switch = {
|
||||
.desc.ops = &switch_ops,
|
||||
.parts = &rpm8960_switch_parts,
|
||||
};
|
||||
|
||||
static const struct of_device_id rpm_of_match[] = {
|
||||
{ .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo },
|
||||
{ .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo },
|
||||
{ .compatible = "qcom,rpm-pm8058-smps", .data = &pm8058_smps },
|
||||
{ .compatible = "qcom,rpm-pm8058-ncp", .data = &pm8058_ncp },
|
||||
{ .compatible = "qcom,rpm-pm8058-switch", .data = &pm8058_switch },
|
||||
|
||||
{ .compatible = "qcom,rpm-pm8901-pldo", .data = &pm8901_pldo },
|
||||
{ .compatible = "qcom,rpm-pm8901-nldo", .data = &pm8901_nldo },
|
||||
{ .compatible = "qcom,rpm-pm8901-ftsmps", .data = &pm8901_ftsmps },
|
||||
{ .compatible = "qcom,rpm-pm8901-switch", .data = &pm8901_switch },
|
||||
|
||||
{ .compatible = "qcom,rpm-pm8921-pldo", .data = &pm8921_pldo },
|
||||
{ .compatible = "qcom,rpm-pm8921-nldo", .data = &pm8921_nldo },
|
||||
{ .compatible = "qcom,rpm-pm8921-nldo1200", .data = &pm8921_nldo1200 },
|
||||
{ .compatible = "qcom,rpm-pm8921-smps", .data = &pm8921_smps },
|
||||
{ .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps },
|
||||
{ .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp },
|
||||
{ .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpm_of_match);
|
||||
|
||||
static int rpm_reg_set(struct qcom_rpm_reg *vreg,
|
||||
const struct request_member *req,
|
||||
const int value)
|
||||
{
|
||||
if (req->mask == 0 || (value << req->shift) & ~req->mask)
|
||||
return -EINVAL;
|
||||
|
||||
vreg->val[req->word] &= ~req->mask;
|
||||
vreg->val[req->word] |= value << req->shift;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpm_reg_of_parse_freq(struct device *dev, struct qcom_rpm_reg *vreg)
|
||||
{
|
||||
static const int freq_table[] = {
|
||||
19200000, 9600000, 6400000, 4800000, 3840000, 3200000, 2740000,
|
||||
2400000, 2130000, 1920000, 1750000, 1600000, 1480000, 1370000,
|
||||
1280000, 1200000,
|
||||
|
||||
};
|
||||
const char *key;
|
||||
u32 freq;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
key = "qcom,switch-mode-frequency";
|
||||
ret = of_property_read_u32(dev->of_node, key, &freq);
|
||||
if (ret) {
|
||||
dev_err(dev, "regulator requires %s property\n", key);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
|
||||
if (freq == freq_table[i]) {
|
||||
rpm_reg_set(vreg, &vreg->parts->freq, i + 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(dev, "invalid frequency %d\n", freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int rpm_reg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regulator_init_data *initdata;
|
||||
const struct qcom_rpm_reg *template;
|
||||
const struct of_device_id *match;
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *rdev;
|
||||
struct qcom_rpm_reg *vreg;
|
||||
const char *key;
|
||||
u32 force_mode;
|
||||
bool pwm;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
match = of_match_device(rpm_of_match, &pdev->dev);
|
||||
template = match->data;
|
||||
|
||||
initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
|
||||
if (!initdata)
|
||||
return -EINVAL;
|
||||
|
||||
vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
|
||||
if (!vreg) {
|
||||
dev_err(&pdev->dev, "failed to allocate vreg\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(vreg, template, sizeof(*vreg));
|
||||
mutex_init(&vreg->lock);
|
||||
vreg->dev = &pdev->dev;
|
||||
vreg->desc.id = -1;
|
||||
vreg->desc.owner = THIS_MODULE;
|
||||
vreg->desc.type = REGULATOR_VOLTAGE;
|
||||
vreg->desc.name = pdev->dev.of_node->name;
|
||||
|
||||
vreg->rpm = dev_get_drvdata(pdev->dev.parent);
|
||||
if (!vreg->rpm) {
|
||||
dev_err(&pdev->dev, "unable to retrieve handle to rpm\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
key = "reg";
|
||||
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to read %s\n", key);
|
||||
return ret;
|
||||
}
|
||||
vreg->resource = val;
|
||||
|
||||
if ((vreg->parts->uV.mask || vreg->parts->mV.mask) &&
|
||||
(!initdata->constraints.min_uV || !initdata->constraints.max_uV)) {
|
||||
dev_err(&pdev->dev, "no voltage specified for regulator\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = "bias-pull-down";
|
||||
if (of_property_read_bool(pdev->dev.of_node, key)) {
|
||||
ret = rpm_reg_set(vreg, &vreg->parts->pd, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s is invalid", key);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (vreg->parts->freq.mask) {
|
||||
ret = rpm_reg_of_parse_freq(&pdev->dev, vreg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (vreg->parts->pm.mask) {
|
||||
key = "qcom,power-mode-hysteretic";
|
||||
pwm = !of_property_read_bool(pdev->dev.of_node, key);
|
||||
|
||||
ret = rpm_reg_set(vreg, &vreg->parts->pm, pwm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to set power mode\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (vreg->parts->fm.mask) {
|
||||
force_mode = -1;
|
||||
|
||||
key = "qcom,force-mode";
|
||||
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
|
||||
if (ret == -EINVAL) {
|
||||
val = QCOM_RPM_FORCE_MODE_NONE;
|
||||
} else if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to read %s\n", key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If force-mode is encoded as 2 bits then the
|
||||
* possible register values are:
|
||||
* NONE, LPM, HPM
|
||||
* otherwise:
|
||||
* NONE, LPM, AUTO, HPM, BYPASS
|
||||
*/
|
||||
switch (val) {
|
||||
case QCOM_RPM_FORCE_MODE_NONE:
|
||||
force_mode = 0;
|
||||
break;
|
||||
case QCOM_RPM_FORCE_MODE_LPM:
|
||||
force_mode = 1;
|
||||
break;
|
||||
case QCOM_RPM_FORCE_MODE_HPM:
|
||||
if (FORCE_MODE_IS_2_BITS(vreg))
|
||||
force_mode = 2;
|
||||
else
|
||||
force_mode = 3;
|
||||
break;
|
||||
case QCOM_RPM_FORCE_MODE_AUTO:
|
||||
if (vreg->supports_force_mode_auto)
|
||||
force_mode = 2;
|
||||
break;
|
||||
case QCOM_RPM_FORCE_MODE_BYPASS:
|
||||
if (vreg->supports_force_mode_bypass)
|
||||
force_mode = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (force_mode < 0) {
|
||||
dev_err(&pdev->dev, "invalid force mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = rpm_reg_set(vreg, &vreg->parts->fm, force_mode);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to set force mode\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.init_data = initdata;
|
||||
config.driver_data = vreg;
|
||||
config.of_node = pdev->dev.of_node;
|
||||
rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(&pdev->dev, "can't register regulator\n");
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rpm_reg_driver = {
|
||||
.probe = rpm_reg_probe,
|
||||
.driver = {
|
||||
.name = "qcom_rpm_reg",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(rpm_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rpm_reg_init(void)
|
||||
{
|
||||
return platform_driver_register(&rpm_reg_driver);
|
||||
}
|
||||
subsys_initcall(rpm_reg_init);
|
||||
|
||||
static void __exit rpm_reg_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rpm_reg_driver);
|
||||
}
|
||||
module_exit(rpm_reg_exit)
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm RPM regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* sky81452-regulator.c SKY81452 regulator driver
|
||||
*
|
||||
* Copyright 2014 Skyworks Solutions Inc.
|
||||
* Author : Gyungoh Yoo <jack.yoo@skyworksinc.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, 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
|
||||
/* registers */
|
||||
#define SKY81452_REG1 0x01
|
||||
#define SKY81452_REG3 0x03
|
||||
|
||||
/* bit mask */
|
||||
#define SKY81452_LEN 0x40
|
||||
#define SKY81452_LOUT 0x1F
|
||||
|
||||
static struct regulator_ops sky81452_reg_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.map_voltage = regulator_map_voltage_linear_range,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_linear_range sky81452_reg_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE(4500000, 0, 14, 250000),
|
||||
REGULATOR_LINEAR_RANGE(9000000, 15, 31, 1000000),
|
||||
};
|
||||
|
||||
static const struct regulator_desc sky81452_reg = {
|
||||
.name = "LOUT",
|
||||
.ops = &sky81452_reg_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.n_voltages = SKY81452_LOUT + 1,
|
||||
.linear_ranges = sky81452_reg_ranges,
|
||||
.n_linear_ranges = ARRAY_SIZE(sky81452_reg_ranges),
|
||||
.vsel_reg = SKY81452_REG3,
|
||||
.vsel_mask = SKY81452_LOUT,
|
||||
.enable_reg = SKY81452_REG1,
|
||||
.enable_mask = SKY81452_LEN,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev)
|
||||
{
|
||||
struct regulator_init_data *init_data;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_get_child_by_name(dev->parent->of_node, "regulator");
|
||||
if (unlikely(!np)) {
|
||||
dev_err(dev, "regulator node not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
init_data = of_get_regulator_init_data(dev, np);
|
||||
|
||||
of_node_put(np);
|
||||
return init_data;
|
||||
}
|
||||
#else
|
||||
static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sky81452_reg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct regulator_init_data *init_data = dev_get_platdata(dev);
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *rdev;
|
||||
|
||||
if (!init_data) {
|
||||
init_data = sky81452_reg_parse_dt(dev);
|
||||
if (IS_ERR(init_data))
|
||||
return PTR_ERR(init_data);
|
||||
}
|
||||
|
||||
config.dev = dev;
|
||||
config.init_data = init_data;
|
||||
config.of_node = dev->of_node;
|
||||
config.regmap = dev_get_drvdata(dev->parent);
|
||||
|
||||
rdev = devm_regulator_register(dev, &sky81452_reg, &config);
|
||||
if (IS_ERR(rdev))
|
||||
return PTR_ERR(rdev);
|
||||
|
||||
platform_set_drvdata(pdev, rdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sky81452_reg_driver = {
|
||||
.driver = {
|
||||
.name = "sky81452-regulator",
|
||||
},
|
||||
.probe = sky81452_reg_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(sky81452_reg_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Skyworks SKY81452 Regulator driver");
|
||||
MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("1.0");
|
|
@ -1,190 +0,0 @@
|
|||
/*
|
||||
* Regulator driver for ST's PWM Regulators
|
||||
*
|
||||
* Copyright (C) 2014 - STMicroelectronics Inc.
|
||||
*
|
||||
* Author: Lee Jones <lee.jones@linaro.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#define ST_PWM_REG_PERIOD 8448
|
||||
|
||||
struct st_pwm_regulator_pdata {
|
||||
const struct regulator_desc *desc;
|
||||
struct st_pwm_voltages *duty_cycle_table;
|
||||
};
|
||||
|
||||
struct st_pwm_regulator_data {
|
||||
const struct st_pwm_regulator_pdata *pdata;
|
||||
struct pwm_device *pwm;
|
||||
bool enabled;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct st_pwm_voltages {
|
||||
unsigned int uV;
|
||||
unsigned int dutycycle;
|
||||
};
|
||||
|
||||
static int st_pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
|
||||
{
|
||||
struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
|
||||
|
||||
return drvdata->state;
|
||||
}
|
||||
|
||||
static int st_pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
|
||||
int dutycycle;
|
||||
int ret;
|
||||
|
||||
dutycycle = (ST_PWM_REG_PERIOD / 100) *
|
||||
drvdata->pdata->duty_cycle_table[selector].dutycycle;
|
||||
|
||||
ret = pwm_config(drvdata->pwm, dutycycle, ST_PWM_REG_PERIOD);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "Failed to configure PWM\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->state = selector;
|
||||
|
||||
if (!drvdata->enabled) {
|
||||
ret = pwm_enable(drvdata->pwm);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "Failed to enable PWM\n");
|
||||
return ret;
|
||||
}
|
||||
drvdata->enabled = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_pwm_regulator_list_voltage(struct regulator_dev *dev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
|
||||
|
||||
if (selector >= dev->desc->n_voltages)
|
||||
return -EINVAL;
|
||||
|
||||
return drvdata->pdata->duty_cycle_table[selector].uV;
|
||||
}
|
||||
|
||||
static struct regulator_ops st_pwm_regulator_voltage_ops = {
|
||||
.set_voltage_sel = st_pwm_regulator_set_voltage_sel,
|
||||
.get_voltage_sel = st_pwm_regulator_get_voltage_sel,
|
||||
.list_voltage = st_pwm_regulator_list_voltage,
|
||||
.map_voltage = regulator_map_voltage_iterate,
|
||||
};
|
||||
|
||||
static struct st_pwm_voltages b2105_duty_cycle_table[] = {
|
||||
{ .uV = 1114000, .dutycycle = 0, },
|
||||
{ .uV = 1095000, .dutycycle = 10, },
|
||||
{ .uV = 1076000, .dutycycle = 20, },
|
||||
{ .uV = 1056000, .dutycycle = 30, },
|
||||
{ .uV = 1036000, .dutycycle = 40, },
|
||||
{ .uV = 1016000, .dutycycle = 50, },
|
||||
/* WARNING: Values above 50% duty-cycle cause boot failures. */
|
||||
};
|
||||
|
||||
static const struct regulator_desc b2105_desc = {
|
||||
.name = "b2105-pwm-regulator",
|
||||
.ops = &st_pwm_regulator_voltage_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.n_voltages = ARRAY_SIZE(b2105_duty_cycle_table),
|
||||
.supply_name = "pwm",
|
||||
};
|
||||
|
||||
static const struct st_pwm_regulator_pdata b2105_info = {
|
||||
.desc = &b2105_desc,
|
||||
.duty_cycle_table = b2105_duty_cycle_table,
|
||||
};
|
||||
|
||||
static const struct of_device_id st_pwm_of_match[] = {
|
||||
{ .compatible = "st,b2105-pwm-regulator", .data = &b2105_info, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_pwm_of_match);
|
||||
|
||||
static int st_pwm_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct st_pwm_regulator_data *drvdata;
|
||||
struct regulator_dev *regulator;
|
||||
struct regulator_config config = { };
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *of_match;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "Device Tree node missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
of_match = of_match_device(st_pwm_of_match, &pdev->dev);
|
||||
if (!of_match) {
|
||||
dev_err(&pdev->dev, "failed to match of device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
drvdata->pdata = of_match->data;
|
||||
|
||||
config.init_data = of_get_regulator_init_data(&pdev->dev, np);
|
||||
if (!config.init_data)
|
||||
return -ENOMEM;
|
||||
|
||||
config.of_node = np;
|
||||
config.dev = &pdev->dev;
|
||||
config.driver_data = drvdata;
|
||||
|
||||
drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(drvdata->pwm)) {
|
||||
dev_err(&pdev->dev, "Failed to get PWM\n");
|
||||
return PTR_ERR(drvdata->pwm);
|
||||
}
|
||||
|
||||
regulator = devm_regulator_register(&pdev->dev,
|
||||
drvdata->pdata->desc, &config);
|
||||
if (IS_ERR(regulator)) {
|
||||
dev_err(&pdev->dev, "Failed to register regulator %s\n",
|
||||
drvdata->pdata->desc->name);
|
||||
return PTR_ERR(regulator);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver st_pwm_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "st-pwm-regulator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(st_pwm_of_match),
|
||||
},
|
||||
.probe = st_pwm_regulator_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(st_pwm_regulator_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
|
||||
MODULE_DESCRIPTION("ST PWM Regulator Driver");
|
||||
MODULE_ALIAS("platform:st_pwm-regulator");
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* isl9305 - Intersil ISL9305 DCDC regulator
|
||||
*
|
||||
* Copyright 2014 Linaro Ltd
|
||||
*
|
||||
* Author: Mark Brown <broonie@kernel.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ISL9305_H
|
||||
#define __ISL9305_H
|
||||
|
||||
#define ISL9305_DCD1 0
|
||||
#define ISL9305_DCD2 1
|
||||
#define ISL9305_LDO1 2
|
||||
#define ISL9305_LDO2 3
|
||||
|
||||
#define ISL9305_MAX_REGULATOR ISL9305_LDO2
|
||||
|
||||
struct regulator_init_data;
|
||||
|
||||
struct isl9305_pdata {
|
||||
struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -203,6 +203,8 @@ enum regulator_type {
|
|||
*
|
||||
* @name: Identifying name for the regulator.
|
||||
* @supply_name: Identifying the regulator supply
|
||||
* @of_match: Name used to identify regulator in DT.
|
||||
* @regulators_node: Name of node containing regulator definitions in DT.
|
||||
* @id: Numerical identifier for the regulator.
|
||||
* @ops: Regulator operations table.
|
||||
* @irq: Interrupt number for the regulator.
|
||||
|
@ -240,14 +242,17 @@ enum regulator_type {
|
|||
* @bypass_val_off: Disabling value for control when using regmap set_bypass
|
||||
*
|
||||
* @enable_time: Time taken for initial enable of regulator (in uS).
|
||||
* @off_on_delay: guard time (in uS), before re-enabling a regulator
|
||||
*/
|
||||
struct regulator_desc {
|
||||
const char *name;
|
||||
const char *supply_name;
|
||||
const char *of_match;
|
||||
const char *regulators_node;
|
||||
int id;
|
||||
bool continuous_voltage_range;
|
||||
unsigned n_voltages;
|
||||
struct regulator_ops *ops;
|
||||
const struct regulator_ops *ops;
|
||||
int irq;
|
||||
enum regulator_type type;
|
||||
struct module *owner;
|
||||
|
@ -278,6 +283,8 @@ struct regulator_desc {
|
|||
unsigned int bypass_val_off;
|
||||
|
||||
unsigned int enable_time;
|
||||
|
||||
unsigned int off_on_delay;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -350,6 +357,9 @@ struct regulator_dev {
|
|||
|
||||
struct regulator_enable_gpio *ena_pin;
|
||||
unsigned int ena_gpio_state:1;
|
||||
|
||||
/* time when this regulator was disabled last time */
|
||||
unsigned long last_off_jiffy;
|
||||
};
|
||||
|
||||
struct regulator_dev *
|
||||
|
|
Загрузка…
Ссылка в новой задаче