diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 25 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 4 | ||||
-rw-r--r-- | drivers/regulator/arizona-ldo1.c | 27 | ||||
-rw-r--r-- | drivers/regulator/bd71837-regulator.c | 44 | ||||
-rw-r--r-- | drivers/regulator/bd9571mwv-regulator.c | 72 | ||||
-rw-r--r-- | drivers/regulator/core.c | 44 | ||||
-rw-r--r-- | drivers/regulator/cpcap-regulator.c | 103 | ||||
-rw-r--r-- | drivers/regulator/max14577-regulator.c | 22 | ||||
-rw-r--r-- | drivers/regulator/max77686-regulator.c | 32 | ||||
-rw-r--r-- | drivers/regulator/max77693-regulator.c | 32 | ||||
-rw-r--r-- | drivers/regulator/max77802-regulator.c | 34 | ||||
-rw-r--r-- | drivers/regulator/max8997-regulator.c | 33 | ||||
-rw-r--r-- | drivers/regulator/max8998.c | 28 | ||||
-rw-r--r-- | drivers/regulator/pfuze100-regulator.c | 112 | ||||
-rw-r--r-- | drivers/regulator/qcom-rpmh-regulator.c | 769 | ||||
-rw-r--r-- | drivers/regulator/qcom_spmi-regulator.c | 38 | ||||
-rw-r--r-- | drivers/regulator/s2mpa01.c | 14 | ||||
-rw-r--r-- | drivers/regulator/s2mps11.c | 21 | ||||
-rw-r--r-- | drivers/regulator/s5m8767.c | 16 | ||||
-rw-r--r-- | drivers/regulator/tps65217-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/uniphier-regulator.c | 213 |
21 files changed, 1435 insertions, 250 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 5dbccf5f3037..329cdd33ed62 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -180,9 +180,9 @@ config REGULATOR_BCM590XX BCM590xx PMUs. This will enable support for the software controllable LDO/Switching regulators. -config REGULATOR_BD71837 +config REGULATOR_BD718XX tristate "ROHM BD71837 Power Regulator" - depends on MFD_BD71837 + depends on MFD_ROHM_BD718XX help This driver supports voltage regulators on ROHM BD71837 PMIC. This will enable support for the software controllable buck @@ -633,12 +633,12 @@ config REGULATOR_PCF50633 on PCF50633 config REGULATOR_PFUZE100 - tristate "Freescale PFUZE100/200/3000 regulator driver" + tristate "Freescale PFUZE100/200/3000/3001 regulator driver" depends on I2C select REGMAP_I2C help Say y here to support the regulators found on the Freescale - PFUZE100/200/3000 PMIC. + PFUZE100/200/3000/3001 PMIC. config REGULATOR_PV88060 tristate "Powerventure Semiconductor PV88060 regulator" @@ -682,6 +682,15 @@ config REGULATOR_QCOM_RPM Qualcomm RPM as a module. The module will be named "qcom_rpm-regulator". +config REGULATOR_QCOM_RPMH + tristate "Qualcomm Technologies, Inc. RPMh regulator driver" + depends on QCOM_RPMH || COMPILE_TEST + help + This driver supports control of PMIC regulators via the RPMh hardware + block found on Qualcomm Technologies Inc. SoCs. RPMh regulator + control allows for voting on regulator state between multiple + processors within the SoC. + config REGULATOR_QCOM_SMD_RPM tristate "Qualcomm SMD based RPM regulator driver" depends on QCOM_SMD_RPM @@ -950,6 +959,14 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_UNIPHIER + tristate "UniPhier regulator driver" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on OF && MFD_SYSCON + default ARCH_UNIPHIER + help + Support for regulators implemented on Socionext UniPhier SoCs. + config REGULATOR_VCTRL tristate "Voltage controlled regulators" depends on OF diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bd818ceb7c72..801d9a34a203 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -27,7 +27,7 @@ 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_BD71837) += bd71837-regulator.o +obj-$(CONFIG_REGULATOR_BD718XX) += bd71837-regulator.o obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o @@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o +obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o @@ -118,6 +119,7 @@ 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_UNIPHIER) += uniphier-regulator.o obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index f6d6a4ad9e8a..e976d073f28d 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -36,6 +36,8 @@ struct arizona_ldo1 { struct regulator_consumer_supply supply; struct regulator_init_data init_data; + + struct gpio_desc *ena_gpiod; }; static int arizona_ldo1_hc_list_voltage(struct regulator_dev *rdev, @@ -253,12 +255,17 @@ static int arizona_ldo1_common_init(struct platform_device *pdev, } } - /* We assume that high output = regulator off */ - config.ena_gpiod = devm_gpiod_get_optional(&pdev->dev, "wlf,ldoena", - GPIOD_OUT_HIGH); + /* We assume that high output = regulator off + * Don't use devm, since we need to get against the parent device + * so clean up would happen at the wrong time + */ + config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena", + GPIOD_OUT_LOW); if (IS_ERR(config.ena_gpiod)) return PTR_ERR(config.ena_gpiod); + ldo1->ena_gpiod = config.ena_gpiod; + if (pdata->init_data) config.init_data = pdata->init_data; else @@ -276,6 +283,9 @@ static int arizona_ldo1_common_init(struct platform_device *pdev, of_node_put(config.of_node); if (IS_ERR(ldo1->regulator)) { + if (config.ena_gpiod) + gpiod_put(config.ena_gpiod); + ret = PTR_ERR(ldo1->regulator); dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n", ret); @@ -334,8 +344,19 @@ static int arizona_ldo1_probe(struct platform_device *pdev) return ret; } +static int arizona_ldo1_remove(struct platform_device *pdev) +{ + struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev); + + if (ldo1->ena_gpiod) + gpiod_put(ldo1->ena_gpiod); + + return 0; +} + static struct platform_driver arizona_ldo1_driver = { .probe = arizona_ldo1_probe, + .remove = arizona_ldo1_remove, .driver = { .name = "arizona-ldo1", }, diff --git a/drivers/regulator/bd71837-regulator.c b/drivers/regulator/bd71837-regulator.c index 6eae4d0432a2..0f8ac8dec3e1 100644 --- a/drivers/regulator/bd71837-regulator.c +++ b/drivers/regulator/bd71837-regulator.c @@ -2,19 +2,18 @@ // Copyright (C) 2018 ROHM Semiconductors // bd71837-regulator.c ROHM BD71837MWV regulator driver -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> +#include <linux/delay.h> #include <linux/err.h> +#include <linux/gpio.h> #include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/rohm-bd718x7.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/gpio.h> -#include <linux/mfd/bd71837.h> #include <linux/regulator/of_regulator.h> +#include <linux/slab.h> struct bd71837_pmic { struct regulator_desc descs[BD71837_REGULATOR_CNT]; @@ -39,7 +38,7 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev, int id = rdev->desc->id; unsigned int ramp_value = BUCK_RAMPRATE_10P00MV; - dev_dbg(&(pmic->pdev->dev), "Buck[%d] Set Ramp = %d\n", id + 1, + dev_dbg(&pmic->pdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1, ramp_delay); switch (ramp_delay) { case 1 ... 1250: @@ -73,14 +72,10 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev, static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev, unsigned int sel) { - int ret; - - ret = regulator_is_enabled_regmap(rdev); - if (!ret) - ret = regulator_set_voltage_sel_regmap(rdev, sel); - else if (ret == 1) - ret = -EBUSY; - return ret; + if (regulator_is_enabled_regmap(rdev)) + return -EBUSY; + + return regulator_set_voltage_sel_regmap(rdev, sel); } static struct regulator_ops bd71837_ldo_regulator_ops = { @@ -195,7 +190,7 @@ static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = { * LDO2 * 0.8 or 0.9V */ -const unsigned int ldo_2_volts[] = { +static const unsigned int ldo_2_volts[] = { 900000, 800000 }; @@ -495,7 +490,6 @@ struct reg_init { static int bd71837_probe(struct platform_device *pdev) { struct bd71837_pmic *pmic; - struct bd71837_board *pdata; struct regulator_config config = { 0 }; struct reg_init pmic_regulator_inits[] = { { @@ -548,8 +542,7 @@ static int bd71837_probe(struct platform_device *pdev) int i, err; - pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic), - GFP_KERNEL); + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); if (!pmic) return -ENOMEM; @@ -564,7 +557,6 @@ static int bd71837_probe(struct platform_device *pdev) goto err; } platform_set_drvdata(pdev, pmic); - pdata = dev_get_platdata(pmic->mfd->dev); /* Register LOCK release */ err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK, @@ -573,8 +565,8 @@ static int bd71837_probe(struct platform_device *pdev) dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err); goto err; } else { - dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n", - __func__, BD71837_REG_REGLOCK); + dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n", + BD71837_REG_REGLOCK); } for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) { @@ -584,9 +576,6 @@ static int bd71837_probe(struct platform_device *pdev) desc = &pmic->descs[i]; - if (pdata) - config.init_data = pdata->init_data[i]; - config.dev = pdev->dev.parent; config.driver_data = pmic; config.regmap = pmic->mfd->regmap; @@ -619,8 +608,6 @@ static int bd71837_probe(struct platform_device *pdev) pmic->rdev[i] = rdev; } - return 0; - err: return err; } @@ -628,7 +615,6 @@ err: static struct platform_driver bd71837_regulator = { .driver = { .name = "bd71837-pmic", - .owner = THIS_MODULE, }, .probe = bd71837_probe, }; diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index be574eb444eb..274c5ed7cd73 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -30,6 +30,7 @@ struct bd9571mwv_reg { /* DDR Backup Power */ u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */ u8 bkup_mode_cnt_saved; + bool bkup_mode_enabled; /* Power switch type */ bool rstbmode_level; @@ -171,13 +172,60 @@ static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode) return 0; } +static ssize_t backup_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", bdreg->bkup_mode_enabled ? "on" : "off"); +} + +static ssize_t backup_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); + unsigned int mode; + int ret; + + if (!count) + return 0; + + ret = kstrtobool(buf, &bdreg->bkup_mode_enabled); + if (ret) + return ret; + + if (!bdreg->rstbmode_level) + return count; + + /* + * Configure DDR Backup Mode, to change the role of the accessory power + * switch from a power switch to a wake-up switch, or vice versa + */ + ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode); + if (ret) + return ret; + + mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK; + if (bdreg->bkup_mode_enabled) + mode |= bdreg->bkup_mode_cnt_keepon; + + ret = bd9571mwv_bkup_mode_write(bdreg->bd, mode); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(backup_mode); + static int bd9571mwv_suspend(struct device *dev) { struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); unsigned int mode; int ret; - if (!device_may_wakeup(dev)) + if (!bdreg->bkup_mode_enabled) return 0; /* Save DDR Backup Mode */ @@ -204,7 +252,7 @@ static int bd9571mwv_resume(struct device *dev) { struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); - if (!device_may_wakeup(dev)) + if (!bdreg->bkup_mode_enabled) return 0; /* Restore DDR Backup Mode */ @@ -215,9 +263,15 @@ static const struct dev_pm_ops bd9571mwv_pm = { SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume) }; +static int bd9571mwv_regulator_remove(struct platform_device *pdev) +{ + device_remove_file(&pdev->dev, &dev_attr_backup_mode); + return 0; +} #define DEV_PM_OPS &bd9571mwv_pm #else #define DEV_PM_OPS NULL +#define bd9571mwv_regulator_remove NULL #endif /* CONFIG_PM_SLEEP */ static int bd9571mwv_regulator_probe(struct platform_device *pdev) @@ -270,14 +324,21 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) return -EINVAL; } +#ifdef CONFIG_PM_SLEEP if (bdreg->bkup_mode_cnt_keepon) { - device_set_wakeup_capable(&pdev->dev, true); + int ret; + /* - * Wakeup is enabled by default in pulse mode, but needs + * Backup mode is enabled by default in pulse mode, but needs * explicit user setup in level mode. */ - device_set_wakeup_enable(&pdev->dev, bdreg->rstbmode_pulse); + bdreg->bkup_mode_enabled = bdreg->rstbmode_pulse; + + ret = device_create_file(&pdev->dev, &dev_attr_backup_mode); + if (ret) + return ret; } +#endif /* CONFIG_PM_SLEEP */ return 0; } @@ -294,6 +355,7 @@ static struct platform_driver bd9571mwv_regulator_driver = { .pm = DEV_PM_OPS, }, .probe = bd9571mwv_regulator_probe, + .remove = bd9571mwv_regulator_remove, .id_table = bd9571mwv_regulator_id_table, }; module_platform_driver(bd9571mwv_regulator_driver); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6ed568b96c0e..bb1324f93143 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1740,6 +1740,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id, rdev->use_count = 0; } + device_link_add(dev, &rdev->dev, DL_FLAG_STATELESS); + return regulator; } @@ -1829,9 +1831,21 @@ static void _regulator_put(struct regulator *regulator) debugfs_remove_recursive(regulator->debugfs); - /* remove any sysfs entries */ - if (regulator->dev) + if (regulator->dev) { + int count = 0; + struct regulator *r; + + list_for_each_entry(r, &rdev->consumer_list, list) + if (r->dev == regulator->dev) + count++; + + if (count == 1) + device_link_remove(regulator->dev, &rdev->dev); + + /* remove any sysfs entries */ sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); + } + regulator_lock(rdev); list_del(®ulator->list); @@ -4441,7 +4455,7 @@ void regulator_unregister(struct regulator_dev *rdev) EXPORT_SYMBOL_GPL(regulator_unregister); #ifdef CONFIG_SUSPEND -static int _regulator_suspend_late(struct device *dev, void *data) +static int _regulator_suspend(struct device *dev, void *data) { struct regulator_dev *rdev = dev_to_rdev(dev); suspend_state_t *state = data; @@ -4455,20 +4469,20 @@ static int _regulator_suspend_late(struct device *dev, void *data) } /** - * regulator_suspend_late - prepare regulators for system wide suspend + * regulator_suspend - prepare regulators for system wide suspend * @state: system suspend state * * Configure each regulator with it's suspend operating parameters for state. */ -static int regulator_suspend_late(struct device *dev) +static int regulator_suspend(struct device *dev) { suspend_state_t state = pm_suspend_target_state; return class_for_each_device(®ulator_class, NULL, &state, - _regulator_suspend_late); + _regulator_suspend); } -static int _regulator_resume_early(struct device *dev, void *data) +static int _regulator_resume(struct device *dev, void *data) { int ret = 0; struct regulator_dev *rdev = dev_to_rdev(dev); @@ -4481,35 +4495,35 @@ static int _regulator_resume_early(struct device *dev, void *data) regulator_lock(rdev); - if (rdev->desc->ops->resume_early && + if (rdev->desc->ops->resume && (rstate->enabled == ENABLE_IN_SUSPEND || rstate->enabled == DISABLE_IN_SUSPEND)) - ret = rdev->desc->ops->resume_early(rdev); + ret = rdev->desc->ops->resume(rdev); regulator_unlock(rdev); return ret; } -static int regulator_resume_early(struct device *dev) +static int regulator_resume(struct device *dev) { suspend_state_t state = pm_suspend_target_state; return class_for_each_device(®ulator_class, NULL, &state, - _regulator_resume_early); + _regulator_resume); } #else /* !CONFIG_SUSPEND */ -#define regulator_suspend_late NULL -#define regulator_resume_early NULL +#define regulator_suspend NULL +#define regulator_resume NULL #endif /* !CONFIG_SUSPEND */ #ifdef CONFIG_PM static const struct dev_pm_ops __maybe_unused regulator_pm_ops = { - .suspend_late = regulator_suspend_late, - .resume_early = regulator_resume_early, + .suspend = regulator_suspend, + .resume = regulator_resume, }; #endif diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c index bd910fe123d9..2131457937b7 100644 --- a/drivers/regulator/cpcap-regulator.c +++ b/drivers/regulator/cpcap-regulator.c @@ -271,6 +271,29 @@ static struct regulator_ops cpcap_regulator_ops = { }; static const unsigned int unknown_val_tbl[] = { 0, }; +static const unsigned int sw2_sw4_val_tbl[] = { 612500, 625000, 637500, + 650000, 662500, 675000, + 687500, 700000, 712500, + 725000, 737500, 750000, + 762500, 775000, 787500, + 800000, 812500, 825000, + 837500, 850000, 862500, + 875000, 887500, 900000, + 912500, 925000, 937500, + 950000, 962500, 975000, + 987500, 1000000, 1012500, + 1025000, 1037500, 1050000, + 1062500, 1075000, 1087500, + 1100000, 1112500, 1125000, + 1137500, 1150000, 1162500, + 1175000, 1187500, 1200000, + 1212500, 1225000, 1237500, + 1250000, 1262500, 1275000, + 1287500, 1300000, 1312500, + 1325000, 1337500, 1350000, + 1362500, 1375000, 1387500, + 1400000, 1412500, 1425000, + 1437500, 1450000, 1462500, }; static const unsigned int sw5_val_tbl[] = { 0, 5050000, }; static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000, 2900000, }; @@ -389,6 +412,82 @@ static struct cpcap_regulator omap4_regulators[] = { { /* sentinel */ }, }; +static struct cpcap_regulator xoom_regulators[] = { + CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2, + CPCAP_BIT_SW1_SEL, unknown_val_tbl, + 0, 0, 0, 0, 0, 0), + CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2, + CPCAP_BIT_SW2_SEL, sw2_sw4_val_tbl, + 0xf00, 0x7f, 0, 0x800, 0, 120), + CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2, + CPCAP_BIT_SW3_SEL, unknown_val_tbl, + 0, 0, 0, 0, 0, 0), + CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2, + CPCAP_BIT_SW4_SEL, sw2_sw4_val_tbl, + 0xf00, 0x7f, 0, 0x900, 0, 100), + CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2, + CPCAP_BIT_SW5_SEL, sw5_val_tbl, + 0x2a, 0, 0, 0x22, 0, 0), + CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2, + CPCAP_BIT_SW6_SEL, unknown_val_tbl, + 0, 0, 0, 0, 0, 0), + CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2, + CPCAP_BIT_VCAM_SEL, vcam_val_tbl, + 0x87, 0x30, 4, 0x7, 0, 420), + CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VCSI_SEL, vcsi_val_tbl, + 0x47, 0x10, 4, 0x7, 0, 350), + CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VDAC_SEL, vdac_val_tbl, + 0x87, 0x30, 4, 0x3, 0, 420), + CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2, + CPCAP_BIT_VDIG_SEL, vdig_val_tbl, + 0x87, 0x30, 4, 0x5, 0, 420), + CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl, + 0x80, 0xf, 0, 0x80, 0, 420), + CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl, + 0x17, 0, 0, 0x2, 0, 0), + CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2, + CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl, + 0x87, 0x38, 3, 0x2, 0, 420), + CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VPLL_SEL, vpll_val_tbl, + 0x43, 0x18, 3, 0x1, 0, 420), + CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VRF1_SEL, vrf1_val_tbl, + 0xac, 0x2, 1, 0xc, 0, 10), + CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VRF2_SEL, vrf2_val_tbl, + 0x23, 0x8, 3, 0x3, 0, 10), + CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl, + 0x23, 0x8, 3, 0x3, 0, 420), + CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl, + 0x47, 0x10, 4, 0x5, 0, 420), + CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl, + 0x20c, 0xc0, 6, 0x8, 0, 420), + CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3, + 0xffff, vsim_val_tbl, + 0x23, 0x8, 3, 0x3, 0, 420), + CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3, + 0xffff, vsimcard_val_tbl, + 0x1e80, 0x8, 3, 0x1e00, 0, 420), + CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VVIB_SEL, vvib_val_tbl, + 0x1, 0xc, 2, 0, 0x1, 500), + CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3, + CPCAP_BIT_VUSB_SEL, vusb_val_tbl, + 0x11c, 0x40, 6, 0xc, 0, 0), + CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4, + CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl, + 0x16, 0x1, 0, 0x4, 0, 0), + { /* sentinel */ }, +}; + static const struct of_device_id cpcap_regulator_id_table[] = { { .compatible = "motorola,cpcap-regulator", @@ -397,6 +496,10 @@ static const struct of_device_id cpcap_regulator_id_table[] = { .compatible = "motorola,mapphone-cpcap-regulator", .data = omap4_regulators, }, + { + .compatible = "motorola,xoom-cpcap-regulator", + .data = xoom_regulators, + }, {}, }; MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table); diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index 0db288ce319c..bc7f4751bf9c 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -1,19 +1,9 @@ -/* - * max14577.c - Regulator driver for the Maxim 14577/77836 - * - * Copyright (C) 2013,2014 Samsung Electronics - * Krzysztof Kozlowski <krzk@kernel.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// max14577.c - Regulator driver for the Maxim 14577/77836 +// +// Copyright (C) 2013,2014 Samsung Electronics +// Krzysztof Kozlowski <krzk@kernel.org> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index c301f3733475..bee060937f56 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -1,26 +1,12 @@ -/* - * max77686.c - Regulator driver for the Maxim 77686 - * - * Copyright (C) 2012 Samsung Electronics - * Chiwoong Byun <woong.byun@samsung.com> - * Jonghwa Lee <jonghwa3.lee@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * This driver is based on max8997.c - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// max77686.c - Regulator driver for the Maxim 77686 +// +// Copyright (C) 2012 Samsung Electronics +// Chiwoong Byun <woong.byun@samsung.com> +// Jonghwa Lee <jonghwa3.lee@samsung.com> +// +// This driver is based on max8997.c #include <linux/kernel.h> #include <linux/bug.h> diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c index e7000e777292..077ecbbfdf76 100644 --- a/drivers/regulator/max77693-regulator.c +++ b/drivers/regulator/max77693-regulator.c @@ -1,26 +1,12 @@ -/* - * max77693.c - Regulator driver for the Maxim 77693 and 77843 - * - * Copyright (C) 2013-2015 Samsung Electronics - * Jonghwa Lee <jonghwa3.lee@samsung.com> - * Krzysztof Kozlowski <krzk@kernel.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * This driver is based on max77686.c - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// max77693.c - Regulator driver for the Maxim 77693 and 77843 +// +// Copyright (C) 2013-2015 Samsung Electronics +// Jonghwa Lee <jonghwa3.lee@samsung.com> +// Krzysztof Kozlowski <krzk@kernel.org> +// +// This driver is based on max77686.c #include <linux/err.h> #include <linux/slab.h> diff --git a/drivers/regulator/max77802-regulator.c b/drivers/regulator/max77802-regulator.c index b6261903818c..c30cf5c9f2de 100644 --- a/drivers/regulator/max77802-regulator.c +++ b/drivers/regulator/max77802-regulator.c @@ -1,25 +1,15 @@ -/* - * max77802.c - Regulator driver for the Maxim 77802 - * - * Copyright (C) 2013-2014 Google, Inc - * Simon Glass <sjg@chromium.org> - * - * Copyright (C) 2012 Samsung Electronics - * Chiwoong Byun <woong.byun@samsung.com> - * Jonghwa Lee <jonghwa3.lee@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This driver is based on max8997.c - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// max77802.c - Regulator driver for the Maxim 77802 +// +// Copyright (C) 2013-2014 Google, Inc +// Simon Glass <sjg@chromium.org> +// +// Copyright (C) 2012 Samsung Electronics +// Chiwoong Byun <woong.byun@samsung.com> +// Jonghwa Lee <jonghwa3.lee@samsung.com> +// +// This driver is based on max8997.c #include <linux/kernel.h> #include <linux/bug.h> diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c index a8ea30ee18a6..ad0c806b0737 100644 --- a/drivers/regulator/max8997-regulator.c +++ b/drivers/regulator/max8997-regulator.c @@ -1,25 +1,11 @@ -/* - * max8997.c - Regulator driver for the Maxim 8997/8966 - * - * Copyright (C) 2011 Samsung Electronics - * MyungJoo Ham <myungjoo.ham@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * This driver is based on max8998.c - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// max8997.c - Regulator driver for the Maxim 8997/8966 +// +// Copyright (C) 2011 Samsung Electronics +// MyungJoo Ham <myungjoo.ham@samsung.com> +// +// This driver is based on max8998.c #include <linux/bug.h> #include <linux/err.h> @@ -165,8 +151,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev, int rid = rdev_get_id(rdev); int val; - if (rid >= ARRAY_SIZE(reg_voltage_map) || - rid < 0) + if (rid < 0 || rid >= ARRAY_SIZE(reg_voltage_map)) return -EINVAL; desc = reg_voltage_map[rid]; diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 6b9f262ebbb0..271bb736f3f5 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -1,24 +1,10 @@ -/* - * max8998.c - Voltage regulator driver for the Maxim 8998 - * - * Copyright (C) 2009-2010 Samsung Electronics - * Kyungmin Park <kyungmin.park@samsung.com> - * Marek Szyprowski <m.szyprowski@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// max8998.c - Voltage regulator driver for the Maxim 8998 +// +// Copyright (C) 2009-2010 Samsung Electronics +// Kyungmin Park <kyungmin.park@samsung.com> +// Marek Szyprowski <m.szyprowski@samsung.com> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 8d9dbcc775ea..31c3a236120a 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -17,6 +17,8 @@ #include <linux/slab.h> #include <linux/regmap.h> +#define PFUZE_FLAG_DISABLE_SW BIT(1) + #define PFUZE_NUMREGS 128 #define PFUZE100_VOL_OFFSET 0 #define PFUZE100_STANDBY_OFFSET 1 @@ -44,16 +46,18 @@ #define PFUZE100_VGEN5VOL 0x70 #define PFUZE100_VGEN6VOL 0x71 -enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 }; +enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, }; struct pfuze_regulator { struct regulator_desc desc; unsigned char stby_reg; unsigned char stby_mask; + bool sw_reg; }; struct pfuze_chip { int chip_id; + int flags; struct regmap *regmap; struct device *dev; struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR]; @@ -92,6 +96,7 @@ static const struct i2c_device_id pfuze_device_id[] = { {.name = "pfuze100", .driver_data = PFUZE100}, {.name = "pfuze200", .driver_data = PFUZE200}, {.name = "pfuze3000", .driver_data = PFUZE3000}, + {.name = "pfuze3001", .driver_data = PFUZE3001}, { } }; MODULE_DEVICE_TABLE(i2c, pfuze_device_id); @@ -100,6 +105,7 @@ static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000}, + { .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001}, { } }; MODULE_DEVICE_TABLE(of, pfuze_dt_ids); @@ -108,10 +114,28 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev); int id = rdev_get_id(rdev); + bool reg_has_ramp_delay; unsigned int ramp_bits; int ret; - if (id < PFUZE100_SWBST) { + switch (pfuze100->chip_id) { + case PFUZE3001: + /* no dynamic voltage scaling for PF3001 */ + reg_has_ramp_delay = false; + break; + case PFUZE3000: + reg_has_ramp_delay = (id < PFUZE3000_SWBST); + break; + case PFUZE200: + reg_has_ramp_delay = (id < PFUZE200_SWBST); + break; + case PFUZE100: + default: + reg_has_ramp_delay = (id < PFUZE100_SWBST); + break; + } + + if (reg_has_ramp_delay) { ramp_delay = 12500 / ramp_delay; ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3); ret = regmap_update_bits(pfuze100->regmap, @@ -119,8 +143,9 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) 0xc0, ramp_bits << 6); if (ret < 0) dev_err(pfuze100->dev, "ramp failed, err %d\n", ret); - } else + } else { ret = -EACCES; + } return ret; } @@ -142,6 +167,14 @@ static const struct regulator_ops pfuze100_fixed_regulator_ops = { }; static const struct regulator_ops pfuze100_sw_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = pfuze100_set_ramp_delay, +}; + +static const struct regulator_ops pfuze100_sw_disable_regulator_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -192,13 +225,11 @@ static const struct regulator_ops pfuze100_swb_regulator_ops = { .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ .vsel_mask = 0x3f, \ .enable_reg = (base) + PFUZE100_MODE_OFFSET, \ - .enable_val = 0xc, \ - .disable_val = 0x0, \ .enable_mask = 0xf, \ - .enable_time = 500, \ }, \ .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ .stby_mask = 0x3f, \ + .sw_reg = true, \ } #define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \ @@ -361,6 +392,19 @@ static struct pfuze_regulator pfuze3000_regulators[] = { PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; +static struct pfuze_regulator pfuze3001_regulators[] = { + PFUZE100_SWB_REG(PFUZE3001, SW1, PFUZE100_SW1ABVOL, 0x1f, pfuze3000_sw1a), + PFUZE100_SWB_REG(PFUZE3001, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo), + PFUZE3000_SW3_REG(PFUZE3001, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000), + PFUZE100_SWB_REG(PFUZE3001, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_VGEN_REG(PFUZE3001, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE3001, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE3000_VCC_REG(PFUZE3001, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000), + PFUZE3000_VCC_REG(PFUZE3001, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000), + PFUZE100_VGEN_REG(PFUZE3001, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), +}; + #ifdef CONFIG_OF /* PFUZE100 */ static struct of_regulator_match pfuze100_matches[] = { @@ -418,6 +462,21 @@ static struct of_regulator_match pfuze3000_matches[] = { { .name = "vldo4", }, }; +/* PFUZE3001 */ +static struct of_regulator_match pfuze3001_matches[] = { + + { .name = "sw1", }, + { .name = "sw2", }, + { .name = "sw3", }, + { .name = "vsnvs", }, + { .name = "vldo1", }, + { .name = "vldo2", }, + { .name = "vccsd", }, + { .name = "v33", }, + { .name = "vldo3", }, + { .name = "vldo4", }, +}; + static struct of_regulator_match *pfuze_matches; static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) @@ -430,6 +489,9 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) if (!np) return -EINVAL; + if (of_property_read_bool(np, "fsl,pfuze-support-disable-sw")) + chip->flags |= PFUZE_FLAG_DISABLE_SW; + parent = of_get_child_by_name(np, "regulators"); if (!parent) { dev_err(dev, "regulators node not found\n"); @@ -437,6 +499,11 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) } switch (chip->chip_id) { + case PFUZE3001: + pfuze_matches = pfuze3001_matches; + ret = of_regulator_match(dev, parent, pfuze3001_matches, + ARRAY_SIZE(pfuze3001_matches)); + break; case PFUZE3000: pfuze_matches = pfuze3000_matches; ret = of_regulator_match(dev, parent, pfuze3000_matches, @@ -508,7 +575,8 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip) */ dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8"); } else if ((value & 0x0f) != pfuze_chip->chip_id && - (value & 0xf0) >> 4 != pfuze_chip->chip_id) { + (value & 0xf0) >> 4 != pfuze_chip->chip_id && + (value != pfuze_chip->chip_id)) { /* device id NOT match with your setting */ dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); return -ENODEV; @@ -588,6 +656,13 @@ static int pfuze100_regulator_probe(struct i2c_client *client, /* use the right regulators after identify the right device */ switch (pfuze_chip->chip_id) { + case PFUZE3001: + pfuze_chip->pfuze_regulators = pfuze3001_regulators; + regulator_num = ARRAY_SIZE(pfuze3001_regulators); + sw_check_start = PFUZE3001_SW2; + sw_check_end = PFUZE3001_SW2; + sw_hi = 1 << 3; + break; case PFUZE3000: pfuze_chip->pfuze_regulators = pfuze3000_regulators; regulator_num = ARRAY_SIZE(pfuze3000_regulators); @@ -611,7 +686,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client, } dev_info(&client->dev, "pfuze%s found.\n", (pfuze_chip->chip_id == PFUZE100) ? "100" : - ((pfuze_chip->chip_id == PFUZE200) ? "200" : "3000")); + (((pfuze_chip->chip_id == PFUZE200) ? "200" : + ((pfuze_chip->chip_id == PFUZE3000) ? "3000" : "3001")))); memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators, sizeof(pfuze_chip->regulator_descs)); @@ -636,7 +712,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client, if (i >= sw_check_start && i <= sw_check_end) { regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val); if (val & sw_hi) { - if (pfuze_chip->chip_id == PFUZE3000) { + if (pfuze_chip->chip_id == PFUZE3000 || + pfuze_chip->chip_id == PFUZE3001) { desc->volt_table = pfuze3000_sw2hi; desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi); } else { @@ -647,6 +724,21 @@ static int pfuze100_regulator_probe(struct i2c_client *client, } } + /* + * Allow SW regulators to turn off. Checking it trough a flag is + * a workaround to keep the backward compatibility with existing + * old dtb's which may relay on the fact that we didn't disable + * the switched regulator till yet. + */ + if (pfuze_chip->flags & PFUZE_FLAG_DISABLE_SW) { + if (pfuze_chip->regulator_descs[i].sw_reg) { + desc->ops = &pfuze100_sw_disable_regulator_ops; + desc->enable_val = 0x8; + desc->disable_val = 0x0; + desc->enable_time = 500; + } + } + config.dev = &client->dev; config.init_data = init_data; config.driver_data = pfuze_chip; @@ -675,5 +767,5 @@ static struct i2c_driver pfuze_driver = { module_i2c_driver(pfuze_driver); MODULE_AUTHOR("Robin Gong <b38343@freescale.com>"); -MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000 PMIC"); +MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000/3001 PMIC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c new file mode 100644 index 000000000000..9f27daebd8c8 --- /dev/null +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +#include <soc/qcom/cmd-db.h> +#include <soc/qcom/rpmh.h> + +#include <dt-bindings/regulator/qcom,rpmh-regulator.h> + +/** + * enum rpmh_regulator_type - supported RPMh accelerator types + * %VRM: RPMh VRM accelerator which supports voting on enable, voltage, + * and mode of LDO, SMPS, and BOB type PMIC regulators. + * %XOB: RPMh XOB accelerator which supports voting on the enable state + * of PMIC regulators. + */ +enum rpmh_regulator_type { + VRM, + XOB, +}; + +#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0 +#define RPMH_REGULATOR_REG_ENABLE 0x4 +#define RPMH_REGULATOR_REG_VRM_MODE 0x8 + +#define PMIC4_LDO_MODE_RETENTION 4 +#define PMIC4_LDO_MODE_LPM 5 +#define PMIC4_LDO_MODE_HPM 7 + +#define PMIC4_SMPS_MODE_RETENTION 4 +#define PMIC4_SMPS_MODE_PFM 5 +#define PMIC4_SMPS_MODE_AUTO 6 +#define PMIC4_SMPS_MODE_PWM 7 + +#define PMIC4_BOB_MODE_PASS 0 +#define PMIC4_BOB_MODE_PFM 1 +#define PMIC4_BOB_MODE_AUTO 2 +#define PMIC4_BOB_MODE_PWM 3 + +/** + * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations + * @regulator_type: RPMh accelerator type used to manage this + * regulator + * @ops: Pointer to regulator ops callback structure + * @voltage_range: The single range of voltages supported by this + * PMIC regulator type + * @n_voltages: The number of unique voltage set points defined + * by voltage_range + * @hpm_min_load_uA: Minimum load current in microamps that requires + * high power mode (HPM) operation. This is used + * for LDO hardware type regulators only. + * @pmic_mode_map: Array indexed by regulator framework mode + * containing PMIC hardware modes. Must be large + * enough to index all framework modes supported + * by this regulator hardware type. + * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined + * in device tree to a regulator framework mode + */ +struct rpmh_vreg_hw_data { + enum rpmh_regulator_type regulator_type; + const struct regulator_ops *ops; + const struct regulator_linear_range voltage_range; + int n_voltages; + int hpm_min_load_uA; + const int *pmic_mode_map; + unsigned int (*of_map_mode)(unsigned int mode); +}; + +/** + * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a + * single regulator device + * @dev: Device pointer for the top-level PMIC RPMh + * regulator parent device. This is used as a + * handle in RPMh write requests. + * @addr: Base address of the regulator resource within + * an RPMh accelerator + * @rdesc: Regulator descriptor + * @hw_data: PMIC regulator configuration data for this RPMh + * regulator + * @always_wait_for_ack: Boolean flag indicating if a request must always + * wait for an ACK from RPMh before continuing even + * if it corresponds to a strictly lower power + * state (e.g. enabled --> disabled). + * @enabled: Flag indicating if the regulator is enabled or + * not + * @bypassed: Boolean indicating if the regulator is in + * bypass (pass-through) mode or not. This is + * only used by BOB rpmh-regulator resources. + * @voltage_selector: Selector used for get_voltage_sel() and + * set_voltage_sel() callbacks + * @mode: RPMh VRM regulator current framework mode + */ +struct rpmh_vreg { + struct device *dev; + u32 addr; + struct regulator_desc rdesc; + const struct rpmh_vreg_hw_data *hw_data; + bool always_wait_for_ack; + + int enabled; + bool bypassed; + int voltage_selector; + unsigned int mode; +}; + +/** + * struct rpmh_vreg_init_data - initialization data for an RPMh regulator + * @name: Name for the regulator which also corresponds + * to the device tree subnode name of the regulator + * @resource_name: RPMh regulator resource name format string. + * This must include exactly one field: '%s' which + * is filled at run-time with the PMIC ID provided + * by device tree property qcom,pmic-id. Example: + * "ldo%s1" for RPMh resource "ldoa1". + * @supply_name: Parent supply regulator name + * @hw_data: Configuration data for this PMIC regulator type + */ +struct rpmh_vreg_init_data { + const char *name; + const char *resource_name; + const char *supply_name; + const struct rpmh_vreg_hw_data *hw_data; +}; + +/** + * rpmh_regulator_send_request() - send the request to RPMh + * @vreg: Pointer to the RPMh regulator + * @cmd: Pointer to the RPMh command to send + * @wait_for_ack: Boolean indicating if execution must wait until the + * request has been acknowledged as complete + * + * Return: 0 on success, errno on failure + */ +static int rpmh_regulator_send_request(struct rpmh_vreg *vreg, + struct tcs_cmd *cmd, bool wait_for_ack) +{ + int ret; + + if (wait_for_ack || vreg->always_wait_for_ack) + ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, 1); + else + ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, + 1); + + return ret; +} + +static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector, bool wait_for_ack) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct tcs_cmd cmd = { + .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE, + }; + int ret; + + /* VRM voltage control register is set with voltage in millivolts. */ + cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev, + selector), 1000); + + ret = rpmh_regulator_send_request(vreg, &cmd, wait_for_ack); + if (!ret) + vreg->voltage_selector = selector; + + return ret; +} + +static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + if (vreg->enabled == -EINVAL) { + /* + * Cache the voltage and send it later when the regulator is + * enabled or disabled. + */ + vreg->voltage_selector = selector; + return 0; + } + + return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector, + selector > vreg->voltage_selector); +} + +static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->voltage_selector; +} + +static int rpmh_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->enabled; +} + +static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev, + bool enable) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct tcs_cmd cmd = { + .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE, + .data = enable, + }; + int ret; + + if (vreg->enabled == -EINVAL && + vreg->voltage_selector != -ENOTRECOVERABLE) { + ret = _rpmh_regulator_vrm_set_voltage_sel(rdev, + vreg->voltage_selector, true); + if (ret < 0) + return ret; + } + + ret = rpmh_regulator_send_request(vreg, &cmd, enable); + if (!ret) + vreg->enabled = enable; + + return ret; +} + +static int rpmh_regulator_enable(struct regulator_dev *rdev) +{ + return rpmh_regulator_set_enable_state(rdev, true); +} + +static int rpmh_regulator_disable(struct regulator_dev *rdev) +{ + return rpmh_regulator_set_enable_state(rdev, false); +} + +static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg, + unsigned int mode, bool bypassed) +{ + struct tcs_cmd cmd = { + .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE, + }; + int pmic_mode; + + if (mode > REGULATOR_MODE_STANDBY) + return -EINVAL; + + pmic_mode = vreg->hw_data->pmic_mode_map[mode]; + if (pmic_mode < 0) + return pmic_mode; + + if (bypassed) + cmd.data = PMIC4_BOB_MODE_PASS; + else + cmd.data = pmic_mode; + + return rpmh_regulator_send_request(vreg, &cmd, true); +} + +static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + int ret; + + if (mode == vreg->mode) + return 0; + + ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed); + if (!ret) + vreg->mode = mode; + + return ret; +} + +static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->mode; +} + +/** + * rpmh_regulator_vrm_set_load() - set the regulator mode based upon the load + * current requested + * @rdev: Regulator device pointer for the rpmh-regulator + * @load_uA: Aggregated load current in microamps + * + * This function is used in the regulator_ops for VRM type RPMh regulator + * devices. + * + * Return: 0 on success, errno on failure + */ +static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + unsigned int mode; + + if (load_uA >= vreg->hw_data->hpm_min_load_uA) + mode = REGULATOR_MODE_NORMAL; + else + mode = REGULATOR_MODE_IDLE; + + return rpmh_regulator_vrm_set_mode(rdev, mode); +} + +static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev, + bool enable) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + int ret; + + if (vreg->bypassed == enable) + return 0; + + ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable); + if (!ret) + vreg->bypassed = enable; + + return ret; +} + +static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev, + bool *enable) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + *enable = vreg->bypassed; + + return 0; +} + +static const struct regulator_ops rpmh_regulator_vrm_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, + .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, + .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, +}; + +static const struct regulator_ops rpmh_regulator_vrm_drms_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, + .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, + .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, + .set_load = rpmh_regulator_vrm_set_load, +}; + +static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, + .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, + .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, + .set_bypass = rpmh_regulator_vrm_set_bypass, + .get_bypass = rpmh_regulator_vrm_get_bypass, +}; + +static const struct regulator_ops rpmh_regulator_xob_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, +}; + +/** + * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator + * vreg: Pointer to the individual rpmh-regulator resource + * dev: Pointer to the top level rpmh-regulator PMIC device + * node: Pointer to the individual rpmh-regulator resource + * device node + * pmic_id: String used to identify the top level rpmh-regulator + * PMIC device on the board + * pmic_rpmh_data: Pointer to a null-terminated array of rpmh-regulator + * resources defined for the top level PMIC device + * + * Return: 0 on success, errno on failure + */ +static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev, + struct device_node *node, const char *pmic_id, + const struct rpmh_vreg_init_data *pmic_rpmh_data) +{ + struct regulator_config reg_config = {}; + char rpmh_resource_name[20] = ""; + const struct rpmh_vreg_init_data *rpmh_data; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + int ret; + + vreg->dev = dev; + + for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++) + if (!strcmp(rpmh_data->name, node->name)) + break; + + if (!rpmh_data->name) { + dev_err(dev, "Unknown regulator %s\n", node->name); + return -EINVAL; + } + + scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name), + rpmh_data->resource_name, pmic_id); + + vreg->addr = cmd_db_read_addr(rpmh_resource_name); + if (!vreg->addr) { + dev_err(dev, "%s: could not find RPMh address for resource %s\n", + node->name, rpmh_resource_name); + return -ENODEV; + } + + vreg->rdesc.name = rpmh_data->name; + vreg->rdesc.supply_name = rpmh_data->supply_name; + vreg->hw_data = rpmh_data->hw_data; + + vreg->enabled = -EINVAL; + vreg->voltage_selector = -ENOTRECOVERABLE; + vreg->mode = REGULATOR_MODE_INVALID; + + if (rpmh_data->hw_data->n_voltages) { + vreg->rdesc.linear_ranges = &rpmh_data->hw_data->voltage_range; + vreg->rdesc.n_linear_ranges = 1; + vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages; + } + + vreg->always_wait_for_ack = of_property_read_bool(node, + "qcom,always-wait-for-ack"); + + vreg->rdesc.owner = THIS_MODULE; + vreg->rdesc.type = REGULATOR_VOLTAGE; + vreg->rdesc.ops = vreg->hw_data->ops; + vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode; + + init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc); + if (!init_data) + return -ENOMEM; + + if (rpmh_data->hw_data->regulator_type == XOB && + init_data->constraints.min_uV && + init_data->constraints.min_uV == init_data->constraints.max_uV) { + vreg->rdesc.fixed_uV = init_data->constraints.min_uV; + vreg->rdesc.n_voltages = 1; + } + + reg_config.dev = dev; + reg_config.init_data = init_data; + reg_config.of_node = node; + reg_config.driver_data = vreg; + + rdev = devm_regulator_register(dev, &vreg->rdesc, ®_config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "%s: devm_regulator_register() failed, ret=%d\n", + node->name, ret); + return ret; + } + + dev_dbg(dev, "%s regulator registered for RPMh resource %s @ 0x%05X\n", + node->name, rpmh_resource_name, vreg->addr); + + return 0; +} + +static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM, + [REGULATOR_MODE_NORMAL] = PMIC4_LDO_MODE_HPM, + [REGULATOR_MODE_FAST] = -EINVAL, +}; + +static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode) +{ + unsigned int mode; + + switch (rpmh_mode) { + case RPMH_REGULATOR_MODE_HPM: + mode = REGULATOR_MODE_NORMAL; + break; + case RPMH_REGULATOR_MODE_LPM: + mode = REGULATOR_MODE_IDLE; + break; + case RPMH_REGULATOR_MODE_RET: + mode = REGULATOR_MODE_STANDBY; + break; + default: + mode = REGULATOR_MODE_INVALID; + } + + return mode; +} + +static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM, +}; + +static unsigned int +rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode) +{ + unsigned int mode; + + switch (rpmh_mode) { + case RPMH_REGULATOR_MODE_HPM: + mode = REGULATOR_MODE_FAST; + break; + case RPMH_REGULATOR_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case RPMH_REGULATOR_MODE_LPM: + mode = REGULATOR_MODE_IDLE; + break; + case RPMH_REGULATOR_MODE_RET: + mode = REGULATOR_MODE_STANDBY; + break; + default: + mode = REGULATOR_MODE_INVALID; + } + + return mode; +} + +static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = -EINVAL, + [REGULATOR_MODE_IDLE] = PMIC4_BOB_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC4_BOB_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM, +}; + +static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode) +{ + unsigned int mode; + + switch (rpmh_mode) { + case RPMH_REGULATOR_MODE_HPM: + mode = REGULATOR_MODE_FAST; + break; + case RPMH_REGULATOR_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case RPMH_REGULATOR_MODE_LPM: + mode = REGULATOR_MODE_IDLE; + break; + default: + mode = REGULATOR_MODE_INVALID; + } + + return mode; +} + +static const struct rpmh_vreg_hw_data pmic4_pldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000), + .n_voltages = 256, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic4_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_pldo_lv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000), + .n_voltages = 128, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic4_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_nldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000), + .n_voltages = 128, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic4_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + .n_voltages = 216, + .pmic_mode_map = pmic_mode_map_pmic4_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000), + .n_voltages = 259, + .pmic_mode_map = pmic_mode_map_pmic4_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_bob = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_bypass_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000), + .n_voltages = 84, + .pmic_mode_map = pmic_mode_map_pmic4_bob, + .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_lvs = { + .regulator_type = XOB, + .ops = &rpmh_regulator_xob_ops, + /* LVS hardware does not support voltage or mode configuration. */ +}; + +#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ +{ \ + .name = _name, \ + .resource_name = _resource_name, \ + .hw_data = _hw_data, \ + .supply_name = _supply_name, \ +} + +static const struct rpmh_vreg_init_data pm8998_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic4_hfsmps3, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic4_ftsmps426, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic4_ftsmps426, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic4_ftsmps426, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic4_ftsmps426, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd-s12"), + RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd-s13"), + RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l27"), + RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l8-l17"), + RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l3-l11"), + RPMH_VREG("ldo4", "ldo%s4", &pmic4_nldo, "vdd-l4-l5"), + RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l4-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo8", "ldo%s8", &pmic4_nldo, "vdd-l2-l8-l17"), + RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo, "vdd-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo, "vdd-l10-l23-l25"), + RPMH_VREG("ldo11", "ldo%s11", &pmic4_nldo, "vdd-l3-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo, "vdd-l13-l19-l21"), + RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l16-l28"), + RPMH_VREG("ldo17", "ldo%s17", &pmic4_nldo, "vdd-l2-l8-l17"), + RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l18-l22"), + RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l13-l19-l21"), + RPMH_VREG("ldo20", "ldo%s20", &pmic4_pldo, "vdd-l20-l24"), + RPMH_VREG("ldo21", "ldo%s21", &pmic4_pldo, "vdd-l13-l19-l21"), + RPMH_VREG("ldo22", "ldo%s22", &pmic4_pldo, "vdd-l18-l22"), + RPMH_VREG("ldo23", "ldo%s23", &pmic4_pldo, "vdd-l10-l23-l25"), + RPMH_VREG("ldo24", "ldo%s24", &pmic4_pldo, "vdd-l20-l24"), + RPMH_VREG("ldo25", "ldo%s25", &pmic4_pldo, "vdd-l10-l23-l25"), + RPMH_VREG("ldo26", "ldo%s26", &pmic4_nldo, "vdd-l26"), + RPMH_VREG("ldo27", "ldo%s27", &pmic4_nldo, "vdd-l1-l27"), + RPMH_VREG("ldo28", "ldo%s28", &pmic4_pldo, "vdd-l16-l28"), + RPMH_VREG("lvs1", "vs%s1", &pmic4_lvs, "vin-lvs-1-2"), + RPMH_VREG("lvs2", "vs%s2", &pmic4_lvs, "vin-lvs-1-2"), + {}, +}; + +static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = { + RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8005_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic4_ftsmps426, "vdd-s4"), + {}, +}; + +static int rpmh_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct rpmh_vreg_init_data *vreg_data; + struct device_node *node; + struct rpmh_vreg *vreg; + const char *pmic_id; + int ret; + + vreg_data = of_device_get_match_data(dev); + if (!vreg_data) + return -ENODEV; + + ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id); + if (ret < 0) { + dev_err(dev, "qcom,pmic-id missing in DT node\n"); + return ret; + } + + for_each_available_child_of_node(dev->of_node, node) { + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + of_node_put(node); + return -ENOMEM; + } + + ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id, + vreg_data); + if (ret < 0) { + of_node_put(node); + return ret; + } + } + + return 0; +} + +static const struct of_device_id rpmh_regulator_match_table[] = { + { + .compatible = "qcom,pm8998-rpmh-regulators", + .data = pm8998_vreg_data, + }, + { + .compatible = "qcom,pmi8998-rpmh-regulators", + .data = pmi8998_vreg_data, + }, + { + .compatible = "qcom,pm8005-rpmh-regulators", + .data = pm8005_vreg_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); + +static struct platform_driver rpmh_regulator_driver = { + .driver = { + .name = "qcom-rpmh-regulator", + .of_match_table = of_match_ptr(rpmh_regulator_match_table), + }, + .probe = rpmh_regulator_probe, +}; +module_platform_driver(rpmh_regulator_driver); + +MODULE_DESCRIPTION("Qualcomm RPMh regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 9817f1a75342..53a61fb65642 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -1060,7 +1060,7 @@ static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data) #define SAW3_AVS_CTL_TGGL_MASK 0x8000000 #define SAW3_AVS_CTL_CLEAR_MASK 0x7efc00 -static struct regmap *saw_regmap = NULL; +static struct regmap *saw_regmap; static void spmi_saw_set_vdd(void *data) { @@ -1728,7 +1728,7 @@ static const struct spmi_regulator_data pmi8994_regulators[] = { { "s2", 0x1700, "vdd_s2", }, { "s3", 0x1a00, "vdd_s3", }, { "l1", 0x4000, "vdd_l1", }, - { } + { } }; static const struct of_device_id qcom_spmi_regulator_match[] = { @@ -1752,7 +1752,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) const char *name; struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; - struct device_node *syscon; + struct device_node *syscon, *reg_node; + struct property *reg_prop; int ret, lenp; struct list_head *vreg_list; @@ -1774,16 +1775,19 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) syscon = of_parse_phandle(node, "qcom,saw-reg", 0); saw_regmap = syscon_node_to_regmap(syscon); of_node_put(syscon); - if (IS_ERR(regmap)) + if (IS_ERR(saw_regmap)) dev_err(dev, "ERROR reading SAW regmap\n"); } for (reg = match->data; reg->name; reg++) { - if (saw_regmap && \ - of_find_property(of_find_node_by_name(node, reg->name), \ - "qcom,saw-slave", &lenp)) { - continue; + if (saw_regmap) { + reg_node = of_get_child_by_name(node, reg->name); + reg_prop = of_find_property(reg_node, "qcom,saw-slave", + &lenp); + of_node_put(reg_node); + if (reg_prop) + continue; } vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); @@ -1816,13 +1820,17 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) if (ret) continue; - if (saw_regmap && \ - of_find_property(of_find_node_by_name(node, reg->name), \ - "qcom,saw-leader", &lenp)) { - spmi_saw_ops = *(vreg->desc.ops); - spmi_saw_ops.set_voltage_sel = \ - spmi_regulator_saw_set_voltage; - vreg->desc.ops = &spmi_saw_ops; + if (saw_regmap) { + reg_node = of_get_child_by_name(node, reg->name); + reg_prop = of_find_property(reg_node, "qcom,saw-leader", + &lenp); + of_node_put(reg_node); + if (reg_prop) { + spmi_saw_ops = *(vreg->desc.ops); + spmi_saw_ops.set_voltage_sel = + spmi_regulator_saw_set_voltage; + vreg->desc.ops = &spmi_saw_ops; + } } config.dev = dev; diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index 48f0ca90743c..095d25f3d2ea 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -1,13 +1,7 @@ -/* - * Copyright (c) 2013 Samsung Electronics Co., Ltd - * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2013 Samsung Electronics Co., Ltd +// http://www.samsung.com #include <linux/bug.h> #include <linux/err.h> diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index d1207ec683db..5bb6f4ca48db 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -1,20 +1,7 @@ -/* - * s2mps11.c - * - * Copyright (c) 2012-2014 Samsung Electronics Co., Ltd - * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2012-2014 Samsung Electronics Co., Ltd +// http://www.samsung.com #include <linux/bug.h> #include <linux/err.h> diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 0cbc980753c2..667d16dc83ce 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -1,15 +1,7 @@ -/* - * s5m8767.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd - * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2011 Samsung Electronics Co., Ltd +// http://www.samsung.com #include <linux/err.h> #include <linux/of_gpio.h> diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index fc12badf3805..d84fab616abf 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -232,6 +232,8 @@ static int tps65217_regulator_probe(struct platform_device *pdev) tps->strobes = devm_kcalloc(&pdev->dev, TPS65217_NUM_REGULATOR, sizeof(u8), GFP_KERNEL); + if (!tps->strobes) + return -ENOMEM; platform_set_drvdata(pdev, tps); diff --git a/drivers/regulator/uniphier-regulator.c b/drivers/regulator/uniphier-regulator.c new file mode 100644 index 000000000000..abf22acbd13e --- /dev/null +++ b/drivers/regulator/uniphier-regulator.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Regulator controller driver for UniPhier SoC +// Copyright 2018 Socionext Inc. +// Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> +#include <linux/reset.h> + +#define MAX_CLKS 2 +#define MAX_RSTS 2 + +struct uniphier_regulator_soc_data { + int nclks; + const char * const *clock_names; + int nrsts; + const char * const *reset_names; + const struct regulator_desc *desc; + const struct regmap_config *regconf; +}; + +struct uniphier_regulator_priv { + struct clk_bulk_data clk[MAX_CLKS]; + struct reset_control *rst[MAX_RSTS]; + const struct uniphier_regulator_soc_data *data; +}; + +static struct regulator_ops uniphier_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static int uniphier_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_regulator_priv *priv; + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct regmap *regmap; + struct resource *res; + void __iomem *base; + const char *name; + int i, ret, nr; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->data = of_device_get_match_data(dev); + if (WARN_ON(!priv->data)) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + for (i = 0; i < priv->data->nclks; i++) + priv->clk[i].id = priv->data->clock_names[i]; + ret = devm_clk_bulk_get(dev, priv->data->nclks, priv->clk); + if (ret) + return ret; + + for (i = 0; i < priv->data->nrsts; i++) { + name = priv->data->reset_names[i]; + priv->rst[i] = devm_reset_control_get_shared(dev, name); + if (IS_ERR(priv->rst[i])) + return PTR_ERR(priv->rst[i]); + } + + ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk); + if (ret) + return ret; + + for (nr = 0; nr < priv->data->nrsts; nr++) { + ret = reset_control_deassert(priv->rst[nr]); + if (ret) + goto out_rst_assert; + } + + regmap = devm_regmap_init_mmio(dev, base, priv->data->regconf); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + config.dev = dev; + config.driver_data = priv; + config.of_node = dev->of_node; + config.regmap = regmap; + config.init_data = of_get_regulator_init_data(dev, dev->of_node, + priv->data->desc); + rdev = devm_regulator_register(dev, priv->data->desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + goto out_rst_assert; + } + + platform_set_drvdata(pdev, priv); + + return 0; + +out_rst_assert: + while (nr--) + reset_control_assert(priv->rst[nr]); + + clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); + + return ret; +} + +static int uniphier_regulator_remove(struct platform_device *pdev) +{ + struct uniphier_regulator_priv *priv = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < priv->data->nrsts; i++) + reset_control_assert(priv->rst[i]); + + clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); + + return 0; +} + +/* USB3 controller data */ +#define USB3VBUS_OFFSET 0x0 +#define USB3VBUS_REG BIT(4) +#define USB3VBUS_REG_EN BIT(3) +static const struct regulator_desc uniphier_usb3_regulator_desc = { + .name = "vbus", + .of_match = of_match_ptr("vbus"), + .ops = &uniphier_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = USB3VBUS_OFFSET, + .enable_mask = USB3VBUS_REG_EN | USB3VBUS_REG, + .enable_val = USB3VBUS_REG_EN | USB3VBUS_REG, + .disable_val = USB3VBUS_REG_EN, +}; + +static const struct regmap_config uniphier_usb3_regulator_regconf = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 1, +}; + +static const char * const uniphier_pro4_clock_reset_names[] = { + "gio", "link", +}; + +static const struct uniphier_regulator_soc_data uniphier_pro4_usb3_data = { + .nclks = ARRAY_SIZE(uniphier_pro4_clock_reset_names), + .clock_names = uniphier_pro4_clock_reset_names, + .nrsts = ARRAY_SIZE(uniphier_pro4_clock_reset_names), + .reset_names = uniphier_pro4_clock_reset_names, + .desc = &uniphier_usb3_regulator_desc, + .regconf = &uniphier_usb3_regulator_regconf, +}; + +static const char * const uniphier_pxs2_clock_reset_names[] = { + "link", +}; + +static const struct uniphier_regulator_soc_data uniphier_pxs2_usb3_data = { + .nclks = ARRAY_SIZE(uniphier_pxs2_clock_reset_names), + .clock_names = uniphier_pxs2_clock_reset_names, + .nrsts = ARRAY_SIZE(uniphier_pxs2_clock_reset_names), + .reset_names = uniphier_pxs2_clock_reset_names, + .desc = &uniphier_usb3_regulator_desc, + .regconf = &uniphier_usb3_regulator_regconf, +}; + +static const struct of_device_id uniphier_regulator_match[] = { + /* USB VBUS */ + { + .compatible = "socionext,uniphier-pro4-usb3-regulator", + .data = &uniphier_pro4_usb3_data, + }, + { + .compatible = "socionext,uniphier-pxs2-usb3-regulator", + .data = &uniphier_pxs2_usb3_data, + }, + { + .compatible = "socionext,uniphier-ld20-usb3-regulator", + .data = &uniphier_pxs2_usb3_data, + }, + { + .compatible = "socionext,uniphier-pxs3-usb3-regulator", + .data = &uniphier_pxs2_usb3_data, + }, + { /* Sentinel */ }, +}; + +static struct platform_driver uniphier_regulator_driver = { + .probe = uniphier_regulator_probe, + .remove = uniphier_regulator_remove, + .driver = { + .name = "uniphier-regulator", + .of_match_table = uniphier_regulator_match, + }, +}; +module_platform_driver(uniphier_regulator_driver); + +MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); +MODULE_DESCRIPTION("UniPhier Regulator Controller Driver"); +MODULE_LICENSE("GPL"); |