Required properties:
- compatible: Must be "fsl,anatop-regulator"
+- regulator-name: A string used as a descriptive name for regulator outputs
- anatop-reg-offset: Anatop MFD register offset
- anatop-vol-bit-shift: Bit shift for the register
- anatop-vol-bit-width: Number of bits used in the register
Optional properties:
LM3632 has external enable pins for two LDOs.
- - ti,lcm-en1-gpio: A GPIO specifier for Vpos control pin.
- - ti,lcm-en2-gpio: A GPIO specifier for Vneg control pin.
+ - enable-gpios: Two GPIO specifiers for Vpos and Vneg control pins.
+ The first entry is Vpos, the second is Vneg enable pin.
Child nodes:
LM3631
Examples: Please refer to ti-lmu dt-bindings [2].
+lm3631@29 {
+ compatible = "ti,lm3631";
+ reg = <0x29>;
+
+ regulators {
+ compatible = "ti,lm363x-regulator";
+
+ vboost {
+ regulator-name = "lcd_boost";
+ regulator-min-microvolt = <4500000>;
+ regulator-max-microvolt = <6350000>;
+ regulator-always-on;
+ };
+
+ vcont {
+ regulator-name = "lcd_vcont";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ voref {
+ regulator-name = "lcd_voref";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+
+ vpos {
+ regulator-name = "lcd_vpos";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ regulator-boot-on;
+ };
+
+ vneg {
+ regulator-name = "lcd_vneg";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ regulator-boot-on;
+ };
+ };
+};
+
+lm3632@11 {
+ compatible = "ti,lm3632";
+ reg = <0x11>;
+
+ regulators {
+ compatible = "ti,lm363x-regulator";
+
+ /* GPIO1_16 for Vpos, GPIO1_28 is for Vneg */
+ enable-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>,
+ <&gpio1 28 GPIO_ACTIVE_HIGH>;
+
+ vboost {
+ regulator-name = "lcd_boost";
+ regulator-min-microvolt = <4500000>;
+ regulator-max-microvolt = <6400000>;
+ regulator-always-on;
+ };
+
+ vpos {
+ regulator-name = "lcd_vpos";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+
+ vneg {
+ regulator-name = "lcd_vneg";
+ regulator-min-microvolt = <4000000>;
+ regulator-max-microvolt = <6000000>;
+ };
+ };
+};
+
[1] ../regulator/regulator.txt
[2] ../mfd/ti-lmu.txt
--PFUZE100
sw1ab,sw1c,sw2,sw3a,sw3b,sw4,swbst,vsnvs,vrefddr,vgen1~vgen6
--PFUZE200
- sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6
+ sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin
--PFUZE3000
sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
+
+ coin_reg: coin {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
};
};
design requires. This property describes the total system ramp time
required due to the combination of internal ramping of the regulator itself,
and board design issues such as trace capacitance and load on the supply.
+- regulator-settling-time-us: Settling time, in microseconds, for voltage
+ change if regulator have the constant time for any level voltage change.
+ This is useful when regulator have exponential voltage change.
- regulator-soft-start: Enable soft start so that voltage ramps slowly
- regulator-state-mem sub-root node for Suspend-to-RAM mode
: suspend to memory, the device goes to sleep, but all data stored in memory,
--- /dev/null
+TPS65132 regulators
+
+Required properties:
+- compatible: "ti,tps65132"
+- reg: I2C slave address
+
+Optional Subnode:
+Device supports two regulators OUTP and OUTN. A sub node within the
+ device node describe the properties of these regulators. The sub-node
+ names must be as follows:
+ -For regulator outp, the sub node name should be "outp".
+ -For regulator outn, the sub node name should be "outn".
+
+-enable-gpios:(active high, output) Regulators are controlled by the input pins.
+ If it is connected to GPIO through host system then provide the
+ gpio number as per gpio.txt.
+-active-discharge-gpios: (active high, output) Some configurations use delay mechanisms
+ on the enable pin, to keep the regulator enabled for some time after
+ the enable signal goes low. This GPIO is used to actively discharge
+ the delay mechanism. Requires specification of ti,active-discharge-time-us
+-ti,active-discharge-time-us: how long the active discharge gpio should be
+ asserted for during active discharge, in microseconds.
+
+Each regulator is defined using the standard binding for regulators.
+
+Example:
+
+ tps65132@3e {
+ compatible = "ti,tps65132";
+ reg = <0x3e>;
+
+ outp {
+ regulator-name = "outp";
+ regulator-boot-on;
+ regulator-always-on;
+ enable-gpios = <&gpio 23 0>;
+ };
+
+ outn {
+ regulator-name = "outn";
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-active-discharge = <0>;
+ enable-gpios = <&gpio 40 0>;
+ };
+ };
--- /dev/null
+Bindings for Voltage controlled regulators
+==========================================
+
+Required properties:
+--------------------
+- compatible : must be "vctrl-regulator".
+- regulator-min-microvolt : smallest voltage consumers may set
+- regulator-max-microvolt : largest voltage consumers may set
+- ctrl-supply : The regulator supplying the control voltage.
+- ctrl-voltage-range : an array of two integer values describing the range
+ (min/max) of the control voltage. The values specify
+ the control voltage needed to generate the corresponding
+ regulator-min/max-microvolt output voltage.
+
+Optional properties:
+--------------------
+- ovp-threshold-percent : overvoltage protection (OVP) threshold of the
+ regulator in percent. Some regulators have an OVP
+ circuitry which shuts down the regulator when the
+ actual output voltage deviates beyond a certain
+ margin from the expected value for a given control
+ voltage. On larger voltage decreases this can occur
+ undesiredly since the output voltage does not adjust
+ inmediately to changes in the control voltage. To
+ avoid this situation the vctrl driver breaks down
+ larger voltage decreases into multiple steps, where
+ each step is within the OVP threshold.
+- min-slew-down-rate : Describes how slowly the regulator voltage will decay
+ down in the worst case (lightest expected load).
+ Specified in uV / us (like main regulator ramp rate).
+ This value is required when ovp-threshold-percent is
+ specified.
+
+Example:
+
+ vctrl-reg {
+ compatible = "vctrl-regulator";
+ regulator-name = "vctrl_reg";
+
+ ctrl-supply = <&ctrl_reg>;
+
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1500000>;
+
+ ctrl-voltage-range = <200000 500000>;
+
+ min-slew-down-rate = <225>;
+ ovp-threshold-percent = <16>;
+ };
F: drivers/power/supply/wm83*.c
F: drivers/rtc/rtc-wm83*.c
F: drivers/regulator/wm8*.c
+F: drivers/regulator/arizona*
F: drivers/video/backlight/wm83*_bl.c
F: drivers/watchdog/wm83*_wdt.c
F: include/linux/mfd/arizona/
F: include/linux/mfd/wm831x/
F: include/linux/mfd/wm8350/
F: include/linux/mfd/wm8400*
+F: include/linux/regulator/arizona*
F: include/linux/wm97xx.h
F: include/sound/wm????.h
F: sound/soc/codecs/arizona.?
};
static struct arizona_pdata wm5102_reva_pdata = {
- .ldoena = S3C64XX_GPN(7),
+ .ldo1 = {
+ .ldoena = S3C64XX_GPN(7),
+ },
.gpio_base = CODEC_GPIO_BASE,
.irq_flags = IRQF_TRIGGER_HIGH,
.micd_pol_gpio = CODEC_GPIO_BASE + 4,
};
static struct arizona_pdata wm5102_pdata = {
- .ldoena = S3C64XX_GPN(7),
+ .ldo1 = {
+ .ldoena = S3C64XX_GPN(7),
+ },
.gpio_base = CODEC_GPIO_BASE,
.irq_flags = IRQF_TRIGGER_HIGH,
.micd_pol_gpio = CODEC_GPIO_BASE + 2,
This driver supports the regulators found on the ST-Ericsson mixed
signal AB8500 PMIC
-config REGULATOR_ARIZONA
- tristate "Wolfson Arizona class devices"
+config REGULATOR_ARIZONA_LDO1
+ tristate "Wolfson Arizona class devices LDO1"
depends on MFD_ARIZONA
depends on SND_SOC
help
- Support for the regulators found on Wolfson Arizona class
+ Support for the LDO1 regulators found on Wolfson Arizona class
+ devices.
+
+config REGULATOR_ARIZONA_MICSUPP
+ tristate "Wolfson Arizona class devices MICSUPP"
+ depends on MFD_ARIZONA
+ depends on SND_SOC
+ help
+ Support for the MICSUPP regulators found on Wolfson Arizona class
devices.
config REGULATOR_AS3711
BCM590xx PMUs. This will enable support for the software
controllable LDO/Switching regulators.
+config REGULATOR_BD9571MWV
+ tristate "ROHM BD9571MWV Regulators"
+ depends on MFD_BD9571MWV
+ help
+ This driver provides support for the voltage regulators on the
+ ROHM BD9571MWV PMIC. This will enable support for the software
+ controllable regulator and voltage sampling units.
+
+ This driver can also be built as a module. If so, the module
+ will be called bd9571mwv-regulator.
+
config REGULATOR_CPCAP
tristate "Motorola CPCAP regulator"
depends on MFD_CPCAP
This driver provides support for the voltage regulators on the
TI TPS65090 PMIC.
+config REGULATOR_TPS65132
+ tristate "TI TPS65132 Dual Output Power regulators"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ This driver supports TPS65132 single inductor - dual output
+ power supply specifcally designed for display panels.
+
config REGULATOR_TPS65217
tristate "TI TPS65217 Power regulators"
depends on MFD_TPS65217
This driver supports the voltage regulators provided by
this family of companion chips.
+config REGULATOR_VCTRL
+ tristate "Voltage controlled regulators"
+ depends on OF
+ help
+ This driver provides support for voltage regulators whose output
+ voltage is controlled by the voltage of another regulator.
+
config REGULATOR_VEXPRESS
tristate "Versatile Express regulators"
depends on VEXPRESS_CONFIG
obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
-obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
+obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o
+obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
+obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
+obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
#define LDO_FET_FULL_ON 0x1f
struct anatop_regulator {
- const char *name;
u32 control_reg;
struct regmap *anatop;
int vol_bit_shift;
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
if (!sreg)
return -ENOMEM;
- sreg->name = of_get_property(np, "regulator-name", NULL);
+
rdesc = &sreg->rdesc;
- rdesc->name = sreg->name;
rdesc->type = REGULATOR_VOLTAGE;
rdesc->owner = THIS_MODULE;
+ of_property_read_string(np, "regulator-name", &rdesc->name);
+ if (!rdesc->name) {
+ dev_err(dev, "failed to get a regulator-name\n");
+ return -EINVAL;
+ }
+
initdata = of_get_regulator_init_data(dev, np, rdesc);
+ if (!initdata)
+ return -ENOMEM;
+
initdata->supply_regulator = "vin";
sreg->initdata = initdata;
* a sane default until imx6-cpufreq was probed and changes the
* voltage to the correct value. In this case we set 1.25V.
*/
- if (!sreg->sel && !strcmp(sreg->name, "vddpu"))
+ if (!sreg->sel && !strcmp(rdesc->name, "vddpu"))
sreg->sel = 22;
+ /* set the default voltage of the pcie phy to be 1.100v */
+ if (!sreg->sel && !strcmp(rdesc->name, "vddpcie"))
+ sreg->sel = 0x10;
+
if (!sreg->bypass && !sreg->sel) {
dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
return -EINVAL;
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/regulator/arizona-ldo1.h>
+
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
struct arizona_ldo1 {
struct regulator_dev *regulator;
- struct arizona *arizona;
+ struct regmap *regmap;
struct regulator_consumer_supply supply;
struct regulator_init_data init_data;
unsigned sel)
{
struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
- struct regmap *regmap = ldo->arizona->regmap;
+ struct regmap *regmap = ldo->regmap;
unsigned int val;
int ret;
static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)
{
struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
- struct regmap *regmap = ldo->arizona->regmap;
+ struct regmap *regmap = ldo->regmap;
unsigned int val;
int ret;
.num_consumer_supplies = 1,
};
-static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
+static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,
struct regulator_config *config,
- const struct regulator_desc *desc)
+ const struct regulator_desc *desc,
+ bool *external_dcvdd)
{
- struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_ldo1 *ldo1 = config->driver_data;
- struct device_node *np = arizona->dev->of_node;
+ struct device_node *np = config->dev->of_node;
struct device_node *init_node, *dcvdd_node;
struct regulator_init_data *init_data;
pdata->ldoena = of_get_named_gpio(np, "wlf,ldoena", 0);
if (pdata->ldoena < 0) {
- dev_warn(arizona->dev,
+ dev_warn(config->dev,
"LDOENA GPIO property missing/malformed: %d\n",
pdata->ldoena);
pdata->ldoena = 0;
if (init_node) {
config->of_node = init_node;
- init_data = of_get_regulator_init_data(arizona->dev, init_node,
+ init_data = of_get_regulator_init_data(config->dev, init_node,
desc);
-
if (init_data) {
init_data->consumer_supplies = &ldo1->supply;
init_data->num_consumer_supplies = 1;
if (dcvdd_node && dcvdd_node != init_node)
- arizona->external_dcvdd = true;
+ *external_dcvdd = true;
- pdata->ldo1 = init_data;
+ pdata->init_data = init_data;
}
} else if (dcvdd_node) {
- arizona->external_dcvdd = true;
+ *external_dcvdd = true;
}
of_node_put(dcvdd_node);
return 0;
}
-static int arizona_ldo1_probe(struct platform_device *pdev)
+static int arizona_ldo1_common_init(struct platform_device *pdev,
+ struct arizona_ldo1 *ldo1,
+ const struct regulator_desc *desc,
+ struct arizona_ldo1_pdata *pdata,
+ bool *external_dcvdd)
{
- struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- const struct regulator_desc *desc;
+ struct device *parent_dev = pdev->dev.parent;
struct regulator_config config = { };
- struct arizona_ldo1 *ldo1;
int ret;
- arizona->external_dcvdd = false;
-
- ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
- if (!ldo1)
- return -ENOMEM;
-
- ldo1->arizona = arizona;
-
- /*
- * Since the chip usually supplies itself we provide some
- * default init_data for it. This will be overridden with
- * platform data if provided.
- */
- switch (arizona->type) {
- case WM5102:
- case WM8997:
- case WM8998:
- case WM1814:
- desc = &arizona_ldo1_hc;
- ldo1->init_data = arizona_ldo1_dvfs;
- break;
- case WM5110:
- case WM8280:
- desc = &arizona_ldo1;
- ldo1->init_data = arizona_ldo1_wm5110;
- break;
- default:
- desc = &arizona_ldo1;
- ldo1->init_data = arizona_ldo1_default;
- break;
- }
+ *external_dcvdd = false;
- ldo1->init_data.consumer_supplies = &ldo1->supply;
ldo1->supply.supply = "DCVDD";
- ldo1->supply.dev_name = dev_name(arizona->dev);
+ ldo1->init_data.consumer_supplies = &ldo1->supply;
+ ldo1->supply.dev_name = dev_name(parent_dev);
- config.dev = arizona->dev;
+ config.dev = parent_dev;
config.driver_data = ldo1;
- config.regmap = arizona->regmap;
+ config.regmap = ldo1->regmap;
if (IS_ENABLED(CONFIG_OF)) {
- if (!dev_get_platdata(arizona->dev)) {
- ret = arizona_ldo1_of_get_pdata(arizona, &config, desc);
+ if (!dev_get_platdata(parent_dev)) {
+ ret = arizona_ldo1_of_get_pdata(pdata,
+ &config, desc,
+ external_dcvdd);
if (ret < 0)
return ret;
}
}
- config.ena_gpio = arizona->pdata.ldoena;
+ config.ena_gpio = pdata->ldoena;
- if (arizona->pdata.ldo1)
- config.init_data = arizona->pdata.ldo1;
+ if (pdata->init_data)
+ config.init_data = pdata->init_data;
else
config.init_data = &ldo1->init_data;
* consumers then DCVDD is supplied externally.
*/
if (config.init_data->num_consumer_supplies == 0)
- arizona->external_dcvdd = true;
+ *external_dcvdd = true;
ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
if (IS_ERR(ldo1->regulator)) {
ret = PTR_ERR(ldo1->regulator);
- dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n",
+ dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
ret);
return ret;
}
return 0;
}
+static int arizona_ldo1_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct arizona_ldo1 *ldo1;
+ const struct regulator_desc *desc;
+ bool external_dcvdd;
+ int ret;
+
+ ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
+ if (!ldo1)
+ return -ENOMEM;
+
+ ldo1->regmap = arizona->regmap;
+
+ /*
+ * Since the chip usually supplies itself we provide some
+ * default init_data for it. This will be overridden with
+ * platform data if provided.
+ */
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ case WM8998:
+ case WM1814:
+ desc = &arizona_ldo1_hc;
+ ldo1->init_data = arizona_ldo1_dvfs;
+ break;
+ case WM5110:
+ case WM8280:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_wm5110;
+ break;
+ default:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_default;
+ break;
+ }
+
+ ret = arizona_ldo1_common_init(pdev, ldo1, desc,
+ &arizona->pdata.ldo1,
+ &external_dcvdd);
+ if (ret == 0)
+ arizona->external_dcvdd = external_dcvdd;
+
+ return ret;
+}
+
static struct platform_driver arizona_ldo1_driver = {
.probe = arizona_ldo1_probe,
.driver = {
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
+#include <linux/regulator/arizona-micsupp.h>
+
struct arizona_micsupp {
struct regulator_dev *regulator;
- struct arizona *arizona;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context **dapm;
+ unsigned int enable_reg;
+ struct device *dev;
struct regulator_consumer_supply supply;
struct regulator_init_data init_data;
{
struct arizona_micsupp *micsupp =
container_of(work, struct arizona_micsupp, check_cp_work);
- struct snd_soc_dapm_context *dapm = micsupp->arizona->dapm;
- struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
- struct arizona *arizona = micsupp->arizona;
- struct regmap *regmap = arizona->regmap;
- unsigned int reg;
+ struct snd_soc_dapm_context *dapm = *micsupp->dapm;
+ struct snd_soc_component *component;
+ unsigned int val;
int ret;
- ret = regmap_read(regmap, ARIZONA_MIC_CHARGE_PUMP_1, ®);
+ ret = regmap_read(micsupp->regmap, micsupp->enable_reg, &val);
if (ret != 0) {
- dev_err(arizona->dev, "Failed to read CP state: %d\n", ret);
+ dev_err(micsupp->dev,
+ "Failed to read CP state: %d\n", ret);
return;
}
if (dapm) {
- if ((reg & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
+ component = snd_soc_dapm_to_component(dapm);
+
+ if ((val & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
ARIZONA_CPMIC_ENA)
snd_soc_component_force_enable_pin(component,
"MICSUPP");
.num_consumer_supplies = 1,
};
-static int arizona_micsupp_of_get_pdata(struct arizona *arizona,
+static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata,
struct regulator_config *config,
const struct regulator_desc *desc)
{
- struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_micsupp *micsupp = config->driver_data;
struct device_node *np;
struct regulator_init_data *init_data;
- np = of_get_child_by_name(arizona->dev->of_node, "micvdd");
+ np = of_get_child_by_name(config->dev->of_node, "micvdd");
if (np) {
config->of_node = np;
- init_data = of_get_regulator_init_data(arizona->dev, np, desc);
+ init_data = of_get_regulator_init_data(config->dev, np, desc);
if (init_data) {
init_data->consumer_supplies = &micsupp->supply;
init_data->num_consumer_supplies = 1;
- pdata->micvdd = init_data;
+ pdata->init_data = init_data;
}
}
return 0;
}
-static int arizona_micsupp_probe(struct platform_device *pdev)
+static int arizona_micsupp_common_init(struct platform_device *pdev,
+ struct arizona_micsupp *micsupp,
+ const struct regulator_desc *desc,
+ struct arizona_micsupp_pdata *pdata)
{
- struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- const struct regulator_desc *desc;
struct regulator_config config = { };
- struct arizona_micsupp *micsupp;
int ret;
- micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
- if (!micsupp)
- return -ENOMEM;
-
- micsupp->arizona = arizona;
INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp);
- /*
- * Since the chip usually supplies itself we provide some
- * default init_data for it. This will be overridden with
- * platform data if provided.
- */
- switch (arizona->type) {
- case WM5110:
- case WM8280:
- desc = &arizona_micsupp_ext;
- micsupp->init_data = arizona_micsupp_ext_default;
- break;
- default:
- desc = &arizona_micsupp;
- micsupp->init_data = arizona_micsupp_default;
- break;
- }
-
micsupp->init_data.consumer_supplies = &micsupp->supply;
micsupp->supply.supply = "MICVDD";
- micsupp->supply.dev_name = dev_name(arizona->dev);
+ micsupp->supply.dev_name = dev_name(micsupp->dev);
+ micsupp->enable_reg = desc->enable_reg;
- config.dev = arizona->dev;
+ config.dev = micsupp->dev;
config.driver_data = micsupp;
- config.regmap = arizona->regmap;
+ config.regmap = micsupp->regmap;
if (IS_ENABLED(CONFIG_OF)) {
- if (!dev_get_platdata(arizona->dev)) {
- ret = arizona_micsupp_of_get_pdata(arizona, &config,
+ if (!dev_get_platdata(micsupp->dev)) {
+ ret = arizona_micsupp_of_get_pdata(pdata, &config,
desc);
if (ret < 0)
return ret;
}
}
- if (arizona->pdata.micvdd)
- config.init_data = arizona->pdata.micvdd;
+ if (pdata->init_data)
+ config.init_data = pdata->init_data;
else
config.init_data = &micsupp->init_data;
- /* Default to regulated mode until the API supports bypass */
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1,
+ /* Default to regulated mode */
+ regmap_update_bits(micsupp->regmap, micsupp->enable_reg,
ARIZONA_CPMIC_BYPASS, 0);
micsupp->regulator = devm_regulator_register(&pdev->dev,
if (IS_ERR(micsupp->regulator)) {
ret = PTR_ERR(micsupp->regulator);
- dev_err(arizona->dev, "Failed to register mic supply: %d\n",
+ dev_err(micsupp->dev, "Failed to register mic supply: %d\n",
ret);
return ret;
}
return 0;
}
+static int arizona_micsupp_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *desc;
+ struct arizona_micsupp *micsupp;
+
+ micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
+ if (!micsupp)
+ return -ENOMEM;
+
+ micsupp->regmap = arizona->regmap;
+ micsupp->dapm = &arizona->dapm;
+ micsupp->dev = arizona->dev;
+
+ /*
+ * Since the chip usually supplies itself we provide some
+ * default init_data for it. This will be overridden with
+ * platform data if provided.
+ */
+ switch (arizona->type) {
+ case WM5110:
+ case WM8280:
+ desc = &arizona_micsupp_ext;
+ micsupp->init_data = arizona_micsupp_ext_default;
+ break;
+ default:
+ desc = &arizona_micsupp;
+ micsupp->init_data = arizona_micsupp_default;
+ break;
+ }
+
+ return arizona_micsupp_common_init(pdev, micsupp, desc,
+ &arizona->pdata.micvdd);
+}
+
static struct platform_driver arizona_micsupp_driver = {
.probe = arizona_micsupp_probe,
.driver = {
--- /dev/null
+/*
+ * ROHM BD9571MWV-M regulator driver
+ *
+ * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65086 driver
+ *
+ * NOTE: VD09 is missing
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/bd9571mwv.h>
+
+enum bd9571mwv_regulators { VD09, VD18, VD25, VD33, DVFS };
+
+#define BD9571MWV_REG(_name, _of, _id, _ops, _vr, _vm, _nv, _min, _step, _lmin)\
+ { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .id = _id, \
+ .ops = &_ops, \
+ .n_voltages = _nv, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .min_uV = _min, \
+ .uV_step = _step, \
+ .linear_min_sel = _lmin, \
+ }
+
+int bd9571mwv_avs_get_moni_state(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, BD9571MWV_AVS_SET_MONI, &val);
+ if (ret != 0)
+ return ret;
+
+ return val & BD9571MWV_AVS_SET_MONI_MASK;
+}
+
+int bd9571mwv_avs_set_voltage_sel_regmap(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ int ret;
+
+ ret = bd9571mwv_avs_get_moni_state(rdev);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write_bits(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret),
+ rdev->desc->vsel_mask, sel);
+}
+
+int bd9571mwv_avs_get_voltage_sel_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = bd9571mwv_avs_get_moni_state(rdev);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), &val);
+ if (ret != 0)
+ return ret;
+
+ val &= rdev->desc->vsel_mask;
+ val >>= ffs(rdev->desc->vsel_mask) - 1;
+
+ return val;
+}
+
+int bd9571mwv_reg_set_voltage_sel_regmap(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ return regmap_write_bits(rdev->regmap, BD9571MWV_DVFS_SETVID,
+ rdev->desc->vsel_mask, sel);
+}
+
+/* Operations permitted on AVS voltage regulator */
+static struct regulator_ops avs_ops = {
+ .set_voltage_sel = bd9571mwv_avs_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = bd9571mwv_avs_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* Operations permitted on voltage regulators */
+static struct regulator_ops reg_ops = {
+ .set_voltage_sel = bd9571mwv_reg_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* Operations permitted on voltage monitors */
+static struct regulator_ops vid_ops = {
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_desc regulators[] = {
+ BD9571MWV_REG("VD09", "vd09", VD09, avs_ops, 0, 0x7f,
+ 0x80, 600000, 10000, 0x3c),
+ BD9571MWV_REG("VD18", "vd18", VD18, vid_ops, BD9571MWV_VD18_VID, 0xf,
+ 16, 1625000, 25000, 0),
+ BD9571MWV_REG("VD25", "vd25", VD25, vid_ops, BD9571MWV_VD25_VID, 0xf,
+ 16, 2150000, 50000, 0),
+ BD9571MWV_REG("VD33", "vd33", VD33, vid_ops, BD9571MWV_VD33_VID, 0xf,
+ 11, 2800000, 100000, 0),
+ BD9571MWV_REG("DVFS", "dvfs", DVFS, reg_ops,
+ BD9571MWV_DVFS_MONIVDAC, 0x7f,
+ 0x80, 600000, 10000, 0x3c),
+};
+
+static int bd9571mwv_regulator_probe(struct platform_device *pdev)
+{
+ struct bd9571mwv *bd = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i;
+
+ platform_set_drvdata(pdev, bd);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = bd->dev->of_node;
+ config.driver_data = bd;
+ config.regmap = bd->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, ®ulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(bd->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id bd9571mwv_regulator_id_table[] = {
+ { "bd9571mwv-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table);
+
+static struct platform_driver bd9571mwv_regulator_driver = {
+ .driver = {
+ .name = "bd9571mwv-regulator",
+ },
+ .probe = bd9571mwv_regulator_probe,
+ .id_table = bd9571mwv_regulator_id_table,
+};
+module_platform_driver(bd9571mwv_regulator_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>");
+MODULE_DESCRIPTION("BD9571MWV Regulator driver");
+MODULE_LICENSE("GPL v2");
regulator->dev = dev;
/* Add a link to the device sysfs entry */
- size = scnprintf(buf, REG_STR_SIZE, "%s-%s",
- dev->kobj.name, supply_name);
+ size = snprintf(buf, REG_STR_SIZE, "%s-%s",
+ dev->kobj.name, supply_name);
if (size >= REG_STR_SIZE)
goto overflow_err;
/* non-fatal */
}
} else {
- regulator->supply_name = kstrdup(supply_name, GFP_KERNEL);
+ regulator->supply_name = kstrdup_const(supply_name, GFP_KERNEL);
if (regulator->supply_name == NULL)
goto overflow_err;
}
* regulator_dev_lookup - lookup a regulator device.
* @dev: device for regulator "consumer".
* @supply: Supply name or regulator ID.
- * @ret: 0 on success, -ENODEV if lookup fails permanently, -EPROBE_DEFER if
- * lookup could succeed in the future.
*
* If successful, returns a struct regulator_dev that corresponds to the name
* @supply and with the embedded struct device refcount incremented by one.
if (IS_ERR(r)) {
ret = PTR_ERR(r);
- if (ret == -ENODEV) {
- /*
- * No supply was specified for this regulator and
- * there will never be one.
- */
- return 0;
- }
-
/* Did the lookup explicitly defer for us? */
if (ret == -EPROBE_DEFER)
return ret;
put_device(&rdev->dev);
mutex_unlock(&rdev->mutex);
- kfree(regulator->supply_name);
+ kfree_const(regulator->supply_name);
kfree(regulator);
module_put(rdev->owner);
if (ret < 0)
return ret;
+ _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE,
+ NULL);
} else if (ret < 0) {
rdev_err(rdev, "is_enabled() failed: %d\n", ret);
return ret;
ret = ops->list_voltage(rdev, selector);
if (lock)
mutex_unlock(&rdev->mutex);
- } else if (rdev->supply) {
+ } else if (rdev->is_switch && rdev->supply) {
ret = _regulator_list_voltage(rdev->supply, selector, lock);
} else {
return -EINVAL;
if (rdev->desc->n_voltages)
return rdev->desc->n_voltages;
- if (!rdev->supply)
+ if (!rdev->is_switch || !rdev->supply)
return -EINVAL;
return regulator_count_voltages(rdev->supply);
ramp_delay = rdev->constraints->ramp_delay;
else if (rdev->desc->ramp_delay)
ramp_delay = rdev->desc->ramp_delay;
+ else if (rdev->constraints->settling_time)
+ return rdev->constraints->settling_time;
if (ramp_delay == 0) {
rdev_dbg(rdev, "ramp_delay not set\n");
if (ret < 0)
goto out2;
- if (rdev->supply && (rdev->desc->min_dropout_uV ||
- !rdev->desc->ops->get_voltage)) {
+ if (rdev->supply &&
+ regulator_ops_is_valid(rdev->supply->rdev,
+ REGULATOR_CHANGE_VOLTAGE) &&
+ (rdev->desc->min_dropout_uV || !rdev->desc->ops->get_voltage)) {
int current_supply_uV;
int selector;
mutex_unlock(®ulator_list_mutex);
}
+ if (!rdev->desc->ops->get_voltage &&
+ !rdev->desc->ops->list_voltage &&
+ !rdev->desc->fixed_uV)
+ rdev->is_switch = true;
+
ret = device_register(&rdev->dev);
if (ret != 0) {
put_device(&rdev->dev);
}
EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+/**
+ * regulator_set_soft_start_regmap - Default set_soft_start() using regmap
+ *
+ * @rdev: device to operate on.
+ */
+int regulator_set_soft_start_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ val = rdev->desc->soft_start_val_on;
+ if (!val)
+ val = rdev->desc->soft_start_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->soft_start_reg,
+ rdev->desc->soft_start_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_soft_start_regmap);
+
+/**
+ * regulator_set_pull_down_regmap - Default set_pull_down() using regmap
+ *
+ * @rdev: device to operate on.
+ */
+int regulator_set_pull_down_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ val = rdev->desc->pull_down_val_on;
+ if (!val)
+ val = rdev->desc->pull_down_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->pull_down_reg,
+ rdev->desc->pull_down_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_pull_down_regmap);
+
/**
* regulator_get_bypass_regmap - Default get_bypass() using regmap
*
return 0;
}
+static const struct platform_device_id hi655x_regulator_table[] = {
+ { .name = "hi655x-regulator" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, hi655x_regulator_table);
+
static struct platform_driver hi655x_regulator_driver = {
+ .id_table = hi655x_regulator_table,
.driver = {
.name = "hi655x-regulator",
},
int uA_load;
int min_uV;
int max_uV;
- char *supply_name;
+ const char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
struct dentry *debugfs;
*/
switch (id) {
case LM3632_LDO_POS:
- return of_get_named_gpio(np, "ti,lcm-en1-gpio", 0);
+ return of_get_named_gpio(np, "enable-gpios", 0);
case LM3632_LDO_NEG:
- return of_get_named_gpio(np, "ti,lcm-en2-gpio", 0);
+ return of_get_named_gpio(np, "enable-gpios", 1);
default:
return -EINVAL;
}
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
return -ENOMEM;
i2c_set_clientdata(client, ltc3589);
- ltc3589->variant = id->driver_data;
+ if (client->dev.of_node)
+ ltc3589->variant = (enum ltc3589_variant)
+ of_device_get_match_data(&client->dev);
+ else
+ ltc3589->variant = id->driver_data;
ltc3589->dev = dev;
descs = ltc3589->regulator_descs;
};
MODULE_DEVICE_TABLE(i2c, ltc3589_i2c_id);
+static const struct of_device_id ltc3589_of_match[] = {
+ {
+ .compatible = "lltc,ltc3589",
+ .data = (void *)LTC3589,
+ },
+ {
+ .compatible = "lltc,ltc3589-1",
+ .data = (void *)LTC3589_1,
+ },
+ {
+ .compatible = "lltc,ltc3589-2",
+ .data = (void *)LTC3589_2,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ltc3589_of_match);
+
static struct i2c_driver ltc3589_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ltc3589_of_match),
},
.probe = ltc3589_probe,
.id_table = ltc3589_i2c_id,
};
MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id);
+static const struct of_device_id ltc3676_of_match[] = {
+ { .compatible = "lltc,ltc3676" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ltc3676_of_match);
+
static struct i2c_driver ltc3676_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ltc3676_of_match),
},
.probe = ltc3676_regulator_probe,
.id_table = ltc3676_i2c_id,
* The Maxim 1586 controls V3 and V6 voltages, but offers no way of reading back
* the set up value.
*/
-static struct regulator_ops max1586_v3_ops = {
+static const struct regulator_ops max1586_v3_ops = {
.get_voltage_sel = max1586_v3_get_voltage_sel,
.set_voltage_sel = max1586_v3_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
};
-static struct regulator_ops max1586_v6_ops = {
+static const struct regulator_ops max1586_v6_ops = {
.get_voltage_sel = max1586_v6_get_voltage_sel,
.set_voltage_sel = max1586_v6_set_voltage_sel,
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
-static struct regulator_ops max77693_charger_ops = {
+static const struct regulator_ops max77693_charger_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0);
}
-static struct regulator_ops max8660_ldo5_ops = {
+static const struct regulator_ops max8660_ldo5_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.set_voltage_sel = max8660_ldo5_set_voltage_sel,
selector << 4);
}
-static struct regulator_ops max8660_ldo67_ops = {
+static const struct regulator_ops max8660_ldo67_ops = {
.is_enabled = max8660_ldo67_is_enabled,
.enable = max8660_ldo67_enable,
.disable = max8660_ldo67_disable,
constraints->ramp_disable = true;
}
+ ret = of_property_read_u32(np, "regulator-settling-time-us", &pval);
+ if (!ret)
+ constraints->settling_time = pval;
+
ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval);
if (!ret)
constraints->enable_time = pval;
#define PFUZE100_REVID 0x3
#define PFUZE100_FABID 0x4
+#define PFUZE100_COINVOL 0x1a
#define PFUZE100_SW1ABVOL 0x20
#define PFUZE100_SW1CVOL 0x2e
#define PFUZE100_SW2VOL 0x35
1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000,
};
+static const int pfuze100_coin[] = {
+ 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000,
+};
+
static const int pfuze3000_sw2lo[] = {
1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000,
};
.stby_mask = 0x20, \
}
+#define PFUZE100_COIN_REG(_chip, _name, base, mask, voltages) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pfuze100_swb_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base), \
+ .vsel_mask = (mask), \
+ .enable_reg = (base), \
+ .enable_mask = 0x8, \
+ }, \
+ }
+
#define PFUZE3000_VCC_REG(_chip, _name, base, min, max, step) { \
.desc = { \
.name = #_name, \
PFUZE100_VGEN_REG(PFUZE200, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000),
PFUZE100_VGEN_REG(PFUZE200, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+ PFUZE100_COIN_REG(PFUZE200, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin),
};
static struct pfuze_regulator pfuze3000_regulators[] = {
{ .name = "vgen4", },
{ .name = "vgen5", },
{ .name = "vgen6", },
+ { .name = "coin", },
};
/* PFUZE3000 */
RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
BIT(0), 400),
RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
- RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
BIT(1), 400),
{
.name = "LDO_REG3",
1 << enable_shift, 0);
}
-static struct regulator_ops s2mpa01_ldo_ops = {
+static const struct regulator_ops s2mpa01_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
-static struct regulator_ops s2mpa01_buck_ops = {
+static const struct regulator_ops s2mpa01_buck_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
if (iodev->dev->of_node) {
reg_np = of_get_child_by_name(iodev->dev->of_node,
"regulators");
- if (!reg_np) {
- dev_err(&pdev->dev,
- "could not find regulators sub-node\n");
- return -EINVAL;
- }
+ if (!reg_np) {
+ dev_err(&pdev->dev,
+ "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
of_regulator_match(&pdev->dev, reg_np, rdata,
S2MPA01_REGULATOR_MAX);
1 << enable_shift, 0);
}
-static struct regulator_ops s2mps11_ldo_ops = {
+static const struct regulator_ops s2mps11_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
-static struct regulator_ops s2mps11_buck_ops = {
+static const struct regulator_ops s2mps11_buck_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV),
};
-static struct regulator_ops s2mps14_reg_ops;
+static const struct regulator_ops s2mps14_reg_ops;
#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \
.name = "LDO"#num, \
rdev->desc->enable_mask, state);
}
-static struct regulator_ops s2mps14_reg_ops = {
+static const struct regulator_ops s2mps14_reg_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
S2MPS14_BUCK1235_START_SEL),
};
-static struct regulator_ops s2mps15_reg_ldo_ops = {
+static const struct regulator_ops s2mps15_reg_ldo_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
-static struct regulator_ops s2mps15_reg_buck_ops = {
+static const struct regulator_ops s2mps15_reg_buck_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.is_enabled = regulator_is_enabled_regmap,
ramp_val << ramp_shift);
}
-static struct regulator_ops s2mpu02_ldo_ops = {
+static const struct regulator_ops s2mpu02_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.set_suspend_disable = s2mps14_regulator_set_suspend_disable,
};
-static struct regulator_ops s2mpu02_buck_ops = {
+static const struct regulator_ops s2mpu02_buck_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
return 0;
}
-static struct regulator_ops s5m8767_ops = {
+static const struct regulator_ops s5m8767_ops = {
.list_voltage = regulator_list_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.set_voltage_time_sel = s5m8767_set_voltage_time_sel,
};
-static struct regulator_ops s5m8767_buck78_ops = {
+static const struct regulator_ops s5m8767_buck78_ops = {
.list_voltage = regulator_list_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
/* Enable setting output voltage by I2C */
regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2,
- TPS65023_REG_CTRL2_CORE_ADJ,
- TPS65023_REG_CTRL2_CORE_ADJ);
+ TPS65023_REG_CTRL2_CORE_ADJ, 0);
return 0;
}
--- /dev/null
+/*
+ * TI TPS65132 Regulator driver
+ *
+ * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Venkat Reddy Talla <vreddytalla@nvidia.com>
+ * Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#define TPS65132_REG_VPOS 0x00
+#define TPS65132_REG_VNEG 0x01
+#define TPS65132_REG_APPS_DISP_DISN 0x03
+#define TPS65132_REG_CONTROL 0x0FF
+
+#define TPS65132_VOUT_MASK 0x1F
+#define TPS65132_VOUT_N_VOLTAGE 0x15
+#define TPS65132_VOUT_VMIN 4000000
+#define TPS65132_VOUT_VMAX 6000000
+#define TPS65132_VOUT_STEP 100000
+
+#define TPS65132_REG_APPS_DIS_VPOS BIT(0)
+#define TPS65132_REG_APPS_DIS_VNEG BIT(1)
+
+#define TPS65132_REGULATOR_ID_VPOS 0
+#define TPS65132_REGULATOR_ID_VNEG 1
+#define TPS65132_MAX_REGULATORS 2
+
+#define TPS65132_ACT_DIS_TIME_SLACK 1000
+
+struct tps65132_reg_pdata {
+ struct gpio_desc *en_gpiod;
+ struct gpio_desc *act_dis_gpiod;
+ unsigned int act_dis_time_us;
+ int ena_gpio_state;
+};
+
+struct tps65132_regulator {
+ struct device *dev;
+ struct regmap *rmap;
+ struct regulator_desc *rdesc[TPS65132_MAX_REGULATORS];
+ struct tps65132_reg_pdata reg_pdata[TPS65132_MAX_REGULATORS];
+ struct regulator_dev *rdev[TPS65132_MAX_REGULATORS];
+};
+
+static int tps65132_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+ int ret;
+
+ if (!IS_ERR(rpdata->en_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->en_gpiod, 1);
+ rpdata->ena_gpio_state = 1;
+ }
+
+ /* Hardware automatically enable discharge bit in enable */
+ if (rdev->constraints->active_discharge ==
+ REGULATOR_ACTIVE_DISCHARGE_DISABLE) {
+ ret = regulator_set_active_discharge_regmap(rdev, false);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to disable active discharge: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tps65132_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+
+ if (!IS_ERR(rpdata->en_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->en_gpiod, 0);
+ rpdata->ena_gpio_state = 0;
+ }
+
+ if (!IS_ERR(rpdata->act_dis_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 1);
+ usleep_range(rpdata->act_dis_time_us, rpdata->act_dis_time_us +
+ TPS65132_ACT_DIS_TIME_SLACK);
+ gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 0);
+ }
+
+ return 0;
+}
+
+static int tps65132_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+
+ if (!IS_ERR(rpdata->en_gpiod))
+ return rpdata->ena_gpio_state;
+
+ return 1;
+}
+
+static struct regulator_ops tps65132_regulator_ops = {
+ .enable = tps65132_regulator_enable,
+ .disable = tps65132_regulator_disable,
+ .is_enabled = tps65132_regulator_is_enabled,
+ .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,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static int tps65132_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct tps65132_regulator *tps = config->driver_data;
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[desc->id];
+ int ret;
+
+ rpdata->en_gpiod = devm_fwnode_get_index_gpiod_from_child(tps->dev,
+ "enable", 0, &np->fwnode, 0, "enable");
+ if (IS_ERR(rpdata->en_gpiod)) {
+ ret = PTR_ERR(rpdata->en_gpiod);
+
+ /* Ignore the error other than probe defer */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ return 0;
+ }
+
+ rpdata->act_dis_gpiod = devm_fwnode_get_index_gpiod_from_child(
+ tps->dev, "active-discharge", 0,
+ &np->fwnode, 0, "active-discharge");
+ if (IS_ERR(rpdata->act_dis_gpiod)) {
+ ret = PTR_ERR(rpdata->act_dis_gpiod);
+
+ /* Ignore the error other than probe defer */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return 0;
+ }
+
+ ret = of_property_read_u32(np, "ti,active-discharge-time-us",
+ &rpdata->act_dis_time_us);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to read active discharge time:%d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define TPS65132_REGULATOR_DESC(_id, _name) \
+ [TPS65132_REGULATOR_ID_##_id] = { \
+ .name = "tps65132-"#_name, \
+ .supply_name = "vin", \
+ .id = TPS65132_REGULATOR_ID_##_id, \
+ .of_match = of_match_ptr(#_name), \
+ .of_parse_cb = tps65132_of_parse_cb, \
+ .ops = &tps65132_regulator_ops, \
+ .n_voltages = TPS65132_VOUT_N_VOLTAGE, \
+ .min_uV = TPS65132_VOUT_VMIN, \
+ .uV_step = TPS65132_VOUT_STEP, \
+ .enable_time = 500, \
+ .vsel_mask = TPS65132_VOUT_MASK, \
+ .vsel_reg = TPS65132_REG_##_id, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = TPS65132_REG_APPS_DIS_##_id, \
+ .active_discharge_mask = TPS65132_REG_APPS_DIS_##_id, \
+ .active_discharge_reg = TPS65132_REG_APPS_DISP_DISN, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+static struct regulator_desc tps_regs_desc[TPS65132_MAX_REGULATORS] = {
+ TPS65132_REGULATOR_DESC(VPOS, outp),
+ TPS65132_REGULATOR_DESC(VNEG, outn),
+};
+
+static const struct regmap_range tps65132_no_reg_ranges[] = {
+ regmap_reg_range(TPS65132_REG_APPS_DISP_DISN + 1,
+ TPS65132_REG_CONTROL - 1),
+};
+
+static const struct regmap_access_table tps65132_no_reg_table = {
+ .no_ranges = tps65132_no_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(tps65132_no_reg_ranges),
+};
+
+static const struct regmap_config tps65132_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPS65132_REG_CONTROL,
+ .cache_type = REGCACHE_NONE,
+ .rd_table = &tps65132_no_reg_table,
+ .wr_table = &tps65132_no_reg_table,
+};
+
+static int tps65132_probe(struct i2c_client *client,
+ const struct i2c_device_id *client_id)
+{
+ struct device *dev = &client->dev;
+ struct tps65132_regulator *tps;
+ struct regulator_config config = { };
+ int id;
+ int ret;
+
+ tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->rmap = devm_regmap_init_i2c(client, &tps65132_regmap_config);
+ if (IS_ERR(tps->rmap)) {
+ ret = PTR_ERR(tps->rmap);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = dev;
+
+ for (id = 0; id < TPS65132_MAX_REGULATORS; ++id) {
+ tps->rdesc[id] = &tps_regs_desc[id];
+
+ config.regmap = tps->rmap;
+ config.dev = dev;
+ config.driver_data = tps;
+
+ tps->rdev[id] = devm_regulator_register(dev,
+ tps->rdesc[id], &config);
+ if (IS_ERR(tps->rdev[id])) {
+ ret = PTR_ERR(tps->rdev[id]);
+ dev_err(dev, "regulator %s register failed: %d\n",
+ tps->rdesc[id]->name, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct i2c_device_id tps65132_id[] = {
+ {.name = "tps65132",},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, tps65132_id);
+
+static struct i2c_driver tps65132_i2c_driver = {
+ .driver = {
+ .name = "tps65132",
+ },
+ .probe = tps65132_probe,
+ .id_table = tps65132_id,
+};
+
+module_i2c_driver(tps65132_i2c_driver);
+
+MODULE_DESCRIPTION("tps65132 regulator driver");
+MODULE_AUTHOR("Venkat Reddy Talla <vreddytalla@nvidia.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
vsel = 60;
else if ((min_uV > 1350000) && (min_uV <= 1500000))
vsel = 59;
- else if ((min_uV > 1300000) && (min_uV <= 1350000))
- vsel = 58;
else
return -EINVAL;
break;
--- /dev/null
+/*
+ * Driver for voltage controller regulators
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/sort.h>
+
+struct vctrl_voltage_range {
+ int min_uV;
+ int max_uV;
+};
+
+struct vctrl_voltage_ranges {
+ struct vctrl_voltage_range ctrl;
+ struct vctrl_voltage_range out;
+};
+
+struct vctrl_voltage_table {
+ int ctrl;
+ int out;
+ int ovp_min_sel;
+};
+
+struct vctrl_data {
+ struct regulator_dev *rdev;
+ struct regulator_desc desc;
+ struct regulator *ctrl_reg;
+ bool enabled;
+ unsigned int min_slew_down_rate;
+ unsigned int ovp_threshold;
+ struct vctrl_voltage_ranges vrange;
+ struct vctrl_voltage_table *vtable;
+ unsigned int sel;
+};
+
+static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV)
+{
+ struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+ struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+ return ctrl->min_uV +
+ DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) *
+ (ctrl->max_uV - ctrl->min_uV),
+ out->max_uV - out->min_uV);
+}
+
+static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)
+{
+ struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+ struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+ if (ctrl_uV < 0) {
+ pr_err("vctrl: failed to get control voltage\n");
+ return ctrl_uV;
+ }
+
+ if (ctrl_uV < ctrl->min_uV)
+ return out->min_uV;
+
+ if (ctrl_uV > ctrl->max_uV)
+ return out->max_uV;
+
+ return out->min_uV +
+ DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) *
+ (out->max_uV - out->min_uV),
+ ctrl->max_uV - ctrl->min_uV);
+}
+
+static int vctrl_get_voltage(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+
+ return vctrl_calc_output_voltage(vctrl, ctrl_uV);
+}
+
+static int vctrl_set_voltage(struct regulator_dev *rdev,
+ int req_min_uV, int req_max_uV,
+ unsigned int *selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ int orig_ctrl_uV = regulator_get_voltage(ctrl_reg);
+ int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
+ int ret;
+
+ if (req_min_uV >= uV || !vctrl->ovp_threshold)
+ /* voltage rising or no OVP */
+ return regulator_set_voltage(
+ ctrl_reg,
+ vctrl_calc_ctrl_voltage(vctrl, req_min_uV),
+ vctrl_calc_ctrl_voltage(vctrl, req_max_uV));
+
+ while (uV > req_min_uV) {
+ int max_drop_uV = (uV * vctrl->ovp_threshold) / 100;
+ int next_uV;
+ int next_ctrl_uV;
+ int delay;
+
+ /* Make sure no infinite loop even in crazy cases */
+ if (max_drop_uV == 0)
+ max_drop_uV = 1;
+
+ next_uV = max_t(int, req_min_uV, uV - max_drop_uV);
+ next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);
+
+ ret = regulator_set_voltage(ctrl_reg,
+ next_ctrl_uV,
+ next_ctrl_uV);
+ if (ret)
+ goto err;
+
+ delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate);
+ usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+
+ uV = next_uV;
+ }
+
+ return 0;
+
+err:
+ /* Try to go back to original voltage */
+ regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV);
+
+ return ret;
+}
+
+static int vctrl_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ return vctrl->sel;
+}
+
+static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ unsigned int orig_sel = vctrl->sel;
+ int ret;
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ if (selector >= vctrl->sel || !vctrl->ovp_threshold) {
+ /* voltage rising or no OVP */
+ ret = regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[selector].ctrl,
+ vctrl->vtable[selector].ctrl);
+ if (!ret)
+ vctrl->sel = selector;
+
+ return ret;
+ }
+
+ while (vctrl->sel != selector) {
+ unsigned int next_sel;
+ int delay;
+
+ if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel)
+ next_sel = selector;
+ else
+ next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
+
+ ret = regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[next_sel].ctrl,
+ vctrl->vtable[next_sel].ctrl);
+ if (ret) {
+ dev_err(&rdev->dev,
+ "failed to set control voltage to %duV\n",
+ vctrl->vtable[next_sel].ctrl);
+ goto err;
+ }
+ vctrl->sel = next_sel;
+
+ delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out -
+ vctrl->vtable[next_sel].out,
+ vctrl->min_slew_down_rate);
+ usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+ }
+
+ return 0;
+
+err:
+ if (vctrl->sel != orig_sel) {
+ /* Try to go back to original voltage */
+ if (!regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[orig_sel].ctrl,
+ vctrl->vtable[orig_sel].ctrl))
+ vctrl->sel = orig_sel;
+ else
+ dev_warn(&rdev->dev,
+ "failed to restore original voltage\n");
+ }
+
+ return ret;
+}
+
+static int vctrl_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ return vctrl->vtable[selector].out;
+}
+
+static int vctrl_parse_dt(struct platform_device *pdev,
+ struct vctrl_data *vctrl)
+{
+ int ret;
+ struct device_node *np = pdev->dev.of_node;
+ u32 pval;
+ u32 vrange_ctrl[2];
+
+ vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");
+ if (IS_ERR(vctrl->ctrl_reg))
+ return PTR_ERR(vctrl->ctrl_reg);
+
+ ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);
+ if (!ret) {
+ vctrl->ovp_threshold = pval;
+ if (vctrl->ovp_threshold > 100) {
+ dev_err(&pdev->dev,
+ "ovp-threshold-percent (%u) > 100\n",
+ vctrl->ovp_threshold);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32(np, "min-slew-down-rate", &pval);
+ if (!ret) {
+ vctrl->min_slew_down_rate = pval;
+
+ /* We use the value as int and as divider; sanity check */
+ if (vctrl->min_slew_down_rate == 0) {
+ dev_err(&pdev->dev,
+ "min-slew-down-rate must not be 0\n");
+ return -EINVAL;
+ } else if (vctrl->min_slew_down_rate > INT_MAX) {
+ dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n",
+ vctrl->min_slew_down_rate);
+ return -EINVAL;
+ }
+ }
+
+ if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) {
+ dev_err(&pdev->dev,
+ "ovp-threshold-percent requires min-slew-down-rate\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "regulator-min-microvolt", &pval);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to read regulator-min-microvolt: %d\n", ret);
+ return ret;
+ }
+ vctrl->vrange.out.min_uV = pval;
+
+ ret = of_property_read_u32(np, "regulator-max-microvolt", &pval);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to read regulator-max-microvolt: %d\n", ret);
+ return ret;
+ }
+ vctrl->vrange.out.max_uV = pval;
+
+ ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl,
+ 2);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (vrange_ctrl[0] >= vrange_ctrl[1]) {
+ dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n",
+ vrange_ctrl[0], vrange_ctrl[1]);
+ return -EINVAL;
+ }
+
+ vctrl->vrange.ctrl.min_uV = vrange_ctrl[0];
+ vctrl->vrange.ctrl.max_uV = vrange_ctrl[1];
+
+ return 0;
+}
+
+static int vctrl_cmp_ctrl_uV(const void *a, const void *b)
+{
+ const struct vctrl_voltage_table *at = a;
+ const struct vctrl_voltage_table *bt = b;
+
+ return at->ctrl - bt->ctrl;
+}
+
+static int vctrl_init_vtable(struct platform_device *pdev)
+{
+ struct vctrl_data *vctrl = platform_get_drvdata(pdev);
+ struct regulator_desc *rdesc = &vctrl->desc;
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;
+ int n_voltages;
+ int ctrl_uV;
+ int i, idx_vt;
+
+ n_voltages = regulator_count_voltages(ctrl_reg);
+
+ rdesc->n_voltages = n_voltages;
+
+ /* determine number of steps within the range of the vctrl regulator */
+ for (i = 0; i < n_voltages; i++) {
+ ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+ if (ctrl_uV < vrange_ctrl->min_uV ||
+ ctrl_uV > vrange_ctrl->max_uV) {
+ rdesc->n_voltages--;
+ continue;
+ }
+ }
+
+ if (rdesc->n_voltages == 0) {
+ dev_err(&pdev->dev, "invalid configuration\n");
+ return -EINVAL;
+ }
+
+ vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages,
+ sizeof(struct vctrl_voltage_table),
+ GFP_KERNEL);
+ if (!vctrl->vtable)
+ return -ENOMEM;
+
+ /* create mapping control <=> output voltage */
+ for (i = 0, idx_vt = 0; i < n_voltages; i++) {
+ ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+ if (ctrl_uV < vrange_ctrl->min_uV ||
+ ctrl_uV > vrange_ctrl->max_uV)
+ continue;
+
+ vctrl->vtable[idx_vt].ctrl = ctrl_uV;
+ vctrl->vtable[idx_vt].out =
+ vctrl_calc_output_voltage(vctrl, ctrl_uV);
+ idx_vt++;
+ }
+
+ /* we rely on the table to be ordered by ascending voltage */
+ sort(vctrl->vtable, rdesc->n_voltages,
+ sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV,
+ NULL);
+
+ /* pre-calculate OVP-safe downward transitions */
+ for (i = rdesc->n_voltages - 1; i > 0; i--) {
+ int j;
+ int ovp_min_uV = (vctrl->vtable[i].out *
+ (100 - vctrl->ovp_threshold)) / 100;
+
+ for (j = 0; j < i; j++) {
+ if (vctrl->vtable[j].out >= ovp_min_uV) {
+ vctrl->vtable[i].ovp_min_sel = j;
+ break;
+ }
+ }
+
+ if (j == i) {
+ dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n",
+ vctrl->vtable[i].out);
+ /* use next lowest voltage */
+ vctrl->vtable[i].ovp_min_sel = i - 1;
+ }
+ }
+
+ return 0;
+}
+
+static int vctrl_enable(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ret = regulator_enable(vctrl->ctrl_reg);
+
+ if (!ret)
+ vctrl->enabled = true;
+
+ return ret;
+}
+
+static int vctrl_disable(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ret = regulator_disable(vctrl->ctrl_reg);
+
+ if (!ret)
+ vctrl->enabled = false;
+
+ return ret;
+}
+
+static int vctrl_is_enabled(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ return vctrl->enabled;
+}
+
+static const struct regulator_ops vctrl_ops_cont = {
+ .enable = vctrl_enable,
+ .disable = vctrl_disable,
+ .is_enabled = vctrl_is_enabled,
+ .get_voltage = vctrl_get_voltage,
+ .set_voltage = vctrl_set_voltage,
+};
+
+static const struct regulator_ops vctrl_ops_non_cont = {
+ .enable = vctrl_enable,
+ .disable = vctrl_disable,
+ .is_enabled = vctrl_is_enabled,
+ .set_voltage_sel = vctrl_set_voltage_sel,
+ .get_voltage_sel = vctrl_get_voltage_sel,
+ .list_voltage = vctrl_list_voltage,
+ .map_voltage = regulator_map_voltage_iterate,
+};
+
+static int vctrl_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct vctrl_data *vctrl;
+ const struct regulator_init_data *init_data;
+ struct regulator_desc *rdesc;
+ struct regulator_config cfg = { };
+ struct vctrl_voltage_range *vrange_ctrl;
+ int ctrl_uV;
+ int ret;
+
+ vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data),
+ GFP_KERNEL);
+ if (!vctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vctrl);
+
+ ret = vctrl_parse_dt(pdev, vctrl);
+ if (ret)
+ return ret;
+
+ vrange_ctrl = &vctrl->vrange.ctrl;
+
+ rdesc = &vctrl->desc;
+ rdesc->name = "vctrl";
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->owner = THIS_MODULE;
+
+ if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) ||
+ (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) {
+ rdesc->continuous_voltage_range = true;
+ rdesc->ops = &vctrl_ops_cont;
+ } else {
+ rdesc->ops = &vctrl_ops_non_cont;
+ }
+
+ init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc);
+ if (!init_data)
+ return -ENOMEM;
+
+ cfg.of_node = np;
+ cfg.dev = &pdev->dev;
+ cfg.driver_data = vctrl;
+ cfg.init_data = init_data;
+
+ if (!rdesc->continuous_voltage_range) {
+ ret = vctrl_init_vtable(pdev);
+ if (ret)
+ return ret;
+
+ ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+ if (ctrl_uV < 0) {
+ dev_err(&pdev->dev, "failed to get control voltage\n");
+ return ctrl_uV;
+ }
+
+ /* determine current voltage selector from control voltage */
+ if (ctrl_uV < vrange_ctrl->min_uV) {
+ vctrl->sel = 0;
+ } else if (ctrl_uV > vrange_ctrl->max_uV) {
+ vctrl->sel = rdesc->n_voltages - 1;
+ } else {
+ int i;
+
+ for (i = 0; i < rdesc->n_voltages; i++) {
+ if (ctrl_uV == vctrl->vtable[i].ctrl) {
+ vctrl->sel = i;
+ break;
+ }
+ }
+ }
+ }
+
+ vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);
+ if (IS_ERR(vctrl->rdev)) {
+ ret = PTR_ERR(vctrl->rdev);
+ dev_err(&pdev->dev, "failed to register regulator: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id vctrl_of_match[] = {
+ { .compatible = "vctrl-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vctrl_of_match);
+
+static struct platform_driver vctrl_driver = {
+ .probe = vctrl_probe,
+ .driver = {
+ .name = "vctrl-regulator",
+ .of_match_table = of_match_ptr(vctrl_of_match),
+ },
+};
+
+module_platform_driver(vctrl_driver);
+
+MODULE_DESCRIPTION("Voltage Controlled Regulator Driver");
+MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
+MODULE_LICENSE("GPL v2");
#define _ARIZONA_PDATA_H
#include <dt-bindings/mfd/arizona.h>
+#include <linux/regulator/arizona-ldo1.h>
+#include <linux/regulator/arizona-micsupp.h>
#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */
#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */
struct arizona_pdata {
int reset; /** GPIO controlling /RESET, if any */
- int ldoena; /** GPIO controlling LODENA, if any */
/** Regulator configuration for MICVDD */
- struct regulator_init_data *micvdd;
+ struct arizona_micsupp_pdata micvdd;
/** Regulator configuration for LDO1 */
- struct regulator_init_data *ldo1;
+ struct arizona_ldo1_pdata ldo1;
/** If a direct 32kHz clock is provided on an MCLK specify it here */
int clk32k_src;
struct regulator_init_data;
struct isl9305_pdata {
- struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR];
+ struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR + 1];
};
#endif
--- /dev/null
+/*
+ * Platform data for Arizona LDO1 regulator
+ *
+ * Copyright 2017 Cirrus Logic
+ *
+ * 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.
+ */
+
+#ifndef ARIZONA_LDO1_H
+#define ARIZONA_LDO1_H
+
+struct regulator_init_data;
+
+struct arizona_ldo1_pdata {
+ /** GPIO controlling LDOENA, if any */
+ int ldoena;
+
+ /** Regulator configuration for LDO1 */
+ const struct regulator_init_data *init_data;
+};
+
+#endif
--- /dev/null
+/*
+ * Platform data for Arizona micsupp regulator
+ *
+ * Copyright 2017 Cirrus Logic
+ *
+ * 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.
+ */
+
+#ifndef ARIZONA_MICSUPP_H
+#define ARIZONA_MICSUPP_H
+
+struct regulator_init_data;
+
+struct arizona_micsupp_pdata {
+ /** Regulator configuration for micsupp */
+ const struct regulator_init_data *init_data;
+};
+
+#endif
#define REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE 0x200
#define REGULATOR_EVENT_PRE_DISABLE 0x400
#define REGULATOR_EVENT_ABORT_DISABLE 0x800
+#define REGULATOR_EVENT_ENABLE 0x1000
/*
* Regulator errors that can be queried using regulator_get_error_flags
* set_active_discharge
* @active_discharge_reg: Register for control when using regmap
* set_active_discharge
+ * @soft_start_reg: Register for control when using regmap set_soft_start
+ * @soft_start_mask: Mask for control when using regmap set_soft_start
+ * @soft_start_val_on: Enabling value for control when using regmap
+ * set_soft_start
+ * @pull_down_reg: Register for control when using regmap set_pull_down
+ * @pull_down_mask: Mask for control when using regmap set_pull_down
+ * @pull_down_val_on: Enabling value for control when using regmap
+ * set_pull_down
*
* @enable_time: Time taken for initial enable of regulator (in uS).
* @off_on_delay: guard time (in uS), before re-enabling a regulator
unsigned int active_discharge_off;
unsigned int active_discharge_mask;
unsigned int active_discharge_reg;
+ unsigned int soft_start_reg;
+ unsigned int soft_start_mask;
+ unsigned int soft_start_val_on;
+ unsigned int pull_down_reg;
+ unsigned int pull_down_mask;
+ unsigned int pull_down_val_on;
unsigned int enable_time;
struct regulator_enable_gpio *ena_pin;
unsigned int ena_gpio_state:1;
+ unsigned int is_switch:1;
+
/* time when this regulator was disabled last time */
unsigned long last_off_jiffy;
};
unsigned int new_selector);
int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable);
int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable);
+int regulator_set_soft_start_regmap(struct regulator_dev *rdev);
+int regulator_set_pull_down_regmap(struct regulator_dev *rdev);
int regulator_set_active_discharge_regmap(struct regulator_dev *rdev,
bool enable);
* @initial_state: Suspend state to set by default.
* @initial_mode: Mode to set at startup.
* @ramp_delay: Time to settle down after voltage change (unit: uV/us)
+ * @settling_time: Time to settle down after voltage change when voltage
+ * change is non-linear (unit: microseconds).
* @active_discharge: Enable/disable active discharge. The enum
* regulator_active_discharge values are used for
* initialisation.
unsigned int initial_mode;
unsigned int ramp_delay;
+ unsigned int settling_time;
unsigned int enable_time;
unsigned int active_discharge;
#define PFUZE200_VGEN4 10
#define PFUZE200_VGEN5 11
#define PFUZE200_VGEN6 12
+#define PFUZE200_COIN 13
#define PFUZE3000_SW1A 0
#define PFUZE3000_SW1B 1