diff options
-rw-r--r-- | Documentation/devicetree/bindings/mfd/bcm590xx.txt | 4 | ||||
-rw-r--r-- | drivers/mfd/arizona-core.c | 41 | ||||
-rw-r--r-- | drivers/mfd/bcm590xx.c | 60 | ||||
-rw-r--r-- | drivers/mfd/tps65090.c | 41 | ||||
-rw-r--r-- | drivers/power/tps65090-charger.c | 11 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 7 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/arizona-ldo1.c | 58 | ||||
-rw-r--r-- | drivers/regulator/arizona-micsupp.c | 38 | ||||
-rw-r--r-- | drivers/regulator/axp20x-regulator.c | 286 | ||||
-rw-r--r-- | drivers/regulator/bcm590xx-regulator.c | 92 | ||||
-rw-r--r-- | include/linux/mfd/arizona/core.h | 3 | ||||
-rw-r--r-- | include/linux/mfd/bcm590xx.h | 9 | ||||
-rw-r--r-- | include/linux/mfd/tps65090.h | 14 |
14 files changed, 593 insertions, 72 deletions
diff --git a/Documentation/devicetree/bindings/mfd/bcm590xx.txt b/Documentation/devicetree/bindings/mfd/bcm590xx.txt index 1fe30e2b10da..be51a15e05f9 100644 --- a/Documentation/devicetree/bindings/mfd/bcm590xx.txt +++ b/Documentation/devicetree/bindings/mfd/bcm590xx.txt @@ -19,7 +19,9 @@ Optional child nodes: The valid regulator node names for BCM59056 are: rfldo, camldo1, camldo2, simldo1, simldo2, sdldo, sdxldo, mmcldo1, mmcldo2, audldo, micldo, usbldo, vibldo, - csr, iosr1, iosr2, msr, sdsr1, sdsr2, vsr + csr, iosr1, iosr2, msr, sdsr1, sdsr2, vsr, + gpldo1, gpldo2, gpldo3, gpldo4, gpldo5, gpldo6, + vbus Example: pmu: bcm59056@8 { diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1c3ae57082ed..07e6e27be23c 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -508,19 +508,31 @@ int arizona_of_get_type(struct device *dev) } EXPORT_SYMBOL_GPL(arizona_of_get_type); +int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, + bool mandatory) +{ + int gpio; + + gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0); + if (gpio < 0) { + if (mandatory) + dev_err(arizona->dev, + "Mandatory DT gpio %s missing/malformed: %d\n", + prop, gpio); + + gpio = 0; + } + + return gpio; +} +EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio); + static int arizona_of_get_core_pdata(struct arizona *arizona) { + struct arizona_pdata *pdata = &arizona->pdata; int ret, i; - arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node, - "wlf,reset", 0); - if (arizona->pdata.reset < 0) - arizona->pdata.reset = 0; - - arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node, - "wlf,ldoena", 0); - if (arizona->pdata.ldoena < 0) - arizona->pdata.ldoena = 0; + pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true); ret = of_property_read_u32_array(arizona->dev->of_node, "wlf,gpio-defaults", @@ -652,6 +664,9 @@ int arizona_dev_init(struct arizona *arizona) return -EINVAL; } + /* Mark DCVDD as external, LDO1 driver will clear if internal */ + arizona->external_dcvdd = true; + ret = mfd_add_devices(arizona->dev, -1, early_devs, ARRAY_SIZE(early_devs), NULL, 0, NULL); if (ret != 0) { @@ -851,14 +866,6 @@ int arizona_dev_init(struct arizona *arizona) arizona->pdata.gpio_defaults[i]); } - /* - * LDO1 can only be used to supply DCVDD so if it has no - * consumers then DCVDD is supplied externally. - */ - if (arizona->pdata.ldo1 && - arizona->pdata.ldo1->num_consumer_supplies == 0) - arizona->external_dcvdd = true; - pm_runtime_set_autosuspend_delay(arizona->dev, 100); pm_runtime_use_autosuspend(arizona->dev); pm_runtime_enable(arizona->dev); diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c index e9a33c79431b..43cba1a1973c 100644 --- a/drivers/mfd/bcm590xx.c +++ b/drivers/mfd/bcm590xx.c @@ -28,39 +28,71 @@ static const struct mfd_cell bcm590xx_devs[] = { }, }; -static const struct regmap_config bcm590xx_regmap_config = { +static const struct regmap_config bcm590xx_regmap_config_pri = { .reg_bits = 8, .val_bits = 8, - .max_register = BCM590XX_MAX_REGISTER, + .max_register = BCM590XX_MAX_REGISTER_PRI, .cache_type = REGCACHE_RBTREE, }; -static int bcm590xx_i2c_probe(struct i2c_client *i2c, +static const struct regmap_config bcm590xx_regmap_config_sec = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BCM590XX_MAX_REGISTER_SEC, + .cache_type = REGCACHE_RBTREE, +}; + +static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri, const struct i2c_device_id *id) { struct bcm590xx *bcm590xx; int ret; - bcm590xx = devm_kzalloc(&i2c->dev, sizeof(*bcm590xx), GFP_KERNEL); + bcm590xx = devm_kzalloc(&i2c_pri->dev, sizeof(*bcm590xx), GFP_KERNEL); if (!bcm590xx) return -ENOMEM; - i2c_set_clientdata(i2c, bcm590xx); - bcm590xx->dev = &i2c->dev; - bcm590xx->i2c_client = i2c; + i2c_set_clientdata(i2c_pri, bcm590xx); + bcm590xx->dev = &i2c_pri->dev; + bcm590xx->i2c_pri = i2c_pri; - bcm590xx->regmap = devm_regmap_init_i2c(i2c, &bcm590xx_regmap_config); - if (IS_ERR(bcm590xx->regmap)) { - ret = PTR_ERR(bcm590xx->regmap); - dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); + bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri, + &bcm590xx_regmap_config_pri); + if (IS_ERR(bcm590xx->regmap_pri)) { + ret = PTR_ERR(bcm590xx->regmap_pri); + dev_err(&i2c_pri->dev, "primary regmap init failed: %d\n", ret); return ret; } - ret = mfd_add_devices(&i2c->dev, -1, bcm590xx_devs, + /* Secondary I2C slave address is the base address with A(2) asserted */ + bcm590xx->i2c_sec = i2c_new_dummy(i2c_pri->adapter, + i2c_pri->addr | BIT(2)); + if (IS_ERR_OR_NULL(bcm590xx->i2c_sec)) { + dev_err(&i2c_pri->dev, "failed to add secondary I2C device\n"); + return -ENODEV; + } + i2c_set_clientdata(bcm590xx->i2c_sec, bcm590xx); + + bcm590xx->regmap_sec = devm_regmap_init_i2c(bcm590xx->i2c_sec, + &bcm590xx_regmap_config_sec); + if (IS_ERR(bcm590xx->regmap_sec)) { + ret = PTR_ERR(bcm590xx->regmap_sec); + dev_err(&bcm590xx->i2c_sec->dev, + "secondary regmap init failed: %d\n", ret); + goto err; + } + + ret = mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs, ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL); - if (ret < 0) - dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret); + if (ret < 0) { + dev_err(&i2c_pri->dev, "failed to add sub-devices: %d\n", ret); + goto err; + } + + return 0; +err: + i2c_unregister_device(bcm590xx->i2c_sec); return ret; } diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index ba1a25d758c1..1c3e6e2efe41 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -32,14 +32,6 @@ #define NUM_INT_REG 2 #define TOTAL_NUM_REG 0x18 -/* interrupt status registers */ -#define TPS65090_INT_STS 0x0 -#define TPS65090_INT_STS2 0x1 - -/* interrupt mask registers */ -#define TPS65090_INT_MSK 0x2 -#define TPS65090_INT_MSK2 0x3 - #define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1 #define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2 #define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3 @@ -64,11 +56,16 @@ static struct resource charger_resources[] = { } }; -static const struct mfd_cell tps65090s[] = { - { +enum tps65090_cells { + PMIC = 0, + CHARGER = 1, +}; + +static struct mfd_cell tps65090s[] = { + [PMIC] = { .name = "tps65090-pmic", }, - { + [CHARGER] = { .name = "tps65090-charger", .num_resources = ARRAY_SIZE(charger_resources), .resources = &charger_resources[0], @@ -139,17 +136,26 @@ static struct regmap_irq_chip tps65090_irq_chip = { .irqs = tps65090_irqs, .num_irqs = ARRAY_SIZE(tps65090_irqs), .num_regs = NUM_INT_REG, - .status_base = TPS65090_INT_STS, - .mask_base = TPS65090_INT_MSK, + .status_base = TPS65090_REG_INTR_STS, + .mask_base = TPS65090_REG_INTR_MASK, .mask_invert = true, }; static bool is_volatile_reg(struct device *dev, unsigned int reg) { - if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2)) - return true; - else + /* Nearly all registers have status bits mixed in, except a few */ + switch (reg) { + case TPS65090_REG_INTR_MASK: + case TPS65090_REG_INTR_MASK2: + case TPS65090_REG_CG_CTRL0: + case TPS65090_REG_CG_CTRL1: + case TPS65090_REG_CG_CTRL2: + case TPS65090_REG_CG_CTRL3: + case TPS65090_REG_CG_CTRL4: + case TPS65090_REG_CG_CTRL5: return false; + } + return true; } static const struct regmap_config tps65090_regmap_config = { @@ -211,6 +217,9 @@ static int tps65090_i2c_probe(struct i2c_client *client, "IRQ init failed with err: %d\n", ret); return ret; } + } else { + /* Don't tell children they have an IRQ that'll never fire */ + tps65090s[CHARGER].num_resources = 0; } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 8fc9d6df87f6..1685f63b9e5d 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -28,17 +28,6 @@ #include <linux/mfd/tps65090.h> -#define TPS65090_REG_INTR_STS 0x00 -#define TPS65090_REG_INTR_MASK 0x02 -#define TPS65090_REG_CG_CTRL0 0x04 -#define TPS65090_REG_CG_CTRL1 0x05 -#define TPS65090_REG_CG_CTRL2 0x06 -#define TPS65090_REG_CG_CTRL3 0x07 -#define TPS65090_REG_CG_CTRL4 0x08 -#define TPS65090_REG_CG_CTRL5 0x09 -#define TPS65090_REG_CG_STATUS1 0x0a -#define TPS65090_REG_CG_STATUS2 0x0b - #define TPS65090_CHARGER_ENABLE BIT(0) #define TPS65090_VACG BIT(1) #define TPS65090_NOITERM BIT(5) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 903eb37f047a..65e5d7d1b35a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -139,6 +139,13 @@ config REGULATOR_AS3722 AS3722 PMIC. This will enable support for all the software controllable DCDC/LDO regulators. +config REGULATOR_AXP20X + tristate "X-POWERS AXP20X PMIC Regulators" + depends on MFD_AXP20X + help + This driver provides support for the voltage regulators on the + AXP20X PMIC. + config REGULATOR_BCM590XX tristate "Broadcom BCM590xx PMU Regulators" depends on MFD_BCM590XX diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 12ef277a48b4..c14696b290c0 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.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_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index b1033d30b504..04f262a836b2 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -16,9 +16,11 @@ #include <linux/init.h> #include <linux/bitops.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> #include <linux/gpio.h> #include <linux/slab.h> @@ -178,6 +180,42 @@ static const struct regulator_init_data arizona_ldo1_default = { .num_consumer_supplies = 1, }; +static int arizona_ldo1_of_get_pdata(struct arizona *arizona, + struct regulator_config *config) +{ + struct arizona_pdata *pdata = &arizona->pdata; + struct arizona_ldo1 *ldo1 = config->driver_data; + struct device_node *init_node, *dcvdd_node; + struct regulator_init_data *init_data; + + pdata->ldoena = arizona_of_get_named_gpio(arizona, "wlf,ldoena", true); + + init_node = of_get_child_by_name(arizona->dev->of_node, "ldo1"); + dcvdd_node = of_parse_phandle(arizona->dev->of_node, "DCVDD-supply", 0); + + if (init_node) { + config->of_node = init_node; + + init_data = of_get_regulator_init_data(arizona->dev, init_node); + + 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; + + pdata->ldo1 = init_data; + } + } else if (dcvdd_node) { + arizona->external_dcvdd = true; + } + + of_node_put(dcvdd_node); + + return 0; +} + static int arizona_ldo1_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -186,6 +224,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) struct arizona_ldo1 *ldo1; int ret; + arizona->external_dcvdd = false; + ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); if (!ldo1) return -ENOMEM; @@ -216,6 +256,15 @@ static int arizona_ldo1_probe(struct platform_device *pdev) config.dev = arizona->dev; config.driver_data = ldo1; config.regmap = arizona->regmap; + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_ldo1_of_get_pdata(arizona, &config); + if (ret < 0) + return ret; + } + } + config.ena_gpio = arizona->pdata.ldoena; if (arizona->pdata.ldo1) @@ -223,6 +272,13 @@ static int arizona_ldo1_probe(struct platform_device *pdev) else config.init_data = &ldo1->init_data; + /* + * LDO1 can only be used to supply DCVDD so if it has no + * consumers then DCVDD is supplied externally. + */ + if (config.init_data->num_consumer_supplies == 0) + arizona->external_dcvdd = true; + ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(ldo1->regulator)) { ret = PTR_ERR(ldo1->regulator); @@ -231,6 +287,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) return ret; } + of_node_put(config.of_node); + platform_set_drvdata(pdev, ldo1); return 0; diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 6fdd9bf6927f..ce9aca5f8ee7 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -16,9 +16,11 @@ #include <linux/init.h> #include <linux/bitops.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> #include <linux/gpio.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -195,6 +197,32 @@ static const struct regulator_init_data arizona_micsupp_ext_default = { .num_consumer_supplies = 1, }; +static int arizona_micsupp_of_get_pdata(struct arizona *arizona, + struct regulator_config *config) +{ + 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"); + + if (np) { + config->of_node = np; + + init_data = of_get_regulator_init_data(arizona->dev, np); + + if (init_data) { + init_data->consumer_supplies = &micsupp->supply; + init_data->num_consumer_supplies = 1; + + pdata->micvdd = init_data; + } + } + + return 0; +} + static int arizona_micsupp_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -234,6 +262,14 @@ static int arizona_micsupp_probe(struct platform_device *pdev) config.driver_data = micsupp; config.regmap = arizona->regmap; + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_micsupp_of_get_pdata(arizona, &config); + if (ret < 0) + return ret; + } + } + if (arizona->pdata.micvdd) config.init_data = arizona->pdata.micvdd; else @@ -253,6 +289,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) return ret; } + of_node_put(config.of_node); + platform_set_drvdata(pdev, micsupp); return 0; diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c new file mode 100644 index 000000000000..004aadb7bcc1 --- /dev/null +++ b/drivers/regulator/axp20x-regulator.c @@ -0,0 +1,286 @@ +/* + * AXP20x regulators driver. + * + * Copyright (C) 2013 Carlo Caione <carlo@caione.org> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/axp20x.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> + +#define AXP20X_IO_ENABLED 0x03 +#define AXP20X_IO_DISABLED 0x07 + +#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) +#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) + +#define AXP20X_FREQ_DCDC_MASK 0x0f + +#define AXP20X_DESC_IO(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ + _emask, _enable_val, _disable_val) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .owner = THIS_MODULE, \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enable_val), \ + .disable_val = (_disable_val), \ + .ops = &axp20x_ops, \ + } + +#define AXP20X_DESC(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ + _emask) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .owner = THIS_MODULE, \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .ops = &axp20x_ops, \ + } + +#define AXP20X_DESC_FIXED(_id, _supply, _volt) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = 1, \ + .owner = THIS_MODULE, \ + .min_uV = (_volt) * 1000, \ + .ops = &axp20x_ops_fixed \ + } + +#define AXP20X_DESC_TABLE(_id, _supply, _table, _vreg, _vmask, _ereg, _emask) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = ARRAY_SIZE(_table), \ + .owner = THIS_MODULE, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .volt_table = (_table), \ + .ops = &axp20x_ops_table, \ + } + +static const int axp20x_ldo4_data[] = { 1250000, 1300000, 1400000, 1500000, 1600000, + 1700000, 1800000, 1900000, 2000000, 2500000, + 2700000, 2800000, 3000000, 3100000, 3200000, + 3300000 }; + +static struct regulator_ops axp20x_ops_fixed = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops axp20x_ops_table = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops axp20x_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc axp20x_regulators[] = { + AXP20X_DESC(DCDC2, "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f, + AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DESC(DCDC3, "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT, 0x7f, + AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DESC_FIXED(LDO1, "acin", 1300), + AXP20X_DESC(LDO2, "ldo24in", 1800, 3300, 100, AXP20X_LDO24_V_OUT, 0xf0, + AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_DESC(LDO3, "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f, + AXP20X_PWR_OUT_CTRL, 0x40), + AXP20X_DESC_TABLE(LDO4, "ldo24in", axp20x_ldo4_data, AXP20X_LDO24_V_OUT, 0x0f, + AXP20X_PWR_OUT_CTRL, 0x08), + AXP20X_DESC_IO(LDO5, "ldo5in", 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0, + AXP20X_GPIO0_CTRL, 0x07, AXP20X_IO_ENABLED, + AXP20X_IO_DISABLED), +}; + +#define AXP_MATCH(_name, _id) \ + [AXP20X_##_id] = { \ + .name = #_name, \ + .driver_data = (void *) &axp20x_regulators[AXP20X_##_id], \ + } + +static struct of_regulator_match axp20x_matches[] = { + AXP_MATCH(dcdc2, DCDC2), + AXP_MATCH(dcdc3, DCDC3), + AXP_MATCH(ldo1, LDO1), + AXP_MATCH(ldo2, LDO2), + AXP_MATCH(ldo3, LDO3), + AXP_MATCH(ldo4, LDO4), + AXP_MATCH(ldo5, LDO5), +}; + +static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + if (dcdcfreq < 750) { + dcdcfreq = 750; + dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n"); + } + + if (dcdcfreq > 1875) { + dcdcfreq = 1875; + dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n"); + } + + dcdcfreq = (dcdcfreq - 750) / 75; + + return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ, + AXP20X_FREQ_DCDC_MASK, dcdcfreq); +} + +static int axp20x_regulator_parse_dt(struct platform_device *pdev) +{ + struct device_node *np, *regulators; + int ret; + u32 dcdcfreq; + + np = of_node_get(pdev->dev.parent->of_node); + if (!np) + return 0; + + regulators = of_get_child_by_name(np, "regulators"); + if (!regulators) { + dev_warn(&pdev->dev, "regulators node not found\n"); + } else { + ret = of_regulator_match(&pdev->dev, regulators, axp20x_matches, + ARRAY_SIZE(axp20x_matches)); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); + return ret; + } + + dcdcfreq = 1500; + of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq); + ret = axp20x_set_dcdc_freq(pdev, dcdcfreq); + if (ret < 0) { + dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret); + return ret; + } + + of_node_put(regulators); + } + + return 0; +} + +static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode) +{ + unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK; + + if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3)) + return -EINVAL; + + if (id == AXP20X_DCDC3) + mask = AXP20X_WORKMODE_DCDC3_MASK; + + workmode <<= ffs(mask) - 1; + + return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode); +} + +static int axp20x_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct regulator_init_data *init_data; + int ret, i; + u32 workmode; + + ret = axp20x_regulator_parse_dt(pdev); + if (ret) + return ret; + + for (i = 0; i < AXP20X_REG_ID_MAX; i++) { + init_data = axp20x_matches[i].init_data; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.regmap = axp20x->regmap; + config.of_node = axp20x_matches[i].of_node; + + rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i], + &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register %s\n", + axp20x_regulators[i].name); + + return PTR_ERR(rdev); + } + + ret = of_property_read_u32(axp20x_matches[i].of_node, "x-powers,dcdc-workmode", + &workmode); + if (!ret) { + if (axp20x_set_dcdc_workmode(rdev, i, workmode)) + dev_err(&pdev->dev, "Failed to set workmode on %s\n", + axp20x_regulators[i].name); + } + } + + return 0; +} + +static struct platform_driver axp20x_regulator_driver = { + .probe = axp20x_regulator_probe, + .driver = { + .name = "axp20x-regulator", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(axp20x_regulator_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); +MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC"); diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c index c3750c5b382b..57544e254a78 100644 --- a/drivers/regulator/bcm590xx-regulator.c +++ b/drivers/regulator/bcm590xx-regulator.c @@ -22,7 +22,7 @@ #include <linux/regulator/of_regulator.h> #include <linux/slab.h> -/* Register defs */ +/* I2C slave 0 registers */ #define BCM590XX_RFLDOPMCTRL1 0x60 #define BCM590XX_IOSR1PMCTRL1 0x7a #define BCM590XX_IOSR2PMCTRL1 0x7c @@ -31,13 +31,34 @@ #define BCM590XX_SDSR2PMCTRL1 0x86 #define BCM590XX_MSRPMCTRL1 0x8a #define BCM590XX_VSRPMCTRL1 0x8e -#define BCM590XX_REG_ENABLE BIT(7) - #define BCM590XX_RFLDOCTRL 0x96 #define BCM590XX_CSRVOUT1 0xc0 + +/* I2C slave 1 registers */ +#define BCM590XX_GPLDO5PMCTRL1 0x16 +#define BCM590XX_GPLDO6PMCTRL1 0x18 +#define BCM590XX_GPLDO1CTRL 0x1a +#define BCM590XX_GPLDO2CTRL 0x1b +#define BCM590XX_GPLDO3CTRL 0x1c +#define BCM590XX_GPLDO4CTRL 0x1d +#define BCM590XX_GPLDO5CTRL 0x1e +#define BCM590XX_GPLDO6CTRL 0x1f +#define BCM590XX_OTG_CTRL 0x40 +#define BCM590XX_GPLDO1PMCTRL1 0x57 +#define BCM590XX_GPLDO2PMCTRL1 0x59 +#define BCM590XX_GPLDO3PMCTRL1 0x5b +#define BCM590XX_GPLDO4PMCTRL1 0x5d + +#define BCM590XX_REG_ENABLE BIT(7) +#define BCM590XX_VBUS_ENABLE BIT(2) #define BCM590XX_LDO_VSEL_MASK GENMASK(5, 3) #define BCM590XX_SR_VSEL_MASK GENMASK(5, 0) +/* + * RFLDO to VSR regulators are + * accessed via I2C slave 0 + */ + /* LDO regulator IDs */ #define BCM590XX_REG_RFLDO 0 #define BCM590XX_REG_CAMLDO1 1 @@ -62,9 +83,25 @@ #define BCM590XX_REG_SDSR2 18 #define BCM590XX_REG_VSR 19 -#define BCM590XX_NUM_REGS 20 +/* + * GPLDO1 to VBUS regulators are + * accessed via I2C slave 1 + */ + +#define BCM590XX_REG_GPLDO1 20 +#define BCM590XX_REG_GPLDO2 21 +#define BCM590XX_REG_GPLDO3 22 +#define BCM590XX_REG_GPLDO4 23 +#define BCM590XX_REG_GPLDO5 24 +#define BCM590XX_REG_GPLDO6 25 +#define BCM590XX_REG_VBUS 26 + +#define BCM590XX_NUM_REGS 27 #define BCM590XX_REG_IS_LDO(n) (n < BCM590XX_REG_CSR) +#define BCM590XX_REG_IS_GPLDO(n) \ + ((n > BCM590XX_REG_VSR) && (n < BCM590XX_REG_VBUS)) +#define BCM590XX_REG_IS_VBUS(n) (n == BCM590XX_REG_VBUS) struct bcm590xx_board { struct regulator_init_data *bcm590xx_pmu_init_data[BCM590XX_NUM_REGS]; @@ -149,6 +186,12 @@ static struct bcm590xx_info bcm590xx_regs[] = { BCM590XX_REG_RANGES(sdsr1, dcdc_sdsr1_ranges), BCM590XX_REG_RANGES(sdsr2, dcdc_iosr1_ranges), BCM590XX_REG_RANGES(vsr, dcdc_iosr1_ranges), + BCM590XX_REG_TABLE(gpldo1, ldo_a_table), + BCM590XX_REG_TABLE(gpldo2, ldo_a_table), + BCM590XX_REG_TABLE(gpldo3, ldo_a_table), + BCM590XX_REG_TABLE(gpldo4, ldo_a_table), + BCM590XX_REG_TABLE(gpldo5, ldo_a_table), + BCM590XX_REG_TABLE(gpldo6, ldo_a_table), }; struct bcm590xx_reg { @@ -161,6 +204,8 @@ static int bcm590xx_get_vsel_register(int id) { if (BCM590XX_REG_IS_LDO(id)) return BCM590XX_RFLDOCTRL + id; + else if (BCM590XX_REG_IS_GPLDO(id)) + return BCM590XX_GPLDO1CTRL + id; else return BCM590XX_CSRVOUT1 + (id - BCM590XX_REG_CSR) * 3; } @@ -171,6 +216,8 @@ static int bcm590xx_get_enable_register(int id) if (BCM590XX_REG_IS_LDO(id)) reg = BCM590XX_RFLDOPMCTRL1 + id * 2; + else if (BCM590XX_REG_IS_GPLDO(id)) + reg = BCM590XX_GPLDO1PMCTRL1 + id * 2; else switch (id) { case BCM590XX_REG_CSR: @@ -191,8 +238,11 @@ static int bcm590xx_get_enable_register(int id) case BCM590XX_REG_SDSR2: reg = BCM590XX_SDSR2PMCTRL1; break; + case BCM590XX_REG_VBUS: + reg = BCM590XX_OTG_CTRL; }; + return reg; } @@ -216,6 +266,12 @@ static struct regulator_ops bcm590xx_ops_dcdc = { .map_voltage = regulator_map_voltage_linear_range, }; +static struct regulator_ops bcm590xx_ops_vbus = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +}; + #define BCM590XX_MATCH(_name, _id) \ { \ .name = #_name, \ @@ -243,6 +299,13 @@ static struct of_regulator_match bcm590xx_matches[] = { BCM590XX_MATCH(sdsr1, SDSR1), BCM590XX_MATCH(sdsr2, SDSR2), BCM590XX_MATCH(vsr, VSR), + BCM590XX_MATCH(gpldo1, GPLDO1), + BCM590XX_MATCH(gpldo2, GPLDO2), + BCM590XX_MATCH(gpldo3, GPLDO3), + BCM590XX_MATCH(gpldo4, GPLDO4), + BCM590XX_MATCH(gpldo5, GPLDO5), + BCM590XX_MATCH(gpldo6, GPLDO6), + BCM590XX_MATCH(vbus, VBUS), }; static struct bcm590xx_board *bcm590xx_parse_dt_reg_data( @@ -353,17 +416,23 @@ static int bcm590xx_probe(struct platform_device *pdev) pmu->desc[i].linear_ranges = info->linear_ranges; pmu->desc[i].n_linear_ranges = info->n_linear_ranges; - if (BCM590XX_REG_IS_LDO(i)) { + if ((BCM590XX_REG_IS_LDO(i)) || (BCM590XX_REG_IS_GPLDO(i))) { pmu->desc[i].ops = &bcm590xx_ops_ldo; pmu->desc[i].vsel_mask = BCM590XX_LDO_VSEL_MASK; - } else { + } else if (BCM590XX_REG_IS_VBUS(i)) + pmu->desc[i].ops = &bcm590xx_ops_vbus; + else { pmu->desc[i].ops = &bcm590xx_ops_dcdc; pmu->desc[i].vsel_mask = BCM590XX_SR_VSEL_MASK; } - pmu->desc[i].vsel_reg = bcm590xx_get_vsel_register(i); - pmu->desc[i].enable_is_inverted = true; - pmu->desc[i].enable_mask = BCM590XX_REG_ENABLE; + if (BCM590XX_REG_IS_VBUS(i)) + pmu->desc[i].enable_mask = BCM590XX_VBUS_ENABLE; + else { + pmu->desc[i].vsel_reg = bcm590xx_get_vsel_register(i); + pmu->desc[i].enable_is_inverted = true; + pmu->desc[i].enable_mask = BCM590XX_REG_ENABLE; + } pmu->desc[i].enable_reg = bcm590xx_get_enable_register(i); pmu->desc[i].type = REGULATOR_VOLTAGE; pmu->desc[i].owner = THIS_MODULE; @@ -371,7 +440,10 @@ static int bcm590xx_probe(struct platform_device *pdev) config.dev = bcm590xx->dev; config.init_data = reg_data; config.driver_data = pmu; - config.regmap = bcm590xx->regmap; + if (BCM590XX_REG_IS_GPLDO(i) || BCM590XX_REG_IS_VBUS(i)) + config.regmap = bcm590xx->regmap_sec; + else + config.regmap = bcm590xx->regmap_pri; if (bcm590xx_reg_matches) config.of_node = bcm590xx_reg_matches[i].of_node; diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 5cf8b91ce996..6d9371f88875 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -124,4 +124,7 @@ int wm5102_patch(struct arizona *arizona); int wm5110_patch(struct arizona *arizona); int wm8997_patch(struct arizona *arizona); +extern int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, + bool mandatory); + #endif diff --git a/include/linux/mfd/bcm590xx.h b/include/linux/mfd/bcm590xx.h index 434df2d4e587..267aedee1c7a 100644 --- a/include/linux/mfd/bcm590xx.h +++ b/include/linux/mfd/bcm590xx.h @@ -19,12 +19,15 @@ #include <linux/regmap.h> /* max register address */ -#define BCM590XX_MAX_REGISTER 0xe7 +#define BCM590XX_MAX_REGISTER_PRI 0xe7 +#define BCM590XX_MAX_REGISTER_SEC 0xf0 struct bcm590xx { struct device *dev; - struct i2c_client *i2c_client; - struct regmap *regmap; + struct i2c_client *i2c_pri; + struct i2c_client *i2c_sec; + struct regmap *regmap_pri; + struct regmap *regmap_sec; unsigned int id; }; diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 3f43069413e7..45f0f9d2ed25 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -64,6 +64,20 @@ enum { TPS65090_REGULATOR_MAX, }; +/* Register addresses */ +#define TPS65090_REG_INTR_STS 0x00 +#define TPS65090_REG_INTR_STS2 0x01 +#define TPS65090_REG_INTR_MASK 0x02 +#define TPS65090_REG_INTR_MASK2 0x03 +#define TPS65090_REG_CG_CTRL0 0x04 +#define TPS65090_REG_CG_CTRL1 0x05 +#define TPS65090_REG_CG_CTRL2 0x06 +#define TPS65090_REG_CG_CTRL3 0x07 +#define TPS65090_REG_CG_CTRL4 0x08 +#define TPS65090_REG_CG_CTRL5 0x09 +#define TPS65090_REG_CG_STATUS1 0x0a +#define TPS65090_REG_CG_STATUS2 0x0b + struct tps65090 { struct device *dev; struct regmap *rmap; |