diff options
Diffstat (limited to 'drivers/regulator')
40 files changed, 2751 insertions, 304 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8df0b0e62976..39b422b97ec0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -78,6 +78,15 @@ config REGULATOR_ACT8865 This driver controls a active-semi act8865 voltage output regulator via I2C bus. +config REGULATOR_ACT8945A + tristate "Active-semi ACT8945A voltage regulator" + depends on MFD_ACT8945A + help + This driver controls a active-semi ACT8945A voltage regulator + via I2C bus. The ACT8945A features three step-down DC/DC converters + and four low-dropout linear regulators, along with a ActivePath + battery charger. + config REGULATOR_AD5398 tristate "Analog Devices AD5398/AD5821 regulators" depends on I2C @@ -261,6 +270,14 @@ config REGULATOR_HI6421 21 general purpose LDOs, 3 dedicated LDOs, and 5 BUCKs. All of them come with support to either ECO (idle) or sleep mode. +config REGULATOR_HI655X + tristate "Hisilicon HI655X PMIC regulators support" + depends on ARCH_HISI || COMPILE_TEST + depends on MFD_HI655X_PMIC && OF + help + This driver provides support for the voltage regulators of the + Hisilicon Hi655x PMIC device. + config REGULATOR_ISL9305 tristate "Intersil ISL9305 regulator" depends on I2C @@ -274,6 +291,15 @@ config REGULATOR_ISL6271A help This driver supports ISL6271A voltage regulator chip. +config REGULATOR_LM363X + tristate "TI LM363X voltage regulators" + depends on MFD_TI_LMU + help + This driver supports LM3631 and LM3632 voltage regulators for + the LCD bias. + One boost output voltage is configurable and always on. + Other LDOs are used for the display module. + config REGULATOR_LP3971 tristate "National Semiconductors LP3971 PMIC regulator driver" depends on I2C @@ -446,6 +472,7 @@ config REGULATOR_MC13892 config REGULATOR_MT6311 tristate "MediaTek MT6311 PMIC" depends on I2C + select REGMAP_I2C help Say y here to select this option to enable the power regulator of MediaTek MT6311 PMIC. @@ -504,6 +531,22 @@ config REGULATOR_PFUZE100 Say y here to support the regulators found on the Freescale PFUZE100/PFUZE200 PMIC. +config REGULATOR_PV88060 + tristate "Powerventure Semiconductor PV88060 regulator" + depends on I2C + select REGMAP_I2C + help + Say y here to support the voltage regulators and convertors + PV88060 + +config REGULATOR_PV88090 + tristate "Powerventure Semiconductor PV88090 regulator" + depends on I2C + select REGMAP_I2C + help + Say y here to support the voltage regulators and convertors + on PV88090 + config REGULATOR_PWM tristate "PWM voltage regulator" depends on PWM @@ -588,10 +631,10 @@ config REGULATOR_S2MPA01 via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. config REGULATOR_S2MPS11 - tristate "Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage regulator" + tristate "Samsung S2MPS11/13/14/15/S2MPU02 voltage regulator" depends on MFD_SEC_CORE help - This driver supports a Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage + This driver supports a Samsung S2MPS11/13/14/15/S2MPU02 voltage output regulator via I2C bus. The chip is comprised of high efficient Buck converters including Dual-Phase Buck converter, Buck-Boost converter, various LDOs. @@ -680,6 +723,13 @@ config REGULATOR_TPS6507X three step-down converters and two general-purpose LDO voltage regulators. It supports TI's software based Class-2 SmartReflex implementation. +config REGULATOR_TPS65086 + tristate "TI TPS65086 Power regulators" + depends on MFD_TPS65086 + help + This driver provides support for the voltage regulators on + TI TPS65086 PMICs. + config REGULATOR_TPS65090 tristate "TI TPS65090 Power regulator" depends on MFD_TPS65090 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 0f8174913c17..bba7b7093bac 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o +obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o @@ -34,8 +35,10 @@ obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o +obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o +obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o @@ -66,6 +69,8 @@ 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 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o +obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o +obj-$(CONFIG_REGULATOR_PV88090) += pv88090-regulator.o obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o @@ -85,6 +90,7 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o +obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o obj-$(CONFIG_REGULATOR_TPS65217) += tps65217-regulator.o obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index f8d4cd3d1397..000d566e32a4 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -218,7 +218,7 @@ static const struct regulator_desc act8600_regulators[] = { .ops = &act8865_ldo_ops, .type = REGULATOR_VOLTAGE, .n_voltages = 1, - .fixed_uV = 1800000, + .fixed_uV = 3300000, .enable_reg = ACT8600_LDO910_CTRL, .enable_mask = ACT8865_ENA, .owner = THIS_MODULE, @@ -369,7 +369,7 @@ static int act8865_pdata_from_dt(struct device *dev, for (i = 0; i < num_matches; i++) { regulator->id = i; regulator->name = matches[i].name; - regulator->platform_data = matches[i].init_data; + regulator->init_data = matches[i].init_data; of_node[i] = matches[i].of_node; regulator++; } @@ -396,7 +396,7 @@ static struct regulator_init_data for (i = 0; i < pdata->num_regulators; i++) { if (pdata->regulators[i].id == id) - return pdata->regulators[i].platform_data; + return pdata->regulators[i].init_data; } return NULL; @@ -415,7 +415,7 @@ static void act8865_power_off(void) static int act8865_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { - static const struct regulator_desc *regulators; + const struct regulator_desc *regulators; struct act8865_platform_data pdata_of, *pdata; struct device *dev = &client->dev; struct device_node **of_node; diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c new file mode 100644 index 000000000000..441864b9fece --- /dev/null +++ b/drivers/regulator/act8945a-regulator.c @@ -0,0 +1,165 @@ +/* + * Voltage regulation driver for active-semi ACT8945A PMIC + * + * Copyright (C) 2015 Atmel Corporation + * + * Author: Wenyou Yang <wenyou.yang@atmel.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. + * + */ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +/** + * ACT8945A Global Register Map. + */ +#define ACT8945A_SYS_MODE 0x00 +#define ACT8945A_SYS_CTRL 0x01 +#define ACT8945A_DCDC1_VSET1 0x20 +#define ACT8945A_DCDC1_VSET2 0x21 +#define ACT8945A_DCDC1_CTRL 0x22 +#define ACT8945A_DCDC2_VSET1 0x30 +#define ACT8945A_DCDC2_VSET2 0x31 +#define ACT8945A_DCDC2_CTRL 0x32 +#define ACT8945A_DCDC3_VSET1 0x40 +#define ACT8945A_DCDC3_VSET2 0x41 +#define ACT8945A_DCDC3_CTRL 0x42 +#define ACT8945A_LDO1_VSET 0x50 +#define ACT8945A_LDO1_CTRL 0x51 +#define ACT8945A_LDO2_VSET 0x54 +#define ACT8945A_LDO2_CTRL 0x55 +#define ACT8945A_LDO3_VSET 0x60 +#define ACT8945A_LDO3_CTRL 0x61 +#define ACT8945A_LDO4_VSET 0x64 +#define ACT8945A_LDO4_CTRL 0x65 + +/** + * Field Definitions. + */ +#define ACT8945A_ENA 0x80 /* ON - [7] */ +#define ACT8945A_VSEL_MASK 0x3F /* VSET - [5:0] */ + +/** + * ACT8945A Voltage Number + */ +#define ACT8945A_VOLTAGE_NUM 64 + +enum { + ACT8945A_ID_DCDC1, + ACT8945A_ID_DCDC2, + ACT8945A_ID_DCDC3, + ACT8945A_ID_LDO1, + ACT8945A_ID_LDO2, + ACT8945A_ID_LDO3, + ACT8945A_ID_LDO4, + ACT8945A_REG_NUM, +}; + +static const struct regulator_linear_range act8945a_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000), + REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000), + REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000), +}; + +static struct regulator_ops act8945a_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply) \ + [_family##_ID_##_id] = { \ + .name = _name, \ + .supply_name = _supply, \ + .of_match = of_match_ptr("REG_"#_id), \ + .regulators_node = of_match_ptr("regulators"), \ + .id = _family##_ID_##_id, \ + .type = REGULATOR_VOLTAGE, \ + .ops = &act8945a_ops, \ + .n_voltages = ACT8945A_VOLTAGE_NUM, \ + .linear_ranges = act8945a_voltage_ranges, \ + .n_linear_ranges = ARRAY_SIZE(act8945a_voltage_ranges), \ + .vsel_reg = _family##_##_id##_##_vsel_reg, \ + .vsel_mask = ACT8945A_VSEL_MASK, \ + .enable_reg = _family##_##_id##_CTRL, \ + .enable_mask = ACT8945A_ENA, \ + .owner = THIS_MODULE, \ + } + +static const struct regulator_desc act8945a_regulators[] = { + ACT89xx_REG("DCDC_REG1", ACT8945A, DCDC1, VSET1, "vp1"), + ACT89xx_REG("DCDC_REG2", ACT8945A, DCDC2, VSET1, "vp2"), + ACT89xx_REG("DCDC_REG3", ACT8945A, DCDC3, VSET1, "vp3"), + ACT89xx_REG("LDO_REG1", ACT8945A, LDO1, VSET, "inl45"), + ACT89xx_REG("LDO_REG2", ACT8945A, LDO2, VSET, "inl45"), + ACT89xx_REG("LDO_REG3", ACT8945A, LDO3, VSET, "inl67"), + ACT89xx_REG("LDO_REG4", ACT8945A, LDO4, VSET, "inl67"), +}; + +static const struct regulator_desc act8945a_alt_regulators[] = { + ACT89xx_REG("DCDC_REG1", ACT8945A, DCDC1, VSET2, "vp1"), + ACT89xx_REG("DCDC_REG2", ACT8945A, DCDC2, VSET2, "vp2"), + ACT89xx_REG("DCDC_REG3", ACT8945A, DCDC3, VSET2, "vp3"), + ACT89xx_REG("LDO_REG1", ACT8945A, LDO1, VSET, "inl45"), + ACT89xx_REG("LDO_REG2", ACT8945A, LDO2, VSET, "inl45"), + ACT89xx_REG("LDO_REG3", ACT8945A, LDO3, VSET, "inl67"), + ACT89xx_REG("LDO_REG4", ACT8945A, LDO4, VSET, "inl67"), +}; + +static int act8945a_pmic_probe(struct platform_device *pdev) +{ + struct regulator_config config = { }; + const struct regulator_desc *regulators; + struct regulator_dev *rdev; + int i, num_regulators; + bool voltage_select; + + voltage_select = of_property_read_bool(pdev->dev.parent->of_node, + "active-semi,vsel-high"); + + if (voltage_select) { + regulators = act8945a_alt_regulators; + num_regulators = ARRAY_SIZE(act8945a_alt_regulators); + } else { + regulators = act8945a_regulators; + num_regulators = ARRAY_SIZE(act8945a_regulators); + } + + config.dev = &pdev->dev; + config.dev->of_node = pdev->dev.parent->of_node; + for (i = 0; i < num_regulators; i++) { + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "failed to register %s regulator\n", + regulators[i].name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver act8945a_pmic_driver = { + .driver = { + .name = "act8945a-regulator", + }, + .probe = act8945a_pmic_probe, +}; +module_platform_driver(act8945a_pmic_driver); + +MODULE_DESCRIPTION("Active-semi ACT8945A voltage regulator driver"); +MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index ea50a886ba63..8b0f788a9bbb 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -58,10 +58,12 @@ static int ad5398_write_reg(struct i2c_client *client, const unsigned short data val = cpu_to_be16(data); ret = i2c_master_send(client, (char *)&val, 2); - if (ret < 0) + if (ret != 2) { dev_err(&client->dev, "I2C write error\n"); + return ret < 0 ? ret : -EIO; + } - return ret; + return 0; } static int ad5398_get_current_limit(struct regulator_dev *rdev) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 35de22fdb7a0..214e815e98eb 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -27,8 +27,8 @@ #define AXP20X_IO_ENABLED 0x03 #define AXP20X_IO_DISABLED 0x07 -#define AXP22X_IO_ENABLED 0x04 -#define AXP22X_IO_DISABLED 0x03 +#define AXP22X_IO_ENABLED 0x03 +#define AXP22X_IO_DISABLED 0x04 #define AXP20X_WORKMODE_DCDC2_MASK BIT(2) #define AXP20X_WORKMODE_DCDC3_MASK BIT(1) @@ -39,7 +39,7 @@ #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ _vmask, _ereg, _emask, _enable_val, _disable_val) \ [_family##_##_id] = { \ - .name = #_id, \ + .name = (_match), \ .supply_name = (_supply), \ .of_match = of_match_ptr(_match), \ .regulators_node = of_match_ptr("regulators"), \ @@ -61,7 +61,7 @@ #define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ _vmask, _ereg, _emask) \ [_family##_##_id] = { \ - .name = #_id, \ + .name = (_match), \ .supply_name = (_supply), \ .of_match = of_match_ptr(_match), \ .regulators_node = of_match_ptr("regulators"), \ @@ -78,21 +78,15 @@ .ops = &axp20x_ops, \ } -#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ - _vmask, _ereg, _emask) \ +#define AXP_DESC_SW(_family, _id, _match, _supply, _ereg, _emask) \ [_family##_##_id] = { \ - .name = #_id, \ + .name = (_match), \ .supply_name = (_supply), \ .of_match = of_match_ptr(_match), \ .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = _family##_##_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_sw, \ @@ -100,7 +94,7 @@ #define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \ [_family##_##_id] = { \ - .name = #_id, \ + .name = (_match), \ .supply_name = (_supply), \ .of_match = of_match_ptr(_match), \ .regulators_node = of_match_ptr("regulators"), \ @@ -112,39 +106,34 @@ .ops = &axp20x_ops_fixed \ } -#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask, \ - _ereg, _emask) \ +#define AXP_DESC_RANGES(_family, _id, _match, _supply, _ranges, _n_voltages, \ + _vreg, _vmask, _ereg, _emask) \ [_family##_##_id] = { \ - .name = #_id, \ + .name = (_match), \ .supply_name = (_supply), \ .of_match = of_match_ptr(_match), \ .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = _family##_##_id, \ - .n_voltages = ARRAY_SIZE(_table), \ + .n_voltages = (_n_voltages), \ .owner = THIS_MODULE, \ .vsel_reg = (_vreg), \ .vsel_mask = (_vmask), \ .enable_reg = (_ereg), \ .enable_mask = (_emask), \ - .volt_table = (_table), \ - .ops = &axp20x_ops_table, \ + .linear_ranges = (_ranges), \ + .n_linear_ranges = ARRAY_SIZE(_ranges), \ + .ops = &axp20x_ops_range, \ } -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 = { +static struct regulator_ops axp20x_ops_range = { .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, + .list_voltage = regulator_list_voltage_linear_range, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -160,13 +149,17 @@ static struct regulator_ops axp20x_ops = { }; static struct regulator_ops axp20x_ops_sw = { - .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_linear_range axp20x_ldo4_ranges[] = { + REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0), + REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000), + REGULATOR_LINEAR_RANGE(2500000, 0x9, 0xf, 100000), +}; + static const struct regulator_desc axp20x_regulators[] = { AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), @@ -177,8 +170,9 @@ static const struct regulator_desc axp20x_regulators[] = { AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), - AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data, - AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08), + AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges, + 16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, + 0x08), AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), @@ -196,8 +190,8 @@ static const struct regulator_desc axp22x_regulators[] = { AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)), + AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, + BIT(7)), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 73b7683355cd..1cff11205642 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -132,24 +132,24 @@ static bool have_full_constraints(void) return has_full_constraints || of_have_populated_dt(); } +static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) +{ + if (rdev && rdev->supply) + return rdev->supply->rdev; + + return NULL; +} + /** * regulator_lock_supply - lock a regulator and its supplies * @rdev: regulator source */ static void regulator_lock_supply(struct regulator_dev *rdev) { - struct regulator *supply; - int i = 0; - - while (1) { - mutex_lock_nested(&rdev->mutex, i++); - supply = rdev->supply; - - if (!rdev->supply) - return; + int i; - rdev = supply->rdev; - } + for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) + mutex_lock_nested(&rdev->mutex, i); } /** @@ -1057,18 +1057,18 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = machine_constraints_voltage(rdev, rdev->constraints); if (ret != 0) - goto out; + return ret; ret = machine_constraints_current(rdev, rdev->constraints); if (ret != 0) - goto out; + return ret; if (rdev->constraints->ilim_uA && ops->set_input_current_limit) { ret = ops->set_input_current_limit(rdev, rdev->constraints->ilim_uA); if (ret < 0) { rdev_err(rdev, "failed to set input limit\n"); - goto out; + return ret; } } @@ -1077,21 +1077,20 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = suspend_prepare(rdev, rdev->constraints->initial_state); if (ret < 0) { rdev_err(rdev, "failed to set suspend state\n"); - goto out; + return ret; } } if (rdev->constraints->initial_mode) { if (!ops->set_mode) { rdev_err(rdev, "no set_mode operation\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } ret = ops->set_mode(rdev, rdev->constraints->initial_mode); if (ret < 0) { rdev_err(rdev, "failed to set initial mode: %d\n", ret); - goto out; + return ret; } } @@ -1102,7 +1101,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = _regulator_do_enable(rdev); if (ret < 0 && ret != -EINVAL) { rdev_err(rdev, "failed to enable\n"); - goto out; + return ret; } } @@ -1111,7 +1110,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); if (ret < 0) { rdev_err(rdev, "failed to set ramp_delay\n"); - goto out; + return ret; } } @@ -1119,7 +1118,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_pull_down(rdev); if (ret < 0) { rdev_err(rdev, "failed to set pull down\n"); - goto out; + return ret; } } @@ -1127,7 +1126,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_soft_start(rdev); if (ret < 0) { rdev_err(rdev, "failed to set soft start\n"); - goto out; + return ret; } } @@ -1136,16 +1135,23 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->set_over_current_protection(rdev); if (ret < 0) { rdev_err(rdev, "failed to set over current protection\n"); - goto out; + return ret; + } + } + + if (rdev->constraints->active_discharge && ops->set_active_discharge) { + bool ad_state = (rdev->constraints->active_discharge == + REGULATOR_ACTIVE_DISCHARGE_ENABLE) ? true : false; + + ret = ops->set_active_discharge(rdev, ad_state); + if (ret < 0) { + rdev_err(rdev, "failed to set active discharge\n"); + return ret; } } print_constraints(rdev); return 0; -out: - kfree(rdev->constraints); - rdev->constraints = NULL; - return ret; } /** @@ -2368,7 +2374,6 @@ static void regulator_disable_work(struct work_struct *work) int regulator_disable_deferred(struct regulator *regulator, int ms) { struct regulator_dev *rdev = regulator->rdev; - int ret; if (regulator->always_on) return 0; @@ -2380,13 +2385,9 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) rdev->deferred_disables++; mutex_unlock(&rdev->mutex); - ret = queue_delayed_work(system_power_efficient_wq, - &rdev->disable_work, - msecs_to_jiffies(ms)); - if (ret < 0) - return ret; - else - return 0; + queue_delayed_work(system_power_efficient_wq, &rdev->disable_work, + msecs_to_jiffies(ms)); + return 0; } EXPORT_SYMBOL_GPL(regulator_disable_deferred); @@ -3451,8 +3452,10 @@ int regulator_bulk_get(struct device *dev, int num_consumers, consumers[i].consumer = NULL; for (i = 0; i < num_consumers; i++) { - consumers[i].consumer = regulator_get(dev, - consumers[i].supply); + consumers[i].consumer = _regulator_get(dev, + consumers[i].supply, + false, + !consumers[i].optional); if (IS_ERR(consumers[i].consumer)) { ret = PTR_ERR(consumers[i].consumer); dev_err(dev, "Failed to get supply '%s': %d\n", @@ -3708,7 +3711,7 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { struct device *dev = kobj_to_dev(kobj); - struct regulator_dev *rdev = container_of(dev, struct regulator_dev, dev); + struct regulator_dev *rdev = dev_to_rdev(dev); const struct regulator_ops *ops = rdev->desc->ops; umode_t mode = attr->mode; @@ -3921,6 +3924,16 @@ regulator_register(const struct regulator_desc *regulator_desc, goto clean; } + if ((config->ena_gpio || config->ena_gpio_initialized) && + gpio_is_valid(config->ena_gpio)) { + ret = regulator_ena_gpio_request(rdev, config); + if (ret != 0) { + rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", + config->ena_gpio, ret); + goto clean; + } + } + /* register with sysfs */ rdev->dev.class = ®ulator_class; rdev->dev.parent = dev; @@ -3929,21 +3942,11 @@ regulator_register(const struct regulator_desc *regulator_desc, ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); - goto clean; + goto wash; } dev_set_drvdata(&rdev->dev, rdev); - if ((config->ena_gpio || config->ena_gpio_initialized) && - gpio_is_valid(config->ena_gpio)) { - ret = regulator_ena_gpio_request(rdev, config); - if (ret != 0) { - rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", - config->ena_gpio, ret); - goto wash; - } - } - /* set regulator constraints */ if (init_data) constraints = &init_data->constraints; @@ -3982,13 +3985,13 @@ unset_supplies: scrub: regulator_ena_gpio_free(rdev); - kfree(rdev->constraints); -wash: device_unregister(&rdev->dev); /* device core frees rdev */ rdev = ERR_PTR(ret); goto out; +wash: + regulator_ena_gpio_free(rdev); clean: kfree(rdev); rdev = ERR_PTR(ret); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index affa1b191314..33e8f3b8d2bd 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -257,7 +257,7 @@ static const struct regulator_linear_range da9034_ldo12_ranges[] = { REGULATOR_LINEAR_RANGE(2700000, 8, 15, 50000), }; -static struct regulator_ops da903x_regulator_ldo_ops = { +static const struct regulator_ops da903x_regulator_ldo_ops = { .set_voltage_sel = da903x_set_voltage_sel, .get_voltage_sel = da903x_get_voltage_sel, .list_voltage = regulator_list_voltage_linear, @@ -268,7 +268,7 @@ static struct regulator_ops da903x_regulator_ldo_ops = { }; /* NOTE: this is dedicated for the insane DA9030 LDO14 */ -static struct regulator_ops da9030_regulator_ldo14_ops = { +static const struct regulator_ops da9030_regulator_ldo14_ops = { .set_voltage_sel = da903x_set_voltage_sel, .get_voltage_sel = da903x_get_voltage_sel, .list_voltage = da9030_list_ldo14_voltage, @@ -279,7 +279,7 @@ static struct regulator_ops da9030_regulator_ldo14_ops = { }; /* NOTE: this is dedicated for the DA9030 LDO1 and LDO15 that have locks */ -static struct regulator_ops da9030_regulator_ldo1_15_ops = { +static const struct regulator_ops da9030_regulator_ldo1_15_ops = { .set_voltage_sel = da9030_set_ldo1_15_voltage_sel, .get_voltage_sel = da903x_get_voltage_sel, .list_voltage = regulator_list_voltage_linear, @@ -289,7 +289,7 @@ static struct regulator_ops da9030_regulator_ldo1_15_ops = { .is_enabled = da903x_is_enabled, }; -static struct regulator_ops da9034_regulator_dvc_ops = { +static const struct regulator_ops da9034_regulator_dvc_ops = { .set_voltage_sel = da9034_set_dvc_voltage_sel, .get_voltage_sel = da903x_get_voltage_sel, .list_voltage = regulator_list_voltage_linear, @@ -300,7 +300,7 @@ static struct regulator_ops da9034_regulator_dvc_ops = { }; /* NOTE: this is dedicated for the insane LDO12 */ -static struct regulator_ops da9034_regulator_ldo12_ops = { +static const struct regulator_ops da9034_regulator_ldo12_ops = { .set_voltage_sel = da903x_set_voltage_sel, .get_voltage_sel = da903x_get_voltage_sel, .list_voltage = regulator_list_voltage_linear_range, diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 12a25b40e473..1050cb77561a 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -265,7 +265,7 @@ static int da9052_regulator_set_voltage_time_sel(struct regulator_dev *rdev, return ret; } -static struct regulator_ops da9052_dcdc_ops = { +static const struct regulator_ops da9052_dcdc_ops = { .get_current_limit = da9052_dcdc_get_current_limit, .set_current_limit = da9052_dcdc_set_current_limit, @@ -279,7 +279,7 @@ static struct regulator_ops da9052_dcdc_ops = { .disable = regulator_disable_regmap, }; -static struct regulator_ops da9052_ldo_ops = { +static const struct regulator_ops da9052_ldo_ops = { .list_voltage = da9052_list_voltage, .map_voltage = da9052_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c index cafdafbffcaf..d029c941a1e1 100644 --- a/drivers/regulator/da9055-regulator.c +++ b/drivers/regulator/da9055-regulator.c @@ -324,7 +324,7 @@ static int da9055_suspend_disable(struct regulator_dev *rdev) return 0; } -static struct regulator_ops da9055_buck_ops = { +static const struct regulator_ops da9055_buck_ops = { .get_mode = da9055_buck_get_mode, .set_mode = da9055_buck_set_mode, @@ -345,7 +345,7 @@ static struct regulator_ops da9055_buck_ops = { .set_suspend_mode = da9055_buck_set_mode, }; -static struct regulator_ops da9055_ldo_ops = { +static const struct regulator_ops da9055_ldo_ops = { .get_mode = da9055_ldo_get_mode, .set_mode = da9055_ldo_set_mode, diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c index 5638fe8d759d..0638c8b40521 100644 --- a/drivers/regulator/da9062-regulator.c +++ b/drivers/regulator/da9062-regulator.c @@ -371,7 +371,7 @@ static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev, return regmap_field_write(regl->suspend_sleep, val); } -static struct regulator_ops da9062_buck_ops = { +static const struct regulator_ops da9062_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -389,7 +389,7 @@ static struct regulator_ops da9062_buck_ops = { .set_suspend_mode = da9062_buck_set_suspend_mode, }; -static struct regulator_ops da9062_ldo_ops = { +static const struct regulator_ops da9062_ldo_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 536e931eb921..ed9e7e96f877 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -427,7 +427,7 @@ static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode return regmap_field_write(regl->suspend_sleep, val); } -static struct regulator_ops da9063_buck_ops = { +static const struct regulator_ops da9063_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -445,7 +445,7 @@ static struct regulator_ops da9063_buck_ops = { .set_suspend_mode = da9063_buck_set_suspend_mode, }; -static struct regulator_ops da9063_ldo_ops = { +static const struct regulator_ops da9063_ldo_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index b3517830edb6..01c0e3709b66 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -46,7 +46,7 @@ static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA); static int da9210_get_current_limit(struct regulator_dev *rdev); -static struct regulator_ops da9210_buck_ops = { +static const struct regulator_ops da9210_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -132,6 +132,8 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) if (error < 0) goto error_i2c; + mutex_lock(&chip->rdev->mutex); + if (val & DA9210_E_OVCURR) { regulator_notifier_call_chain(chip->rdev, REGULATOR_EVENT_OVER_CURRENT, @@ -155,6 +157,9 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) NULL); handled |= DA9210_E_VMAX; } + + mutex_unlock(&chip->rdev->mutex); + if (handled) { /* Clear handled events */ error = regmap_write(chip->regmap, DA9210_REG_EVENT_B, handled); diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 04ef65b7eb3d..236abf473db5 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -219,7 +219,7 @@ static int da9211_get_current_limit(struct regulator_dev *rdev) return current_limits[data]; } -static struct regulator_ops da9211_buck_ops = { +static const struct regulator_ops da9211_buck_ops = { .get_mode = da9211_buck_get_mode, .set_mode = da9211_buck_set_mode, .enable = regulator_enable_regmap, diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 6ec1d400adae..6ad8ab4c578d 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -164,8 +164,11 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, consumers[i].consumer = NULL; for (i = 0; i < num_consumers; i++) { - consumers[i].consumer = devm_regulator_get(dev, - consumers[i].supply); + consumers[i].consumer = _devm_regulator_get(dev, + consumers[i].supply, + consumers[i].optional ? + OPTIONAL_GET : + NORMAL_GET); if (IS_ERR(consumers[i].consumer)) { ret = PTR_ERR(consumers[i].consumer); dev_err(dev, "Failed to get supply '%s': %d\n", diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 7bba8b747f30..a8718e98674a 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -283,8 +283,10 @@ static int gpio_regulator_probe(struct platform_device *pdev) drvdata->nr_gpios = config->nr_gpios; ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator setting GPIOs: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Could not obtain regulator setting GPIOs: %d\n", + ret); goto err_memstate; } } diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 3bbb32680a94..b1e32e7482e9 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -465,3 +465,26 @@ int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) return 0; } EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap); + +/** + * regulator_set_active_discharge_regmap - Default set_active_discharge() + * using regmap + * + * @rdev: device to operate on. + * @enable: state to set, 0 to disable and 1 to enable. + */ +int regulator_set_active_discharge_regmap(struct regulator_dev *rdev, + bool enable) +{ + unsigned int val; + + if (enable) + val = rdev->desc->active_discharge_on; + else + val = rdev->desc->active_discharge_off; + + return regmap_update_bits(rdev->regmap, + rdev->desc->active_discharge_reg, + rdev->desc->active_discharge_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_active_discharge_regmap); diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c new file mode 100644 index 000000000000..aca18466f522 --- /dev/null +++ b/drivers/regulator/hi655x-regulator.c @@ -0,0 +1,227 @@ +/* + * Device driver for regulators in Hi655x IC + * + * Copyright (c) 2016 Hisilicon. + * + * Authors: + * Chen Feng <puck.chen@hisilicon.com> + * Fei Wang <w.f@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/mfd/hi655x-pmic.h> + +struct hi655x_regulator { + unsigned int disable_reg; + unsigned int status_reg; + unsigned int ctrl_regs; + unsigned int ctrl_mask; + struct regulator_desc rdesc; +}; + +/* LDO7 & LDO10 */ +static const unsigned int ldo7_voltages[] = { + 1800000, 1850000, 2850000, 2900000, + 3000000, 3100000, 3200000, 3300000, +}; + +static const unsigned int ldo19_voltages[] = { + 1800000, 1850000, 1900000, 1750000, + 2800000, 2850000, 2900000, 3000000, +}; + +static const unsigned int ldo22_voltages[] = { + 900000, 1000000, 1050000, 1100000, + 1150000, 1175000, 1185000, 1200000, +}; + +enum hi655x_regulator_id { + HI655X_LDO0, + HI655X_LDO1, + HI655X_LDO2, + HI655X_LDO3, + HI655X_LDO4, + HI655X_LDO5, + HI655X_LDO6, + HI655X_LDO7, + HI655X_LDO8, + HI655X_LDO9, + HI655X_LDO10, + HI655X_LDO11, + HI655X_LDO12, + HI655X_LDO13, + HI655X_LDO14, + HI655X_LDO15, + HI655X_LDO16, + HI655X_LDO17, + HI655X_LDO18, + HI655X_LDO19, + HI655X_LDO20, + HI655X_LDO21, + HI655X_LDO22, +}; + +static int hi655x_is_enabled(struct regulator_dev *rdev) +{ + unsigned int value = 0; + + struct hi655x_regulator *regulator = rdev_get_drvdata(rdev); + + regmap_read(rdev->regmap, regulator->status_reg, &value); + return (value & BIT(regulator->ctrl_mask)); +} + +static int hi655x_disable(struct regulator_dev *rdev) +{ + int ret = 0; + + struct hi655x_regulator *regulator = rdev_get_drvdata(rdev); + + ret = regmap_write(rdev->regmap, regulator->disable_reg, + BIT(regulator->ctrl_mask)); + return ret; +} + +static struct regulator_ops hi655x_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = hi655x_disable, + .is_enabled = hi655x_is_enabled, + .list_voltage = regulator_list_voltage_table, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static struct regulator_ops hi655x_ldo_linear_ops = { + .enable = regulator_enable_regmap, + .disable = hi655x_disable, + .is_enabled = hi655x_is_enabled, + .list_voltage = regulator_list_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +#define HI655X_LDO(_ID, vreg, vmask, ereg, dreg, \ + sreg, cmask, vtable) { \ + .rdesc = { \ + .name = #_ID, \ + .of_match = of_match_ptr(#_ID), \ + .ops = &hi655x_regulator_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = HI655X_##_ID, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(vtable), \ + .volt_table = vtable, \ + .vsel_reg = HI655X_BUS_ADDR(vreg), \ + .vsel_mask = vmask, \ + .enable_reg = HI655X_BUS_ADDR(ereg), \ + .enable_mask = BIT(cmask), \ + }, \ + .disable_reg = HI655X_BUS_ADDR(dreg), \ + .status_reg = HI655X_BUS_ADDR(sreg), \ + .ctrl_mask = cmask, \ +} + +#define HI655X_LDO_LINEAR(_ID, vreg, vmask, ereg, dreg, \ + sreg, cmask, minv, nvolt, vstep) { \ + .rdesc = { \ + .name = #_ID, \ + .of_match = of_match_ptr(#_ID), \ + .ops = &hi655x_ldo_linear_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = HI655X_##_ID, \ + .owner = THIS_MODULE, \ + .min_uV = minv, \ + .n_voltages = nvolt, \ + .uV_step = vstep, \ + .vsel_reg = HI655X_BUS_ADDR(vreg), \ + .vsel_mask = vmask, \ + .enable_reg = HI655X_BUS_ADDR(ereg), \ + .enable_mask = BIT(cmask), \ + }, \ + .disable_reg = HI655X_BUS_ADDR(dreg), \ + .status_reg = HI655X_BUS_ADDR(sreg), \ + .ctrl_mask = cmask, \ +} + +static struct hi655x_regulator regulators[] = { + HI655X_LDO_LINEAR(LDO2, 0x72, 0x07, 0x29, 0x2a, 0x2b, 0x01, + 2500000, 8, 100000), + HI655X_LDO(LDO7, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x06, ldo7_voltages), + HI655X_LDO(LDO10, 0x78, 0x07, 0x29, 0x2a, 0x2b, 0x01, ldo7_voltages), + HI655X_LDO_LINEAR(LDO13, 0x7e, 0x07, 0x2c, 0x2d, 0x2e, 0x04, + 1600000, 8, 50000), + HI655X_LDO_LINEAR(LDO14, 0x7f, 0x07, 0x2c, 0x2d, 0x2e, 0x05, + 2500000, 8, 100000), + HI655X_LDO_LINEAR(LDO15, 0x80, 0x07, 0x2c, 0x2d, 0x2e, 0x06, + 1600000, 8, 50000), + HI655X_LDO_LINEAR(LDO17, 0x82, 0x07, 0x2f, 0x30, 0x31, 0x00, + 2500000, 8, 100000), + HI655X_LDO(LDO19, 0x84, 0x07, 0x2f, 0x30, 0x31, 0x02, ldo19_voltages), + HI655X_LDO_LINEAR(LDO21, 0x86, 0x07, 0x2f, 0x30, 0x31, 0x04, + 1650000, 8, 50000), + HI655X_LDO(LDO22, 0x87, 0x07, 0x2f, 0x30, 0x31, 0x05, ldo22_voltages), +}; + +static int hi655x_regulator_probe(struct platform_device *pdev) +{ + unsigned int i; + struct hi655x_regulator *regulator; + struct hi655x_pmic *pmic; + struct regulator_config config = { }; + struct regulator_dev *rdev; + + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) { + dev_err(&pdev->dev, "no pmic in the regulator parent node\n"); + return -ENODEV; + } + + regulator = devm_kzalloc(&pdev->dev, sizeof(*regulator), GFP_KERNEL); + if (!regulator) + return -ENOMEM; + + platform_set_drvdata(pdev, regulator); + + config.dev = pdev->dev.parent; + config.regmap = pmic->regmap; + config.driver_data = regulator; + for (i = 0; i < ARRAY_SIZE(regulators); i++) { + rdev = devm_regulator_register(&pdev->dev, + ®ulators[i].rdesc, + &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + regulator->rdesc.name); + return PTR_ERR(rdev); + } + } + return 0; +} + +static struct platform_driver hi655x_regulator_driver = { + .driver = { + .name = "hi655x-regulator", + }, + .probe = hi655x_regulator_probe, +}; +module_platform_driver(hi655x_regulator_driver); + +MODULE_AUTHOR("Chen Feng <puck.chen@hisilicon.com>"); +MODULE_DESCRIPTION("Hisilicon Hi655x regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c new file mode 100644 index 000000000000..f53e63301a20 --- /dev/null +++ b/drivers/regulator/lm363x-regulator.c @@ -0,0 +1,291 @@ +/* + * TI LM363X Regulator Driver + * + * Copyright 2015 Texas Instruments + * + * Author: Milo Kim <milo.kim@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/mfd/ti-lmu.h> +#include <linux/mfd/ti-lmu-register.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> +#include <linux/slab.h> + +/* LM3631 */ +#define LM3631_BOOST_VSEL_MAX 0x25 +#define LM3631_LDO_VSEL_MAX 0x28 +#define LM3631_CONT_VSEL_MAX 0x03 +#define LM3631_VBOOST_MIN 4500000 +#define LM3631_VCONT_MIN 1800000 +#define LM3631_VLDO_MIN 4000000 +#define ENABLE_TIME_USEC 1000 + +/* LM3632 */ +#define LM3632_BOOST_VSEL_MAX 0x26 +#define LM3632_LDO_VSEL_MAX 0x29 +#define LM3632_VBOOST_MIN 4500000 +#define LM3632_VLDO_MIN 4000000 + +/* Common */ +#define LM363X_STEP_50mV 50000 +#define LM363X_STEP_500mV 500000 + +static const int ldo_cont_enable_time[] = { + 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000, +}; + +static int lm363x_regulator_enable_time(struct regulator_dev *rdev) +{ + enum lm363x_regulator_id id = rdev_get_id(rdev); + u8 val, addr, mask; + + switch (id) { + case LM3631_LDO_CONT: + addr = LM3631_REG_ENTIME_VCONT; + mask = LM3631_ENTIME_CONT_MASK; + break; + case LM3631_LDO_OREF: + addr = LM3631_REG_ENTIME_VOREF; + mask = LM3631_ENTIME_MASK; + break; + case LM3631_LDO_POS: + addr = LM3631_REG_ENTIME_VPOS; + mask = LM3631_ENTIME_MASK; + break; + case LM3631_LDO_NEG: + addr = LM3631_REG_ENTIME_VNEG; + mask = LM3631_ENTIME_MASK; + break; + default: + return 0; + } + + if (regmap_read(rdev->regmap, addr, (unsigned int *)&val)) + return -EINVAL; + + val = (val & mask) >> LM3631_ENTIME_SHIFT; + + if (id == LM3631_LDO_CONT) + return ldo_cont_enable_time[val]; + else + return ENABLE_TIME_USEC * val; +} + +static struct regulator_ops lm363x_boost_voltage_table_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_ops lm363x_regulator_voltage_table_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lm363x_regulator_enable_time, +}; + +static const struct regulator_desc lm363x_regulator_desc[] = { + /* LM3631 */ + { + .name = "vboost", + .of_match = "vboost", + .id = LM3631_BOOST, + .ops = &lm363x_boost_voltage_table_ops, + .n_voltages = LM3631_BOOST_VSEL_MAX + 1, + .min_uV = LM3631_VBOOST_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3631_REG_VOUT_BOOST, + .vsel_mask = LM3631_VOUT_MASK, + }, + { + .name = "ldo_cont", + .of_match = "vcont", + .id = LM3631_LDO_CONT, + .ops = &lm363x_regulator_voltage_table_ops, + .n_voltages = LM3631_CONT_VSEL_MAX + 1, + .min_uV = LM3631_VCONT_MIN, + .uV_step = LM363X_STEP_500mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3631_REG_VOUT_CONT, + .vsel_mask = LM3631_VOUT_CONT_MASK, + .enable_reg = LM3631_REG_LDO_CTRL2, + .enable_mask = LM3631_EN_CONT_MASK, + }, + { + .name = "ldo_oref", + .of_match = "voref", + .id = LM3631_LDO_OREF, + .ops = &lm363x_regulator_voltage_table_ops, + .n_voltages = LM3631_LDO_VSEL_MAX + 1, + .min_uV = LM3631_VLDO_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3631_REG_VOUT_OREF, + .vsel_mask = LM3631_VOUT_MASK, + .enable_reg = LM3631_REG_LDO_CTRL1, + .enable_mask = LM3631_EN_OREF_MASK, + }, + { + .name = "ldo_vpos", + .of_match = "vpos", + .id = LM3631_LDO_POS, + .ops = &lm363x_regulator_voltage_table_ops, + .n_voltages = LM3631_LDO_VSEL_MAX + 1, + .min_uV = LM3631_VLDO_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3631_REG_VOUT_POS, + .vsel_mask = LM3631_VOUT_MASK, + .enable_reg = LM3631_REG_LDO_CTRL1, + .enable_mask = LM3631_EN_VPOS_MASK, + }, + { + .name = "ldo_vneg", + .of_match = "vneg", + .id = LM3631_LDO_NEG, + .ops = &lm363x_regulator_voltage_table_ops, + .n_voltages = LM3631_LDO_VSEL_MAX + 1, + .min_uV = LM3631_VLDO_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3631_REG_VOUT_NEG, + .vsel_mask = LM3631_VOUT_MASK, + .enable_reg = LM3631_REG_LDO_CTRL1, + .enable_mask = LM3631_EN_VNEG_MASK, + }, + /* LM3632 */ + { + .name = "vboost", + .of_match = "vboost", + .id = LM3632_BOOST, + .ops = &lm363x_boost_voltage_table_ops, + .n_voltages = LM3632_BOOST_VSEL_MAX + 1, + .min_uV = LM3632_VBOOST_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3632_REG_VOUT_BOOST, + .vsel_mask = LM3632_VOUT_MASK, + }, + { + .name = "ldo_vpos", + .of_match = "vpos", + .id = LM3632_LDO_POS, + .ops = &lm363x_regulator_voltage_table_ops, + .n_voltages = LM3632_LDO_VSEL_MAX + 1, + .min_uV = LM3632_VLDO_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3632_REG_VOUT_POS, + .vsel_mask = LM3632_VOUT_MASK, + .enable_reg = LM3632_REG_BIAS_CONFIG, + .enable_mask = LM3632_EN_VPOS_MASK, + }, + { + .name = "ldo_vneg", + .of_match = "vneg", + .id = LM3632_LDO_NEG, + .ops = &lm363x_regulator_voltage_table_ops, + .n_voltages = LM3632_LDO_VSEL_MAX + 1, + .min_uV = LM3632_VLDO_MIN, + .uV_step = LM363X_STEP_50mV, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LM3632_REG_VOUT_NEG, + .vsel_mask = LM3632_VOUT_MASK, + .enable_reg = LM3632_REG_BIAS_CONFIG, + .enable_mask = LM3632_EN_VNEG_MASK, + }, +}; + +static int lm363x_regulator_of_get_enable_gpio(struct device_node *np, int id) +{ + /* + * Check LCM_EN1/2_GPIO is configured. + * Those pins are used for enabling VPOS/VNEG LDOs. + */ + switch (id) { + case LM3632_LDO_POS: + return of_get_named_gpio(np, "ti,lcm-en1-gpio", 0); + case LM3632_LDO_NEG: + return of_get_named_gpio(np, "ti,lcm-en2-gpio", 0); + default: + return -EINVAL; + } +} + +static int lm363x_regulator_probe(struct platform_device *pdev) +{ + struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent); + struct regmap *regmap = lmu->regmap; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + struct device *dev = &pdev->dev; + int id = pdev->id; + int ret, ena_gpio; + + cfg.dev = dev; + cfg.regmap = regmap; + + /* + * LM3632 LDOs can be controlled by external pin. + * Register update is required if the pin is used. + */ + ena_gpio = lm363x_regulator_of_get_enable_gpio(dev->of_node, id); + if (gpio_is_valid(ena_gpio)) { + cfg.ena_gpio = ena_gpio; + cfg.ena_gpio_flags = GPIOF_OUT_INIT_LOW; + + ret = regmap_update_bits(regmap, LM3632_REG_BIAS_CONFIG, + LM3632_EXT_EN_MASK, + LM3632_EXT_EN_MASK); + if (ret) { + dev_err(dev, "External pin err: %d\n", ret); + return ret; + } + } + + rdev = devm_regulator_register(dev, &lm363x_regulator_desc[id], &cfg); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "[%d] regulator register err: %d\n", id, ret); + return ret; + } + + return 0; +} + +static struct platform_driver lm363x_regulator_driver = { + .probe = lm363x_regulator_probe, + .driver = { + .name = "lm363x-regulator", + }, +}; + +module_platform_driver(lm363x_regulator_driver); + +MODULE_DESCRIPTION("TI LM363X Regulator Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:lm363x-regulator"); diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index e5af07208f9d..38992112fd6e 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -15,6 +15,7 @@ #include <linux/regmap.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/delay.h> #include <linux/regulator/lp872x.h> #include <linux/regulator/driver.h> #include <linux/platform_device.h> @@ -108,7 +109,6 @@ struct lp872x { struct lp872x_platform_data *pdata; int num_regulators; enum lp872x_dvs_state dvs_pin; - int dvs_gpio; }; /* LP8720/LP8725 shared voltage table for LDOs */ @@ -520,6 +520,7 @@ static struct regulator_ops lp8725_buck_ops = { static struct regulator_desc lp8720_regulator_desc[] = { { .name = "ldo1", + .of_match = of_match_ptr("ldo1"), .id = LP8720_ID_LDO1, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -533,6 +534,7 @@ static struct regulator_desc lp8720_regulator_desc[] = { }, { .name = "ldo2", + .of_match = of_match_ptr("ldo2"), .id = LP8720_ID_LDO2, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -546,6 +548,7 @@ static struct regulator_desc lp8720_regulator_desc[] = { }, { .name = "ldo3", + .of_match = of_match_ptr("ldo3"), .id = LP8720_ID_LDO3, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -559,6 +562,7 @@ static struct regulator_desc lp8720_regulator_desc[] = { }, { .name = "ldo4", + .of_match = of_match_ptr("ldo4"), .id = LP8720_ID_LDO4, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl), @@ -572,6 +576,7 @@ static struct regulator_desc lp8720_regulator_desc[] = { }, { .name = "ldo5", + .of_match = of_match_ptr("ldo5"), .id = LP8720_ID_LDO5, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -585,6 +590,7 @@ static struct regulator_desc lp8720_regulator_desc[] = { }, { .name = "buck", + .of_match = of_match_ptr("buck"), .id = LP8720_ID_BUCK, .ops = &lp8720_buck_ops, .n_voltages = ARRAY_SIZE(lp8720_buck_vtbl), @@ -599,6 +605,7 @@ static struct regulator_desc lp8720_regulator_desc[] = { static struct regulator_desc lp8725_regulator_desc[] = { { .name = "ldo1", + .of_match = of_match_ptr("ldo1"), .id = LP8725_ID_LDO1, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -612,6 +619,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "ldo2", + .of_match = of_match_ptr("ldo2"), .id = LP8725_ID_LDO2, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -625,6 +633,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "ldo3", + .of_match = of_match_ptr("ldo3"), .id = LP8725_ID_LDO3, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -638,6 +647,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "ldo4", + .of_match = of_match_ptr("ldo4"), .id = LP8725_ID_LDO4, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -651,6 +661,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "ldo5", + .of_match = of_match_ptr("ldo5"), .id = LP8725_ID_LDO5, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), @@ -664,6 +675,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "lilo1", + .of_match = of_match_ptr("lilo1"), .id = LP8725_ID_LILO1, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), @@ -677,6 +689,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "lilo2", + .of_match = of_match_ptr("lilo2"), .id = LP8725_ID_LILO2, .ops = &lp872x_ldo_ops, .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), @@ -690,6 +703,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "buck1", + .of_match = of_match_ptr("buck1"), .id = LP8725_ID_BUCK1, .ops = &lp8725_buck_ops, .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), @@ -701,6 +715,7 @@ static struct regulator_desc lp8725_regulator_desc[] = { }, { .name = "buck2", + .of_match = of_match_ptr("buck2"), .id = LP8725_ID_BUCK2, .ops = &lp8725_buck_ops, .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), @@ -724,10 +739,8 @@ static int lp872x_init_dvs(struct lp872x *lp) goto set_default_dvs_mode; gpio = dvs->gpio; - if (!gpio_is_valid(gpio)) { - dev_warn(lp->dev, "invalid gpio: %d\n", gpio); + if (!gpio_is_valid(gpio)) goto set_default_dvs_mode; - } pinstate = dvs->init_state; ret = devm_gpio_request_one(lp->dev, gpio, pinstate, "LP872X DVS"); @@ -737,7 +750,6 @@ static int lp872x_init_dvs(struct lp872x *lp) } lp->dvs_pin = pinstate; - lp->dvs_gpio = gpio; return 0; @@ -746,6 +758,33 @@ set_default_dvs_mode: default_dvs_mode[lp->chipid]); } +static int lp872x_hw_enable(struct lp872x *lp) +{ + int ret, gpio; + + if (!lp->pdata) + return -EINVAL; + + gpio = lp->pdata->enable_gpio; + if (!gpio_is_valid(gpio)) + return 0; + + /* Always set enable GPIO high. */ + ret = devm_gpio_request_one(lp->dev, gpio, GPIOF_OUT_INIT_HIGH, "LP872X EN"); + if (ret) { + dev_err(lp->dev, "gpio request err: %d\n", ret); + return ret; + } + + /* Each chip has a different enable delay. */ + if (lp->chipid == LP8720) + usleep_range(LP8720_ENABLE_DELAY, 1.5 * LP8720_ENABLE_DELAY); + else + usleep_range(LP8725_ENABLE_DELAY, 1.5 * LP8725_ENABLE_DELAY); + + return 0; +} + static int lp872x_config(struct lp872x *lp) { struct lp872x_platform_data *pdata = lp->pdata; @@ -864,6 +903,8 @@ static struct lp872x_platform_data of_property_read_u8(np, "ti,dvs-state", &dvs_state); pdata->dvs->init_state = dvs_state ? DVS_HIGH : DVS_LOW; + pdata->enable_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (of_get_child_count(np) == 0) goto out; @@ -937,6 +978,10 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) lp->chipid = id->driver_data; i2c_set_clientdata(cl, lp); + ret = lp872x_hw_enable(lp); + if (ret) + return ret; + ret = lp872x_config(lp); if (ret) return ret; diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c index a97bed90d39b..ec46290b647e 100644 --- a/drivers/regulator/lp8788-buck.c +++ b/drivers/regulator/lp8788-buck.c @@ -344,7 +344,7 @@ static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev) REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; } -static struct regulator_ops lp8788_buck12_ops = { +static const struct regulator_ops lp8788_buck12_ops = { .list_voltage = regulator_list_voltage_table, .map_voltage = regulator_map_voltage_ascend, .set_voltage_sel = lp8788_buck12_set_voltage_sel, @@ -357,7 +357,7 @@ static struct regulator_ops lp8788_buck12_ops = { .get_mode = lp8788_buck_get_mode, }; -static struct regulator_ops lp8788_buck34_ops = { +static const struct regulator_ops lp8788_buck34_ops = { .list_voltage = regulator_list_voltage_table, .map_voltage = regulator_map_voltage_ascend, .set_voltage_sel = regulator_set_voltage_sel_regmap, diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 9f22d079c8cc..cbfd35873575 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -170,7 +170,7 @@ static int lp8788_ldo_enable_time(struct regulator_dev *rdev) return ENABLE_TIME_USEC * val; } -static struct regulator_ops lp8788_ldo_voltage_table_ops = { +static const struct regulator_ops lp8788_ldo_voltage_table_ops = { .list_voltage = regulator_list_voltage_table, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -180,7 +180,7 @@ static struct regulator_ops lp8788_ldo_voltage_table_ops = { .enable_time = lp8788_ldo_enable_time, }; -static struct regulator_ops lp8788_ldo_voltage_fixed_ops = { +static const struct regulator_ops lp8788_ldo_voltage_fixed_ops = { .list_voltage = regulator_list_voltage_linear, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -613,22 +613,20 @@ static struct platform_driver lp8788_aldo_driver = { }, }; +static struct platform_driver * const drivers[] = { + &lp8788_dldo_driver, + &lp8788_aldo_driver, +}; + static int __init lp8788_ldo_init(void) { - int ret; - - ret = platform_driver_register(&lp8788_dldo_driver); - if (ret) - return ret; - - return platform_driver_register(&lp8788_aldo_driver); + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } subsys_initcall(lp8788_ldo_init); static void __exit lp8788_ldo_exit(void) { - platform_driver_unregister(&lp8788_aldo_driver); - platform_driver_unregister(&lp8788_dldo_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_exit(lp8788_ldo_exit); diff --git a/drivers/regulator/mt6311-regulator.c b/drivers/regulator/mt6311-regulator.c index 02c4e5feca8e..0495716fd35f 100644 --- a/drivers/regulator/mt6311-regulator.c +++ b/drivers/regulator/mt6311-regulator.c @@ -30,6 +30,7 @@ static const struct regmap_config mt6311_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = MT6311_FQMTR_CON4, + .cache_type = REGCACHE_RBTREE, }; /* Default limits measured in millivolts and milliamps */ diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 499e437c7e91..fe2e33441dae 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -93,6 +93,12 @@ static void of_get_regulation_constraints(struct device_node *np, constraints->soft_start = of_property_read_bool(np, "regulator-soft-start"); + ret = of_property_read_u32(np, "regulator-active-discharge", &pval); + if (!ret) { + constraints->active_discharge = + (pval) ? REGULATOR_ACTIVE_DISCHARGE_ENABLE : + REGULATOR_ACTIVE_DISCHARGE_DISABLE; + } if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) { if (desc && desc->of_map_mode) { diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 8217613807d3..6efc7ee8aea3 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -612,6 +612,18 @@ static struct regulator_ops palmas_ops_ldo = { .map_voltage = regulator_map_voltage_linear, }; +static struct regulator_ops palmas_ops_ldo9 = { + .is_enabled = palmas_is_enabled_ldo, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, +}; + static struct regulator_ops palmas_ops_ext_control_ldo = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -639,6 +651,19 @@ static struct regulator_ops tps65917_ops_ldo = { .set_voltage_time_sel = regulator_set_voltage_time_sel, }; +static struct regulator_ops tps65917_ops_ldo_1_2 = { + .is_enabled = palmas_is_enabled_ldo, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, +}; + static int palmas_regulator_config_external(struct palmas *palmas, int id, struct palmas_reg_init *reg_init) { @@ -915,6 +940,13 @@ static int palmas_ldo_registration(struct palmas_pmic *pmic, if (pdata && pdata->ldo6_vibrator && (id == PALMAS_REG_LDO6)) desc->enable_time = 2000; + + if (id == PALMAS_REG_LDO9) { + desc->ops = &palmas_ops_ldo9; + desc->bypass_reg = desc->enable_reg; + desc->bypass_mask = + PALMAS_LDO9_CTRL_LDO_BYPASS_EN; + } } else { if (!ddata->has_regen3 && id == PALMAS_REG_REGEN3) continue; @@ -1019,6 +1051,13 @@ static int tps65917_ldo_registration(struct palmas_pmic *pmic, * It is of the order of ~60mV/uS. */ desc->ramp_delay = 2500; + if (id == TPS65917_REG_LDO1 || + id == TPS65917_REG_LDO2) { + desc->ops = &tps65917_ops_ldo_1_2; + desc->bypass_reg = desc->enable_reg; + desc->bypass_mask = + TPS65917_LDO1_CTRL_BYPASS_EN; + } } else { desc->n_voltages = 1; if (reg_init && reg_init->roof_floor) diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c new file mode 100644 index 000000000000..094376c8de4b --- /dev/null +++ b/drivers/regulator/pv88060-regulator.c @@ -0,0 +1,437 @@ +/* + * pv88060-regulator.c - Regulator device driver for PV88060 + * Copyright (C) 2015 Powerventure Semiconductor Ltd. + * + * 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. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regmap.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/regulator/of_regulator.h> +#include <linux/proc_fs.h> +#include <linux/uaccess.h> +#include "pv88060-regulator.h" + +#define PV88060_MAX_REGULATORS 14 + +/* PV88060 REGULATOR IDs */ +enum { + /* BUCKs */ + PV88060_ID_BUCK1, + + /* LDOs */ + PV88060_ID_LDO1, + PV88060_ID_LDO2, + PV88060_ID_LDO3, + PV88060_ID_LDO4, + PV88060_ID_LDO5, + PV88060_ID_LDO6, + PV88060_ID_LDO7, + + /* SWTs */ + PV88060_ID_SW1, + PV88060_ID_SW2, + PV88060_ID_SW3, + PV88060_ID_SW4, + PV88060_ID_SW5, + PV88060_ID_SW6, +}; + +struct pv88060_regulator { + struct regulator_desc desc; + /* Current limiting */ + unsigned n_current_limits; + const int *current_limits; + unsigned int limit_mask; + unsigned int conf; /* buck configuration register */ +}; + +struct pv88060 { + struct device *dev; + struct regmap *regmap; + struct regulator_dev *rdev[PV88060_MAX_REGULATORS]; +}; + +static const struct regmap_config pv88060_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +/* Current limits array (in uA) for BUCK1 + * Entry indexes corresponds to register values. + */ + +static const int pv88060_buck1_limits[] = { + 1496000, 2393000, 3291000, 4189000 +}; + +static unsigned int pv88060_buck_get_mode(struct regulator_dev *rdev) +{ + struct pv88060_regulator *info = rdev_get_drvdata(rdev); + unsigned int data; + int ret, mode = 0; + + ret = regmap_read(rdev->regmap, info->conf, &data); + if (ret < 0) + return ret; + + switch (data & PV88060_BUCK_MODE_MASK) { + case PV88060_BUCK_MODE_SYNC: + mode = REGULATOR_MODE_FAST; + break; + case PV88060_BUCK_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case PV88060_BUCK_MODE_SLEEP: + mode = REGULATOR_MODE_STANDBY; + break; + } + + return mode; +} + +static int pv88060_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct pv88060_regulator *info = rdev_get_drvdata(rdev); + int val = 0; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = PV88060_BUCK_MODE_SYNC; + break; + case REGULATOR_MODE_NORMAL: + val = PV88060_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = PV88060_BUCK_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, info->conf, + PV88060_BUCK_MODE_MASK, val); +} + +static int pv88060_set_current_limit(struct regulator_dev *rdev, int min, + int max) +{ + struct pv88060_regulator *info = rdev_get_drvdata(rdev); + int i; + + /* search for closest to maximum */ + for (i = info->n_current_limits; i >= 0; i--) { + if (min <= info->current_limits[i] + && max >= info->current_limits[i]) { + return regmap_update_bits(rdev->regmap, + info->conf, + info->limit_mask, + i << PV88060_BUCK_ILIM_SHIFT); + } + } + + return -EINVAL; +} + +static int pv88060_get_current_limit(struct regulator_dev *rdev) +{ + struct pv88060_regulator *info = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(rdev->regmap, info->conf, &data); + if (ret < 0) + return ret; + + data = (data & info->limit_mask) >> PV88060_BUCK_ILIM_SHIFT; + return info->current_limits[data]; +} + +static struct regulator_ops pv88060_buck_ops = { + .get_mode = pv88060_buck_get_mode, + .set_mode = pv88060_buck_set_mode, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_current_limit = pv88060_set_current_limit, + .get_current_limit = pv88060_get_current_limit, +}; + +static struct regulator_ops pv88060_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +#define PV88060_BUCK(chip, regl_name, min, step, max, limits_array) \ +{\ + .desc = {\ + .id = chip##_ID_##regl_name,\ + .name = __stringify(chip##_##regl_name),\ + .of_match = of_match_ptr(#regl_name),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .ops = &pv88060_buck_ops,\ + .min_uV = min,\ + .uV_step = step,\ + .n_voltages = ((max) - (min))/(step) + 1,\ + .enable_reg = PV88060_REG_##regl_name##_CONF0,\ + .enable_mask = PV88060_BUCK_EN, \ + .vsel_reg = PV88060_REG_##regl_name##_CONF0,\ + .vsel_mask = PV88060_VBUCK_MASK,\ + },\ + .current_limits = limits_array,\ + .n_current_limits = ARRAY_SIZE(limits_array),\ + .limit_mask = PV88060_BUCK_ILIM_MASK, \ + .conf = PV88060_REG_##regl_name##_CONF1,\ +} + +#define PV88060_LDO(chip, regl_name, min, step, max) \ +{\ + .desc = {\ + .id = chip##_ID_##regl_name,\ + .name = __stringify(chip##_##regl_name),\ + .of_match = of_match_ptr(#regl_name),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .ops = &pv88060_ldo_ops,\ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = (step) ? ((max - min) / step + 1) : 1, \ + .enable_reg = PV88060_REG_##regl_name##_CONF, \ + .enable_mask = PV88060_LDO_EN, \ + .vsel_reg = PV88060_REG_##regl_name##_CONF, \ + .vsel_mask = PV88060_VLDO_MASK, \ + },\ +} + +#define PV88060_SW(chip, regl_name, max) \ +{\ + .desc = {\ + .id = chip##_ID_##regl_name,\ + .name = __stringify(chip##_##regl_name),\ + .of_match = of_match_ptr(#regl_name),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .ops = &pv88060_ldo_ops,\ + .min_uV = max,\ + .uV_step = 0,\ + .n_voltages = 1,\ + .enable_reg = PV88060_REG_##regl_name##_CONF,\ + .enable_mask = PV88060_SW_EN,\ + },\ +} + +static const struct pv88060_regulator pv88060_regulator_info[] = { + PV88060_BUCK(PV88060, BUCK1, 2800000, 12500, 4387500, + pv88060_buck1_limits), + PV88060_LDO(PV88060, LDO1, 1200000, 50000, 3350000), + PV88060_LDO(PV88060, LDO2, 1200000, 50000, 3350000), + PV88060_LDO(PV88060, LDO3, 1200000, 50000, 3350000), + PV88060_LDO(PV88060, LDO4, 1200000, 50000, 3350000), + PV88060_LDO(PV88060, LDO5, 1200000, 50000, 3350000), + PV88060_LDO(PV88060, LDO6, 1200000, 50000, 3350000), + PV88060_LDO(PV88060, LDO7, 1200000, 50000, 3350000), + PV88060_SW(PV88060, SW1, 5000000), + PV88060_SW(PV88060, SW2, 5000000), + PV88060_SW(PV88060, SW3, 5000000), + PV88060_SW(PV88060, SW4, 5000000), + PV88060_SW(PV88060, SW5, 5000000), + PV88060_SW(PV88060, SW6, 5000000), +}; + +static irqreturn_t pv88060_irq_handler(int irq, void *data) +{ + struct pv88060 *chip = data; + int i, reg_val, err, ret = IRQ_NONE; + + err = regmap_read(chip->regmap, PV88060_REG_EVENT_A, ®_val); + if (err < 0) + goto error_i2c; + + if (reg_val & PV88060_E_VDD_FLT) { + for (i = 0; i < PV88060_MAX_REGULATORS; i++) { + if (chip->rdev[i] != NULL) { + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + } + } + + err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A, + PV88060_E_VDD_FLT, PV88060_E_VDD_FLT); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + if (reg_val & PV88060_E_OVER_TEMP) { + for (i = 0; i < PV88060_MAX_REGULATORS; i++) { + if (chip->rdev[i] != NULL) { + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_TEMP, + NULL); + } + } + + err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A, + PV88060_E_OVER_TEMP, PV88060_E_OVER_TEMP); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + return ret; + +error_i2c: + dev_err(chip->dev, "I2C error : %d\n", err); + return IRQ_NONE; +} + +/* + * I2C driver interface functions + */ +static int pv88060_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev); + struct pv88060 *chip; + struct regulator_config config = { }; + int error, i, ret = 0; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88060), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &i2c->dev; + chip->regmap = devm_regmap_init_i2c(i2c, &pv88060_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + i2c_set_clientdata(i2c, chip); + + if (i2c->irq != 0) { + ret = regmap_write(chip->regmap, PV88060_REG_MASK_A, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask A reg: %d\n", ret); + return ret; + } + + ret = regmap_write(chip->regmap, PV88060_REG_MASK_B, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask B reg: %d\n", ret); + return ret; + } + + ret = regmap_write(chip->regmap, PV88060_REG_MASK_C, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask C reg: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + pv88060_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "pv88060", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed to request IRQ: %d\n", + i2c->irq); + return ret; + } + + ret = regmap_update_bits(chip->regmap, PV88060_REG_MASK_A, + PV88060_M_VDD_FLT | PV88060_M_OVER_TEMP, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to update mask reg: %d\n", ret); + return ret; + } + + } else { + dev_warn(chip->dev, "No IRQ configured\n"); + } + + config.dev = chip->dev; + config.regmap = chip->regmap; + + for (i = 0; i < PV88060_MAX_REGULATORS; i++) { + if (init_data) + config.init_data = &init_data[i]; + + config.driver_data = (void *)&pv88060_regulator_info[i]; + chip->rdev[i] = devm_regulator_register(chip->dev, + &pv88060_regulator_info[i].desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, + "Failed to register PV88060 regulator\n"); + return PTR_ERR(chip->rdev[i]); + } + } + + return 0; +} + +static const struct i2c_device_id pv88060_i2c_id[] = { + {"pv88060", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pv88060_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id pv88060_dt_ids[] = { + { .compatible = "pvs,pv88060", .data = &pv88060_i2c_id[0] }, + {}, +}; +MODULE_DEVICE_TABLE(of, pv88060_dt_ids); +#endif + +static struct i2c_driver pv88060_regulator_driver = { + .driver = { + .name = "pv88060", + .of_match_table = of_match_ptr(pv88060_dt_ids), + }, + .probe = pv88060_i2c_probe, + .id_table = pv88060_i2c_id, +}; + +module_i2c_driver(pv88060_regulator_driver); + +MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88060"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/pv88060-regulator.h b/drivers/regulator/pv88060-regulator.h new file mode 100644 index 000000000000..02ca9203a172 --- /dev/null +++ b/drivers/regulator/pv88060-regulator.h @@ -0,0 +1,69 @@ +/* + * pv88060-regulator.h - Regulator definitions for PV88060 + * Copyright (C) 2015 Powerventure Semiconductor Ltd. + * + * 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. + */ + +#ifndef __PV88060_REGISTERS_H__ +#define __PV88060_REGISTERS_H__ + +/* System Control and Event Registers */ +#define PV88060_REG_EVENT_A 0x04 +#define PV88060_REG_MASK_A 0x08 +#define PV88060_REG_MASK_B 0x09 +#define PV88060_REG_MASK_C 0x0A + +/* Regulator Registers */ +#define PV88060_REG_BUCK1_CONF0 0x1B +#define PV88060_REG_BUCK1_CONF1 0x1C +#define PV88060_REG_LDO1_CONF 0x1D +#define PV88060_REG_LDO2_CONF 0x1E +#define PV88060_REG_LDO3_CONF 0x1F +#define PV88060_REG_LDO4_CONF 0x20 +#define PV88060_REG_LDO5_CONF 0x21 +#define PV88060_REG_LDO6_CONF 0x22 +#define PV88060_REG_LDO7_CONF 0x23 + +#define PV88060_REG_SW1_CONF 0x3B +#define PV88060_REG_SW2_CONF 0x3C +#define PV88060_REG_SW3_CONF 0x3D +#define PV88060_REG_SW4_CONF 0x3E +#define PV88060_REG_SW5_CONF 0x3F +#define PV88060_REG_SW6_CONF 0x40 + +/* PV88060_REG_EVENT_A (addr=0x04) */ +#define PV88060_E_VDD_FLT 0x01 +#define PV88060_E_OVER_TEMP 0x02 + +/* PV88060_REG_MASK_A (addr=0x08) */ +#define PV88060_M_VDD_FLT 0x01 +#define PV88060_M_OVER_TEMP 0x02 + +/* PV88060_REG_BUCK1_CONF0 (addr=0x1B) */ +#define PV88060_BUCK_EN 0x80 +#define PV88060_VBUCK_MASK 0x7F +/* PV88060_REG_LDO1/2/3/4/5/6/7_CONT */ +#define PV88060_LDO_EN 0x40 +#define PV88060_VLDO_MASK 0x3F +/* PV88060_REG_SW1/2/3/4/5_CONF */ +#define PV88060_SW_EN 0x80 + +/* PV88060_REG_BUCK1_CONF1 (addr=0x1C) */ +#define PV88060_BUCK_ILIM_SHIFT 2 +#define PV88060_BUCK_ILIM_MASK 0x0C +#define PV88060_BUCK_MODE_SHIFT 0 +#define PV88060_BUCK_MODE_MASK 0x03 +#define PV88060_BUCK_MODE_SLEEP 0x00 +#define PV88060_BUCK_MODE_AUTO 0x01 +#define PV88060_BUCK_MODE_SYNC 0x02 + +#endif /* __PV88060_REGISTERS_H__ */ diff --git a/drivers/regulator/pv88090-regulator.c b/drivers/regulator/pv88090-regulator.c new file mode 100644 index 000000000000..0057c6740d6f --- /dev/null +++ b/drivers/regulator/pv88090-regulator.c @@ -0,0 +1,458 @@ +/* + * pv88090-regulator.c - Regulator device driver for PV88090 + * Copyright (C) 2015 Powerventure Semiconductor Ltd. + * + * 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. + */ + +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regmap.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/regulator/of_regulator.h> +#include <linux/proc_fs.h> +#include <linux/uaccess.h> +#include "pv88090-regulator.h" + +#define PV88090_MAX_REGULATORS 5 + +/* PV88090 REGULATOR IDs */ +enum { + /* BUCKs */ + PV88090_ID_BUCK1, + PV88090_ID_BUCK2, + PV88090_ID_BUCK3, + + /* LDOs */ + PV88090_ID_LDO1, + PV88090_ID_LDO2, +}; + +struct pv88090_regulator { + struct regulator_desc desc; + /* Current limiting */ + unsigned n_current_limits; + const int *current_limits; + unsigned int limit_mask; + unsigned int conf; + unsigned int conf2; +}; + +struct pv88090 { + struct device *dev; + struct regmap *regmap; + struct regulator_dev *rdev[PV88090_MAX_REGULATORS]; +}; + +struct pv88090_buck_voltage { + int min_uV; + int max_uV; + int uV_step; +}; + +static const struct regmap_config pv88090_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +/* Current limits array (in uA) for BUCK1, BUCK2, BUCK3. + * Entry indexes corresponds to register values. + */ + +static const int pv88090_buck1_limits[] = { + 220000, 440000, 660000, 880000, 1100000, 1320000, 1540000, 1760000, + 1980000, 2200000, 2420000, 2640000, 2860000, 3080000, 3300000, 3520000, + 3740000, 3960000, 4180000, 4400000, 4620000, 4840000, 5060000, 5280000, + 5500000, 5720000, 5940000, 6160000, 6380000, 6600000, 6820000, 7040000 +}; + +static const int pv88090_buck23_limits[] = { + 1496000, 2393000, 3291000, 4189000 +}; + +static const struct pv88090_buck_voltage pv88090_buck_vol[3] = { + { + .min_uV = 600000, + .max_uV = 1393750, + .uV_step = 6250, + }, + + { + .min_uV = 1400000, + .max_uV = 2193750, + .uV_step = 6250, + }, + { + .min_uV = 1250000, + .max_uV = 2837500, + .uV_step = 12500, + }, +}; + +static unsigned int pv88090_buck_get_mode(struct regulator_dev *rdev) +{ + struct pv88090_regulator *info = rdev_get_drvdata(rdev); + unsigned int data; + int ret, mode = 0; + + ret = regmap_read(rdev->regmap, info->conf, &data); + if (ret < 0) + return ret; + + switch (data & PV88090_BUCK1_MODE_MASK) { + case PV88090_BUCK_MODE_SYNC: + mode = REGULATOR_MODE_FAST; + break; + case PV88090_BUCK_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case PV88090_BUCK_MODE_SLEEP: + mode = REGULATOR_MODE_STANDBY; + break; + } + + return mode; +} + +static int pv88090_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct pv88090_regulator *info = rdev_get_drvdata(rdev); + int val = 0; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = PV88090_BUCK_MODE_SYNC; + break; + case REGULATOR_MODE_NORMAL: + val = PV88090_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = PV88090_BUCK_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, info->conf, + PV88090_BUCK1_MODE_MASK, val); +} + +static int pv88090_set_current_limit(struct regulator_dev *rdev, int min, + int max) +{ + struct pv88090_regulator *info = rdev_get_drvdata(rdev); + int i; + + /* search for closest to maximum */ + for (i = info->n_current_limits; i >= 0; i--) { + if (min <= info->current_limits[i] + && max >= info->current_limits[i]) { + return regmap_update_bits(rdev->regmap, + info->conf, + info->limit_mask, + i << PV88090_BUCK1_ILIM_SHIFT); + } + } + + return -EINVAL; +} + +static int pv88090_get_current_limit(struct regulator_dev *rdev) +{ + struct pv88090_regulator *info = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(rdev->regmap, info->conf, &data); + if (ret < 0) + return ret; + + data = (data & info->limit_mask) >> PV88090_BUCK1_ILIM_SHIFT; + return info->current_limits[data]; +} + +static struct regulator_ops pv88090_buck_ops = { + .get_mode = pv88090_buck_get_mode, + .set_mode = pv88090_buck_set_mode, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_current_limit = pv88090_set_current_limit, + .get_current_limit = pv88090_get_current_limit, +}; + +static struct regulator_ops pv88090_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +#define PV88090_BUCK(chip, regl_name, min, step, max, limits_array) \ +{\ + .desc = {\ + .id = chip##_ID_##regl_name,\ + .name = __stringify(chip##_##regl_name),\ + .of_match = of_match_ptr(#regl_name),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .ops = &pv88090_buck_ops,\ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = ((max) - (min))/(step) + 1, \ + .enable_reg = PV88090_REG_##regl_name##_CONF0, \ + .enable_mask = PV88090_##regl_name##_EN, \ + .vsel_reg = PV88090_REG_##regl_name##_CONF0, \ + .vsel_mask = PV88090_V##regl_name##_MASK, \ + },\ + .current_limits = limits_array, \ + .n_current_limits = ARRAY_SIZE(limits_array), \ + .limit_mask = PV88090_##regl_name##_ILIM_MASK, \ + .conf = PV88090_REG_##regl_name##_CONF1, \ + .conf2 = PV88090_REG_##regl_name##_CONF2, \ +} + +#define PV88090_LDO(chip, regl_name, min, step, max) \ +{\ + .desc = {\ + .id = chip##_ID_##regl_name,\ + .name = __stringify(chip##_##regl_name),\ + .of_match = of_match_ptr(#regl_name),\ + .regulators_node = of_match_ptr("regulators"),\ + .type = REGULATOR_VOLTAGE,\ + .owner = THIS_MODULE,\ + .ops = &pv88090_ldo_ops,\ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = ((max) - (min))/(step) + 1, \ + .enable_reg = PV88090_REG_##regl_name##_CONT, \ + .enable_mask = PV88090_##regl_name##_EN, \ + .vsel_reg = PV88090_REG_##regl_name##_CONT, \ + .vsel_mask = PV88090_V##regl_name##_MASK, \ + },\ +} + +static struct pv88090_regulator pv88090_regulator_info[] = { + PV88090_BUCK(PV88090, BUCK1, 600000, 6250, 1393750, + pv88090_buck1_limits), + PV88090_BUCK(PV88090, BUCK2, 600000, 6250, 1393750, + pv88090_buck23_limits), + PV88090_BUCK(PV88090, BUCK3, 600000, 6250, 1393750, + pv88090_buck23_limits), + PV88090_LDO(PV88090, LDO1, 1200000, 50000, 4350000), + PV88090_LDO(PV88090, LDO2, 650000, 25000, 2225000), +}; + +static irqreturn_t pv88090_irq_handler(int irq, void *data) +{ + struct pv88090 *chip = data; + int i, reg_val, err, ret = IRQ_NONE; + + err = regmap_read(chip->regmap, PV88090_REG_EVENT_A, ®_val); + if (err < 0) + goto error_i2c; + + if (reg_val & PV88090_E_VDD_FLT) { + for (i = 0; i < PV88090_MAX_REGULATORS; i++) { + if (chip->rdev[i] != NULL) { + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + } + } + + err = regmap_write(chip->regmap, PV88090_REG_EVENT_A, + PV88090_E_VDD_FLT); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + if (reg_val & PV88090_E_OVER_TEMP) { + for (i = 0; i < PV88090_MAX_REGULATORS; i++) { + if (chip->rdev[i] != NULL) { + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_TEMP, + NULL); + } + } + + err = regmap_write(chip->regmap, PV88090_REG_EVENT_A, + PV88090_E_OVER_TEMP); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + return ret; + +error_i2c: + dev_err(chip->dev, "I2C error : %d\n", err); + return IRQ_NONE; +} + +/* + * I2C driver interface functions + */ +static int pv88090_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev); + struct pv88090 *chip; + struct regulator_config config = { }; + int error, i, ret = 0; + unsigned int conf2, range, index; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88090), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &i2c->dev; + chip->regmap = devm_regmap_init_i2c(i2c, &pv88090_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + i2c_set_clientdata(i2c, chip); + + if (i2c->irq != 0) { + ret = regmap_write(chip->regmap, PV88090_REG_MASK_A, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask A reg: %d\n", ret); + return ret; + } + + ret = regmap_write(chip->regmap, PV88090_REG_MASK_B, 0xFF); + if (ret < 0) { + dev_err(chip->dev, + "Failed to mask B reg: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + pv88090_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "pv88090", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed to request IRQ: %d\n", + i2c->irq); + return ret; + } + + ret = regmap_update_bits(chip->regmap, PV88090_REG_MASK_A, + PV88090_M_VDD_FLT | PV88090_M_OVER_TEMP, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to update mask reg: %d\n", ret); + return ret; + } + + } else { + dev_warn(chip->dev, "No IRQ configured\n"); + } + + config.dev = chip->dev; + config.regmap = chip->regmap; + + for (i = 0; i < PV88090_MAX_REGULATORS; i++) { + if (init_data) + config.init_data = &init_data[i]; + + if (i == PV88090_ID_BUCK2 || i == PV88090_ID_BUCK3) { + ret = regmap_read(chip->regmap, + pv88090_regulator_info[i].conf2, &conf2); + if (ret < 0) + return ret; + + conf2 = (conf2 >> PV88090_BUCK_VDAC_RANGE_SHIFT) & + PV88090_BUCK_VDAC_RANGE_MASK; + + ret = regmap_read(chip->regmap, + PV88090_REG_BUCK_FOLD_RANGE, &range); + if (ret < 0) + return ret; + + range = (range >> + (PV88080_BUCK_VRANGE_GAIN_SHIFT + i - 1)) & + PV88080_BUCK_VRANGE_GAIN_MASK; + index = ((range << 1) | conf2); + + pv88090_regulator_info[i].desc.min_uV + = pv88090_buck_vol[index].min_uV; + pv88090_regulator_info[i].desc.uV_step + = pv88090_buck_vol[index].uV_step; + pv88090_regulator_info[i].desc.n_voltages + = ((pv88090_buck_vol[index].max_uV) + - (pv88090_buck_vol[index].min_uV)) + /(pv88090_buck_vol[index].uV_step) + 1; + } + + config.driver_data = (void *)&pv88090_regulator_info[i]; + chip->rdev[i] = devm_regulator_register(chip->dev, + &pv88090_regulator_info[i].desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, + "Failed to register PV88090 regulator\n"); + return PTR_ERR(chip->rdev[i]); + } + } + + return 0; +} + +static const struct i2c_device_id pv88090_i2c_id[] = { + {"pv88090", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pv88090_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id pv88090_dt_ids[] = { + { .compatible = "pvs,pv88090", .data = &pv88090_i2c_id[0] }, + {}, +}; +MODULE_DEVICE_TABLE(of, pv88090_dt_ids); +#endif + +static struct i2c_driver pv88090_regulator_driver = { + .driver = { + .name = "pv88090", + .of_match_table = of_match_ptr(pv88090_dt_ids), + }, + .probe = pv88090_i2c_probe, + .id_table = pv88090_i2c_id, +}; + +module_i2c_driver(pv88090_regulator_driver); + +MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88090"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/pv88090-regulator.h b/drivers/regulator/pv88090-regulator.h new file mode 100644 index 000000000000..d7aca8d8266d --- /dev/null +++ b/drivers/regulator/pv88090-regulator.h @@ -0,0 +1,98 @@ +/* + * pv88090-regulator.h - Regulator definitions for PV88090 + * Copyright (C) 2015 Powerventure Semiconductor Ltd. + * + * 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. + */ + +#ifndef __PV88090_REGISTERS_H__ +#define __PV88090_REGISTERS_H__ + +/* System Control and Event Registers */ +#define PV88090_REG_EVENT_A 0x03 +#define PV88090_REG_MASK_A 0x06 +#define PV88090_REG_MASK_B 0x07 + +/* Regulator Registers */ +#define PV88090_REG_BUCK1_CONF0 0x18 +#define PV88090_REG_BUCK1_CONF1 0x19 +#define PV88090_REG_BUCK1_CONF2 0x1a +#define PV88090_REG_BUCK2_CONF0 0x1b +#define PV88090_REG_BUCK2_CONF1 0x1c +#define PV88090_REG_BUCK2_CONF2 0x58 +#define PV88090_REG_BUCK3_CONF0 0x1d +#define PV88090_REG_BUCK3_CONF1 0x1e +#define PV88090_REG_BUCK3_CONF2 0x5c + +#define PV88090_REG_LDO1_CONT 0x1f +#define PV88090_REG_LDO2_CONT 0x20 +#define PV88090_REG_LDO3_CONT 0x21 +#define PV88090_REG_BUCK_FOLD_RANGE 0x61 + +/* PV88090_REG_EVENT_A (addr=0x03) */ +#define PV88090_E_VDD_FLT 0x01 +#define PV88090_E_OVER_TEMP 0x02 + +/* PV88090_REG_MASK_A (addr=0x06) */ +#define PV88090_M_VDD_FLT 0x01 +#define PV88090_M_OVER_TEMP 0x02 + +/* PV88090_REG_BUCK1_CONF0 (addr=0x18) */ +#define PV88090_BUCK1_EN 0x80 +#define PV88090_VBUCK1_MASK 0x7F +/* PV88090_REG_BUCK2_CONF0 (addr=0x1b) */ +#define PV88090_BUCK2_EN 0x80 +#define PV88090_VBUCK2_MASK 0x7F +/* PV88090_REG_BUCK3_CONF0 (addr=0x1d) */ +#define PV88090_BUCK3_EN 0x80 +#define PV88090_VBUCK3_MASK 0x7F +/* PV88090_REG_LDO1_CONT (addr=0x1f) */ +#define PV88090_LDO1_EN 0x40 +#define PV88090_VLDO1_MASK 0x3F +/* PV88090_REG_LDO2_CONT (addr=0x20) */ +#define PV88090_LDO2_EN 0x40 +#define PV88090_VLDO2_MASK 0x3F + +/* PV88090_REG_BUCK1_CONF1 (addr=0x19) */ +#define PV88090_BUCK1_ILIM_SHIFT 2 +#define PV88090_BUCK1_ILIM_MASK 0x7C +#define PV88090_BUCK1_MODE_MASK 0x03 + +/* PV88090_REG_BUCK2_CONF1 (addr=0x1c) */ +#define PV88090_BUCK2_ILIM_SHIFT 2 +#define PV88090_BUCK2_ILIM_MASK 0x0C +#define PV88090_BUCK2_MODE_MASK 0x03 + +/* PV88090_REG_BUCK3_CONF1 (addr=0x1e) */ +#define PV88090_BUCK3_ILIM_SHIFT 2 +#define PV88090_BUCK3_ILIM_MASK 0x0C +#define PV88090_BUCK3_MODE_MASK 0x03 + +#define PV88090_BUCK_MODE_SLEEP 0x00 +#define PV88090_BUCK_MODE_AUTO 0x01 +#define PV88090_BUCK_MODE_SYNC 0x02 + +/* PV88090_REG_BUCK2_CONF2 (addr=0x58) */ +/* PV88090_REG_BUCK3_CONF2 (addr=0x5c) */ +#define PV88090_BUCK_VDAC_RANGE_SHIFT 7 +#define PV88090_BUCK_VDAC_RANGE_MASK 0x01 + +#define PV88090_BUCK_VDAC_RANGE_1 0x00 +#define PV88090_BUCK_VDAC_RANGE_2 0x01 + +/* PV88090_REG_BUCK_FOLD_RANGE (addr=0x61) */ +#define PV88080_BUCK_VRANGE_GAIN_SHIFT 3 +#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01 + +#define PV88080_BUCK_VRANGE_GAIN_1 0x00 +#define PV88080_BUCK_VRANGE_GAIN_2 0x01 + +#endif /* __PV88090_REGISTERS_H__ */ diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 6fa0c7d13290..56a17ec5b5ef 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -153,6 +153,49 @@ static const struct regulator_ops rpm_switch_ops = { .is_enabled = rpm_reg_is_enabled, }; +static const struct regulator_desc pma8084_hfsmps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500), + REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000), + }, + .n_linear_ranges = 2, + .n_voltages = 159, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pma8084_ftsmps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000), + REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000), + }, + .n_linear_ranges = 2, + .n_voltages = 340, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pma8084_pldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(750000, 0, 30, 25000), + REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000), + }, + .n_linear_ranges = 2, + .n_voltages = 100, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pma8084_nldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 64, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pma8084_switch = { + .ops = &rpm_switch_ops, +}; + static const struct regulator_desc pm8x41_hfsmps = { .linear_ranges = (struct regulator_linear_range[]) { REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500), @@ -211,6 +254,43 @@ static const struct regulator_desc pm8941_switch = { .ops = &rpm_switch_ops, }; +static const struct regulator_desc pm8916_pldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(750000, 0, 208, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 209, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8916_nldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(375000, 0, 93, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 94, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8916_buck_lvo_smps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500), + REGULATOR_LINEAR_RANGE(750000, 96, 127, 25000), + }, + .n_linear_ranges = 2, + .n_voltages = 128, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8916_buck_hvo_smps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(1550000, 0, 31, 25000), + }, + .n_linear_ranges = 1, + .n_voltages = 32, + .ops = &rpm_smps_ldo_ops, +}; + struct rpm_regulator_data { const char *name; u32 type; @@ -231,6 +311,32 @@ static const struct rpm_regulator_data rpm_pm8841_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pm8916_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8916_buck_lvo_smps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8916_buck_lvo_smps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8916_buck_lvo_smps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8916_buck_hvo_smps, "vdd_s4" }, + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8916_nldo, "vdd_l1_l2_l3" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8916_nldo, "vdd_l1_l2_l3" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8916_nldo, "vdd_l1_l2_l3" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8916_pldo, "vdd_l4_l5_l6" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8916_pldo, "vdd_l4_l5_l6" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8916_pldo, "vdd_l4_l5_l6" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8916_pldo, "vdd_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"}, + {} +}; + static const struct rpm_regulator_data rpm_pm8941_regulators[] = { { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8x41_hfsmps, "vdd_s1" }, { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8x41_hfsmps, "vdd_s2" }, @@ -272,9 +378,62 @@ static const struct rpm_regulator_data rpm_pm8941_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pma8084_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pma8084_ftsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pma8084_ftsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pma8084_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pma8084_hfsmps, "vdd_s4" }, + { "s5", QCOM_SMD_RPM_SMPA, 5, &pma8084_hfsmps, "vdd_s5" }, + { "s6", QCOM_SMD_RPM_SMPA, 6, &pma8084_ftsmps, "vdd_s6" }, + { "s7", QCOM_SMD_RPM_SMPA, 7, &pma8084_ftsmps, "vdd_s7" }, + { "s8", QCOM_SMD_RPM_SMPA, 8, &pma8084_ftsmps, "vdd_s8" }, + { "s9", QCOM_SMD_RPM_SMPA, 9, &pma8084_ftsmps, "vdd_s9" }, + { "s10", QCOM_SMD_RPM_SMPA, 10, &pma8084_ftsmps, "vdd_s10" }, + { "s11", QCOM_SMD_RPM_SMPA, 11, &pma8084_ftsmps, "vdd_s11" }, + { "s12", QCOM_SMD_RPM_SMPA, 12, &pma8084_ftsmps, "vdd_s12" }, + + { "l1", QCOM_SMD_RPM_LDOA, 1, &pma8084_nldo, "vdd_l1_l11" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pma8084_nldo, "vdd_l2_l3_l4_l27" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pma8084_nldo, "vdd_l2_l3_l4_l27" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pma8084_nldo, "vdd_l2_l3_l4_l27" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pma8084_pldo, "vdd_l5_l7" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pma8084_pldo, "vdd_l5_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pma8084_pldo, "vdd_l8" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pma8084_nldo, "vdd_l1_l11" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" }, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pma8084_pldo, "vdd_l16_l25" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pma8084_pldo, "vdd_l17" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pma8084_pldo, "vdd_l18" }, + { "l19", QCOM_SMD_RPM_LDOA, 19, &pma8084_pldo, "vdd_l19" }, + { "l20", QCOM_SMD_RPM_LDOA, 20, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" }, + { "l21", QCOM_SMD_RPM_LDOA, 21, &pma8084_pldo, "vdd_l21" }, + { "l22", QCOM_SMD_RPM_LDOA, 22, &pma8084_pldo, "vdd_l22" }, + { "l23", QCOM_SMD_RPM_LDOA, 23, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" }, + { "l24", QCOM_SMD_RPM_LDOA, 24, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" }, + { "l25", QCOM_SMD_RPM_LDOA, 25, &pma8084_pldo, "vdd_l16_l25" }, + { "l26", QCOM_SMD_RPM_LDOA, 26, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" }, + { "l27", QCOM_SMD_RPM_LDOA, 27, &pma8084_nldo, "vdd_l2_l3_l4_l27" }, + + { "lvs1", QCOM_SMD_RPM_VSA, 1, &pma8084_switch }, + { "lvs2", QCOM_SMD_RPM_VSA, 2, &pma8084_switch }, + { "lvs3", QCOM_SMD_RPM_VSA, 3, &pma8084_switch }, + { "lvs4", QCOM_SMD_RPM_VSA, 4, &pma8084_switch }, + { "5vs1", QCOM_SMD_RPM_VSA, 5, &pma8084_switch }, + + {} +}; + static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators }, + { .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators }, { .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators }, + { .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators }, {} }; MODULE_DEVICE_TABLE(of, rpm_of_match); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 72fc3c32db49..3242ffc0cb25 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -32,6 +32,7 @@ #include <linux/mfd/samsung/s2mps11.h> #include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> +#include <linux/mfd/samsung/s2mps15.h> #include <linux/mfd/samsung/s2mpu02.h> /* The highest number of possible regulators for supported devices. */ @@ -661,6 +662,133 @@ static const struct regulator_desc s2mps14_regulators[] = { S2MPS14_BUCK1235_START_SEL), }; +static struct regulator_ops s2mps15_reg_ldo_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static struct regulator_ops s2mps15_reg_buck_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +#define regulator_desc_s2mps15_ldo(num, range) { \ + .name = "LDO"#num, \ + .id = S2MPS15_LDO##num, \ + .ops = &s2mps15_reg_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .linear_ranges = range, \ + .n_linear_ranges = ARRAY_SIZE(range), \ + .n_voltages = S2MPS15_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS15_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS15_LDO_VSEL_MASK, \ + .enable_reg = S2MPS15_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS15_ENABLE_MASK \ +} + +#define regulator_desc_s2mps15_buck(num, range) { \ + .name = "BUCK"#num, \ + .id = S2MPS15_BUCK##num, \ + .ops = &s2mps15_reg_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .linear_ranges = range, \ + .n_linear_ranges = ARRAY_SIZE(range), \ + .ramp_delay = 12500, \ + .n_voltages = S2MPS15_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPS15_REG_B1CTRL2 + ((num - 1) * 2), \ + .vsel_mask = S2MPS15_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS15_REG_B1CTRL1 + ((num - 1) * 2), \ + .enable_mask = S2MPS15_ENABLE_MASK \ +} + +/* voltage range for s2mps15 LDO 3, 5, 15, 16, 18, 20, 23 and 27 */ +static const struct regulator_linear_range s2mps15_ldo_voltage_ranges1[] = { + REGULATOR_LINEAR_RANGE(1000000, 0xc, 0x38, 25000), +}; + +/* voltage range for s2mps15 LDO 2, 6, 14, 17, 19, 21, 24 and 25 */ +static const struct regulator_linear_range s2mps15_ldo_voltage_ranges2[] = { + REGULATOR_LINEAR_RANGE(1800000, 0x0, 0x3f, 25000), +}; + +/* voltage range for s2mps15 LDO 4, 11, 12, 13, 22 and 26 */ +static const struct regulator_linear_range s2mps15_ldo_voltage_ranges3[] = { + REGULATOR_LINEAR_RANGE(700000, 0x0, 0x34, 12500), +}; + +/* voltage range for s2mps15 LDO 7, 8, 9 and 10 */ +static const struct regulator_linear_range s2mps15_ldo_voltage_ranges4[] = { + REGULATOR_LINEAR_RANGE(700000, 0xc, 0x18, 25000), +}; + +/* voltage range for s2mps15 LDO 1 */ +static const struct regulator_linear_range s2mps15_ldo_voltage_ranges5[] = { + REGULATOR_LINEAR_RANGE(500000, 0x0, 0x20, 12500), +}; + +/* voltage range for s2mps15 BUCK 1, 2, 3, 4, 5, 6 and 7 */ +static const struct regulator_linear_range s2mps15_buck_voltage_ranges1[] = { + REGULATOR_LINEAR_RANGE(500000, 0x20, 0xb0, 6250), +}; + +/* voltage range for s2mps15 BUCK 8, 9 and 10 */ +static const struct regulator_linear_range s2mps15_buck_voltage_ranges2[] = { + REGULATOR_LINEAR_RANGE(1000000, 0x20, 0xc0, 12500), +}; + +static const struct regulator_desc s2mps15_regulators[] = { + regulator_desc_s2mps15_ldo(1, s2mps15_ldo_voltage_ranges5), + regulator_desc_s2mps15_ldo(2, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(3, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(4, s2mps15_ldo_voltage_ranges3), + regulator_desc_s2mps15_ldo(5, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(6, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(7, s2mps15_ldo_voltage_ranges4), + regulator_desc_s2mps15_ldo(8, s2mps15_ldo_voltage_ranges4), + regulator_desc_s2mps15_ldo(9, s2mps15_ldo_voltage_ranges4), + regulator_desc_s2mps15_ldo(10, s2mps15_ldo_voltage_ranges4), + regulator_desc_s2mps15_ldo(11, s2mps15_ldo_voltage_ranges3), + regulator_desc_s2mps15_ldo(12, s2mps15_ldo_voltage_ranges3), + regulator_desc_s2mps15_ldo(13, s2mps15_ldo_voltage_ranges3), + regulator_desc_s2mps15_ldo(14, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(15, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(16, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(17, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(18, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(19, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(20, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(21, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(22, s2mps15_ldo_voltage_ranges3), + regulator_desc_s2mps15_ldo(23, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_ldo(24, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(25, s2mps15_ldo_voltage_ranges2), + regulator_desc_s2mps15_ldo(26, s2mps15_ldo_voltage_ranges3), + regulator_desc_s2mps15_ldo(27, s2mps15_ldo_voltage_ranges1), + regulator_desc_s2mps15_buck(1, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(2, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(3, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(4, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(5, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(6, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(7, s2mps15_buck_voltage_ranges1), + regulator_desc_s2mps15_buck(8, s2mps15_buck_voltage_ranges2), + regulator_desc_s2mps15_buck(9, s2mps15_buck_voltage_ranges2), + regulator_desc_s2mps15_buck(10, s2mps15_buck_voltage_ranges2), +}; + static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, struct regulator_dev *rdev) { @@ -974,6 +1102,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) regulators = s2mps14_regulators; BUILD_BUG_ON(S2MPS_REGULATOR_MAX < s2mps11->rdev_num); break; + case S2MPS15X: + s2mps11->rdev_num = ARRAY_SIZE(s2mps15_regulators); + regulators = s2mps15_regulators; + break; case S2MPU02: s2mps11->rdev_num = ARRAY_SIZE(s2mpu02_regulators); regulators = s2mpu02_regulators; @@ -1067,10 +1199,11 @@ out: } static const struct platform_device_id s2mps11_pmic_id[] = { - { "s2mps11-pmic", S2MPS11X}, - { "s2mps13-pmic", S2MPS13X}, - { "s2mps14-pmic", S2MPS14X}, - { "s2mpu02-pmic", S2MPU02}, + { "s2mps11-regulator", S2MPS11X}, + { "s2mps13-regulator", S2MPS13X}, + { "s2mps14-regulator", S2MPS14X}, + { "s2mps15-regulator", S2MPS15X}, + { "s2mpu02-regulator", S2MPU02}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id); @@ -1097,5 +1230,5 @@ module_exit(s2mps11_pmic_exit); /* Module information */ MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); -MODULE_DESCRIPTION("SAMSUNG S2MPS11/S2MPS14/S2MPU02 Regulator Driver"); +MODULE_DESCRIPTION("SAMSUNG S2MPS11/S2MPS14/S2MPS15/S2MPU02 Regulator Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index ddc4f10e268a..584ef3dedca6 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -27,90 +27,12 @@ static const unsigned int tps6105x_voltages[] = { 5000000, /* There is an additional 5V */ }; -static int tps6105x_regulator_enable(struct regulator_dev *rdev) -{ - struct tps6105x *tps6105x = rdev_get_drvdata(rdev); - int ret; - - /* Activate voltage mode */ - ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, - TPS6105X_REG0_MODE_MASK, - TPS6105X_REG0_MODE_VOLTAGE << TPS6105X_REG0_MODE_SHIFT); - if (ret) - return ret; - - return 0; -} - -static int tps6105x_regulator_disable(struct regulator_dev *rdev) -{ - struct tps6105x *tps6105x = rdev_get_drvdata(rdev); - int ret; - - /* Set into shutdown mode */ - ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, - TPS6105X_REG0_MODE_MASK, - TPS6105X_REG0_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); - if (ret) - return ret; - - return 0; -} - -static int tps6105x_regulator_is_enabled(struct regulator_dev *rdev) -{ - struct tps6105x *tps6105x = rdev_get_drvdata(rdev); - unsigned int regval; - int ret; - - ret = regmap_read(tps6105x->regmap, TPS6105X_REG_0, ®val); - if (ret) - return ret; - regval &= TPS6105X_REG0_MODE_MASK; - regval >>= TPS6105X_REG0_MODE_SHIFT; - - if (regval == TPS6105X_REG0_MODE_VOLTAGE) - return 1; - - return 0; -} - -static int tps6105x_regulator_get_voltage_sel(struct regulator_dev *rdev) -{ - struct tps6105x *tps6105x = rdev_get_drvdata(rdev); - unsigned int regval; - int ret; - - ret = regmap_read(tps6105x->regmap, TPS6105X_REG_0, ®val); - if (ret) - return ret; - - regval &= TPS6105X_REG0_VOLTAGE_MASK; - regval >>= TPS6105X_REG0_VOLTAGE_SHIFT; - return (int) regval; -} - -static int tps6105x_regulator_set_voltage_sel(struct regulator_dev *rdev, - unsigned selector) -{ - struct tps6105x *tps6105x = rdev_get_drvdata(rdev); - int ret; - - ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, - TPS6105X_REG0_VOLTAGE_MASK, - selector << TPS6105X_REG0_VOLTAGE_SHIFT); - if (ret) - return ret; - - return 0; -} - static struct regulator_ops tps6105x_regulator_ops = { - .enable = tps6105x_regulator_enable, - .disable = tps6105x_regulator_disable, - .is_enabled = tps6105x_regulator_is_enabled, - .get_voltage_sel = tps6105x_regulator_get_voltage_sel, - .set_voltage_sel = tps6105x_regulator_set_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_table, }; @@ -122,6 +44,12 @@ static const struct regulator_desc tps6105x_regulator_desc = { .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(tps6105x_voltages), .volt_table = tps6105x_voltages, + .vsel_reg = TPS6105X_REG_0, + .vsel_mask = TPS6105X_REG0_VOLTAGE_MASK, + .enable_reg = TPS6105X_REG_0, + .enable_mask = TPS6105X_REG0_MODE_MASK, + .enable_val = TPS6105X_REG0_MODE_VOLTAGE << + TPS6105X_REG0_MODE_SHIFT, }; /* @@ -144,6 +72,7 @@ static int tps6105x_regulator_probe(struct platform_device *pdev) config.dev = &tps6105x->client->dev; config.init_data = pdata->regulator_data; config.driver_data = tps6105x; + config.regmap = tps6105x->regmap; /* Register regulator with framework */ tps6105x->regulator = devm_regulator_register(&pdev->dev, diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c new file mode 100644 index 000000000000..33f389d583ef --- /dev/null +++ b/drivers/regulator/tps65086-regulator.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * + * Author: Andrew F. Davis <afd@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + * + * Based on the TPS65912 driver + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> + +#include <linux/mfd/tps65086.h> + +enum tps65086_regulators { BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, LDOA1, + LDOA2, LDOA3, SWA1, SWB1, SWB2, VTT }; + +#define TPS65086_REGULATOR(_name, _of, _id, _nv, _vr, _vm, _er, _em, _lr, _dr, _dm) \ + [_id] = { \ + .desc = { \ + .name = _name, \ + .of_match = of_match_ptr(_of), \ + .regulators_node = "regulators", \ + .of_parse_cb = tps65086_of_parse_cb, \ + .id = _id, \ + .ops = ®_ops, \ + .n_voltages = _nv, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = _er, \ + .enable_mask = _em, \ + .volt_table = NULL, \ + .linear_ranges = _lr, \ + .n_linear_ranges = ARRAY_SIZE(_lr), \ + }, \ + .decay_reg = _dr, \ + .decay_mask = _dm, \ + } + +#define TPS65086_SWITCH(_name, _of, _id, _er, _em) \ + [_id] = { \ + .desc = { \ + .name = _name, \ + .of_match = of_match_ptr(_of), \ + .regulators_node = "regulators", \ + .of_parse_cb = tps65086_of_parse_cb, \ + .id = _id, \ + .ops = &switch_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_reg = _er, \ + .enable_mask = _em, \ + }, \ + } + +struct tps65086_regulator { + struct regulator_desc desc; + unsigned int decay_reg; + unsigned int decay_mask; +}; + +static const struct regulator_linear_range tps65086_buck126_10mv_ranges[] = { + REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), + REGULATOR_LINEAR_RANGE(410000, 0x1, 0x7F, 10000), +}; + +static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = { + REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), + REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0), + REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000), +}; + +static const struct regulator_linear_range tps65086_buck345_ranges[] = { + REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), + REGULATOR_LINEAR_RANGE(425000, 0x1, 0x7F, 25000), +}; + +static const struct regulator_linear_range tps65086_ldoa1_ranges[] = { + REGULATOR_LINEAR_RANGE(1350000, 0x0, 0x0, 0), + REGULATOR_LINEAR_RANGE(1500000, 0x1, 0x7, 100000), + REGULATOR_LINEAR_RANGE(2300000, 0x8, 0xA, 100000), + REGULATOR_LINEAR_RANGE(2700000, 0xB, 0xD, 150000), + REGULATOR_LINEAR_RANGE(3300000, 0xE, 0xE, 0), +}; + +static const struct regulator_linear_range tps65086_ldoa23_ranges[] = { + REGULATOR_LINEAR_RANGE(700000, 0x0, 0xD, 50000), + REGULATOR_LINEAR_RANGE(1400000, 0xE, 0xF, 100000), +}; + +/* Operations permitted on regulators */ +static struct regulator_ops reg_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, +}; + +/* Operations permitted on load switches */ +static struct regulator_ops switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static int tps65086_of_parse_cb(struct device_node *dev, + const struct regulator_desc *desc, + struct regulator_config *config); + +static struct tps65086_regulator regulators[] = { + TPS65086_REGULATOR("BUCK1", "buck1", BUCK1, 0x80, TPS65086_BUCK1CTRL, + BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(0), + tps65086_buck126_10mv_ranges, TPS65086_BUCK1CTRL, + BIT(0)), + TPS65086_REGULATOR("BUCK2", "buck2", BUCK2, 0x80, TPS65086_BUCK2CTRL, + BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(1), + tps65086_buck126_10mv_ranges, TPS65086_BUCK2CTRL, + BIT(0)), + TPS65086_REGULATOR("BUCK3", "buck3", BUCK3, 0x80, TPS65086_BUCK3VID, + BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(2), + tps65086_buck345_ranges, TPS65086_BUCK3DECAY, + BIT(0)), + TPS65086_REGULATOR("BUCK4", "buck4", BUCK4, 0x80, TPS65086_BUCK4VID, + BUCK_VID_MASK, TPS65086_BUCK4CTRL, BIT(0), + tps65086_buck345_ranges, TPS65086_BUCK4VID, + BIT(0)), + TPS65086_REGULATOR("BUCK5", "buck5", BUCK5, 0x80, TPS65086_BUCK5VID, + BUCK_VID_MASK, TPS65086_BUCK5CTRL, BIT(0), + tps65086_buck345_ranges, TPS65086_BUCK5CTRL, + BIT(0)), + TPS65086_REGULATOR("BUCK6", "buck6", BUCK6, 0x80, TPS65086_BUCK6VID, + BUCK_VID_MASK, TPS65086_BUCK6CTRL, BIT(0), + tps65086_buck126_10mv_ranges, TPS65086_BUCK6CTRL, + BIT(0)), + TPS65086_REGULATOR("LDOA1", "ldoa1", LDOA1, 0xF, TPS65086_LDOA1CTRL, + VDOA1_VID_MASK, TPS65086_LDOA1CTRL, BIT(0), + tps65086_ldoa1_ranges, 0, 0), + TPS65086_REGULATOR("LDOA2", "ldoa2", LDOA2, 0x10, TPS65086_LDOA2VID, + VDOA23_VID_MASK, TPS65086_LDOA2CTRL, BIT(0), + tps65086_ldoa23_ranges, 0, 0), + TPS65086_REGULATOR("LDOA3", "ldoa3", LDOA3, 0x10, TPS65086_LDOA3VID, + VDOA23_VID_MASK, TPS65086_LDOA3CTRL, BIT(0), + tps65086_ldoa23_ranges, 0, 0), + TPS65086_SWITCH("SWA1", "swa1", SWA1, TPS65086_SWVTT_EN, BIT(5)), + TPS65086_SWITCH("SWB1", "swa2", SWB1, TPS65086_SWVTT_EN, BIT(6)), + TPS65086_SWITCH("SWB2", "swa3", SWB2, TPS65086_SWVTT_EN, BIT(7)), + TPS65086_SWITCH("VTT", "vtt", VTT, TPS65086_SWVTT_EN, BIT(4)), +}; + +static inline bool has_25mv_mode(int id) +{ + switch (id) { + case BUCK1: + case BUCK2: + case BUCK6: + return true; + default: + return false; + } +} + +static int tps65086_of_parse_cb(struct device_node *dev, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + int ret; + + /* Check for 25mV step mode */ + if (has_25mv_mode(desc->id) && + of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) { + regulators[desc->id].desc.linear_ranges = + tps65086_buck126_25mv_ranges; + regulators[desc->id].desc.n_linear_ranges = + ARRAY_SIZE(tps65086_buck126_25mv_ranges); + } + + /* Check for decay mode */ + if (desc->id <= BUCK6 && of_property_read_bool(config->of_node, "ti,regulator-decay")) { + ret = regmap_write_bits(config->regmap, + regulators[desc->id].decay_reg, + regulators[desc->id].decay_mask, + regulators[desc->id].decay_mask); + if (ret) { + dev_err(config->dev, "Error setting decay\n"); + return ret; + } + } + + return 0; +} + +static int tps65086_regulator_probe(struct platform_device *pdev) +{ + struct tps65086 *tps = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct regulator_dev *rdev; + int i; + + platform_set_drvdata(pdev, tps); + + config.dev = &pdev->dev; + config.dev->of_node = tps->dev->of_node; + config.driver_data = tps; + config.regmap = tps->regmap; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) { + rdev = devm_regulator_register(&pdev->dev, ®ulators[i].desc, + &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "failed to register %s regulator\n", + pdev->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id tps65086_regulator_id_table[] = { + { "tps65086-regulator", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65086_regulator_id_table); + +static struct platform_driver tps65086_regulator_driver = { + .driver = { + .name = "tps65086-regulator", + }, + .probe = tps65086_regulator_probe, + .id_table = tps65086_regulator_id_table, +}; +module_platform_driver(tps65086_regulator_driver); + +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); +MODULE_DESCRIPTION("TPS65086 Regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index a02c1b961039..a5e5634eeb9e 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -27,19 +27,22 @@ #include <linux/regulator/machine.h> #include <linux/mfd/tps65218.h> -enum tps65218_regulators { DCDC1, DCDC2, DCDC3, DCDC4, DCDC5, DCDC6, LDO1 }; +enum tps65218_regulators { DCDC1, DCDC2, DCDC3, DCDC4, + DCDC5, DCDC6, LDO1, LS3 }; -#define TPS65218_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _er, _em, \ - _lr, _nlr, _delay, _fuv) \ +#define TPS65218_REGULATOR(_name, _id, _type, _ops, _n, _vr, _vm, _er, _em, \ + _cr, _cm, _lr, _nlr, _delay, _fuv) \ { \ .name = _name, \ .id = _id, \ .ops = &_ops, \ .n_voltages = _n, \ - .type = REGULATOR_VOLTAGE, \ + .type = _type, \ .owner = THIS_MODULE, \ .vsel_reg = _vr, \ .vsel_mask = _vm, \ + .csel_reg = _cr, \ + .csel_mask = _cm, \ .enable_reg = _er, \ .enable_mask = _em, \ .volt_table = NULL, \ @@ -80,6 +83,7 @@ static struct tps_info tps65218_pmic_regs[] = { TPS65218_INFO(DCDC5, "DCDC5", 1000000, 1000000), TPS65218_INFO(DCDC6, "DCDC6", 1800000, 1800000), TPS65218_INFO(LDO1, "LDO1", 900000, 3400000), + TPS65218_INFO(LS3, "LS3", -1, -1), }; #define TPS65218_OF_MATCH(comp, label) \ @@ -96,6 +100,7 @@ static const struct of_device_id tps65218_of_match[] = { TPS65218_OF_MATCH("ti,tps65218-dcdc5", tps65218_pmic_regs[DCDC5]), TPS65218_OF_MATCH("ti,tps65218-dcdc6", tps65218_pmic_regs[DCDC6]), TPS65218_OF_MATCH("ti,tps65218-ldo1", tps65218_pmic_regs[LDO1]), + TPS65218_OF_MATCH("ti,tps65218-ls3", tps65218_pmic_regs[LS3]), { } }; MODULE_DEVICE_TABLE(of, tps65218_of_match); @@ -175,6 +180,68 @@ static struct regulator_ops tps65218_ldo1_dcdc34_ops = { .map_voltage = regulator_map_voltage_linear_range, }; +static const int ls3_currents[] = { 100, 200, 500, 1000 }; + +static int tps65218_pmic_set_input_current_lim(struct regulator_dev *dev, + int lim_uA) +{ + unsigned int index = 0; + unsigned int num_currents = ARRAY_SIZE(ls3_currents); + struct tps65218 *tps = rdev_get_drvdata(dev); + + while (index < num_currents && ls3_currents[index] != lim_uA) + index++; + + if (index == num_currents) + return -EINVAL; + + return tps65218_set_bits(tps, dev->desc->csel_reg, dev->desc->csel_mask, + index << 2, TPS65218_PROTECT_L1); +} + +static int tps65218_pmic_set_current_limit(struct regulator_dev *dev, + int min_uA, int max_uA) +{ + int index = 0; + unsigned int num_currents = ARRAY_SIZE(ls3_currents); + struct tps65218 *tps = rdev_get_drvdata(dev); + + while (index < num_currents && ls3_currents[index] < max_uA) + index++; + + index--; + + if (index < 0 || ls3_currents[index] < min_uA) + return -EINVAL; + + return tps65218_set_bits(tps, dev->desc->csel_reg, dev->desc->csel_mask, + index << 2, TPS65218_PROTECT_L1); +} + +static int tps65218_pmic_get_current_limit(struct regulator_dev *dev) +{ + int retval; + unsigned int index; + struct tps65218 *tps = rdev_get_drvdata(dev); + + retval = tps65218_reg_read(tps, dev->desc->csel_reg, &index); + if (retval < 0) + return retval; + + index = (index & dev->desc->csel_mask) >> 2; + + return ls3_currents[index]; +} + +static struct regulator_ops tps65218_ls3_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps65218_pmic_enable, + .disable = tps65218_pmic_disable, + .set_input_current_limit = tps65218_pmic_set_input_current_lim, + .set_current_limit = tps65218_pmic_set_current_limit, + .get_current_limit = tps65218_pmic_get_current_limit, +}; + /* Operations permitted on DCDC5, DCDC6 */ static struct regulator_ops tps65218_dcdc56_pmic_ops = { .is_enabled = regulator_is_enabled_regmap, @@ -183,36 +250,46 @@ static struct regulator_ops tps65218_dcdc56_pmic_ops = { }; static const struct regulator_desc regulators[] = { - TPS65218_REGULATOR("DCDC1", TPS65218_DCDC_1, tps65218_dcdc12_ops, 64, - TPS65218_REG_CONTROL_DCDC1, - TPS65218_CONTROL_DCDC1_MASK, - TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC1_EN, - dcdc1_dcdc2_ranges, 2, 4000, 0), - TPS65218_REGULATOR("DCDC2", TPS65218_DCDC_2, tps65218_dcdc12_ops, 64, - TPS65218_REG_CONTROL_DCDC2, - TPS65218_CONTROL_DCDC2_MASK, - TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC2_EN, - dcdc1_dcdc2_ranges, 2, 4000, 0), - TPS65218_REGULATOR("DCDC3", TPS65218_DCDC_3, tps65218_ldo1_dcdc34_ops, - 64, TPS65218_REG_CONTROL_DCDC3, + TPS65218_REGULATOR("DCDC1", TPS65218_DCDC_1, REGULATOR_VOLTAGE, + tps65218_dcdc12_ops, 64, TPS65218_REG_CONTROL_DCDC1, + TPS65218_CONTROL_DCDC1_MASK, TPS65218_REG_ENABLE1, + TPS65218_ENABLE1_DC1_EN, 0, 0, dcdc1_dcdc2_ranges, + 2, 4000, 0), + TPS65218_REGULATOR("DCDC2", TPS65218_DCDC_2, REGULATOR_VOLTAGE, + tps65218_dcdc12_ops, 64, TPS65218_REG_CONTROL_DCDC2, + TPS65218_CONTROL_DCDC2_MASK, TPS65218_REG_ENABLE1, + TPS65218_ENABLE1_DC2_EN, 0, 0, dcdc1_dcdc2_ranges, + 2, 4000, 0), + TPS65218_REGULATOR("DCDC3", TPS65218_DCDC_3, REGULATOR_VOLTAGE, + tps65218_ldo1_dcdc34_ops, 64, + TPS65218_REG_CONTROL_DCDC3, TPS65218_CONTROL_DCDC3_MASK, TPS65218_REG_ENABLE1, - TPS65218_ENABLE1_DC3_EN, ldo1_dcdc3_ranges, 2, 0, 0), - TPS65218_REGULATOR("DCDC4", TPS65218_DCDC_4, tps65218_ldo1_dcdc34_ops, - 53, TPS65218_REG_CONTROL_DCDC4, - TPS65218_CONTROL_DCDC4_MASK, - TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC4_EN, - dcdc4_ranges, 2, 0, 0), - TPS65218_REGULATOR("DCDC5", TPS65218_DCDC_5, tps65218_dcdc56_pmic_ops, - 1, -1, -1, TPS65218_REG_ENABLE1, - TPS65218_ENABLE1_DC5_EN, NULL, 0, 0, 1000000), - TPS65218_REGULATOR("DCDC6", TPS65218_DCDC_6, tps65218_dcdc56_pmic_ops, - 1, -1, -1, TPS65218_REG_ENABLE1, - TPS65218_ENABLE1_DC6_EN, NULL, 0, 0, 1800000), - TPS65218_REGULATOR("LDO1", TPS65218_LDO_1, tps65218_ldo1_dcdc34_ops, 64, + TPS65218_ENABLE1_DC3_EN, 0, 0, ldo1_dcdc3_ranges, 2, + 0, 0), + TPS65218_REGULATOR("DCDC4", TPS65218_DCDC_4, REGULATOR_VOLTAGE, + tps65218_ldo1_dcdc34_ops, 53, + TPS65218_REG_CONTROL_DCDC4, + TPS65218_CONTROL_DCDC4_MASK, TPS65218_REG_ENABLE1, + TPS65218_ENABLE1_DC4_EN, 0, 0, dcdc4_ranges, 2, + 0, 0), + TPS65218_REGULATOR("DCDC5", TPS65218_DCDC_5, REGULATOR_VOLTAGE, + tps65218_dcdc56_pmic_ops, 1, -1, -1, + TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC5_EN, 0, 0, + NULL, 0, 0, 1000000), + TPS65218_REGULATOR("DCDC6", TPS65218_DCDC_6, REGULATOR_VOLTAGE, + tps65218_dcdc56_pmic_ops, 1, -1, -1, + TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC6_EN, 0, 0, + NULL, 0, 0, 1800000), + TPS65218_REGULATOR("LDO1", TPS65218_LDO_1, REGULATOR_VOLTAGE, + tps65218_ldo1_dcdc34_ops, 64, TPS65218_REG_CONTROL_LDO1, TPS65218_CONTROL_LDO1_MASK, TPS65218_REG_ENABLE2, - TPS65218_ENABLE2_LDO1_EN, ldo1_dcdc3_ranges, + TPS65218_ENABLE2_LDO1_EN, 0, 0, ldo1_dcdc3_ranges, 2, 0, 0), + TPS65218_REGULATOR("LS3", TPS65218_LS_3, REGULATOR_CURRENT, + tps65218_ls3_ops, 0, 0, 0, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_LS3_EN, TPS65218_REG_CONFIG2, + TPS65218_CONFIG2_LS3ILIM_MASK, NULL, 0, 0, 0), }; static int tps65218_regulator_probe(struct platform_device *pdev) diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 8cbb82ceec40..5a5bc4bb08d2 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -365,7 +365,7 @@ static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev) return wm831x_dcdc_ilim[val]; } -static struct regulator_ops wm831x_buckv_ops = { +static const struct regulator_ops wm831x_buckv_ops = { .set_voltage_sel = wm831x_buckv_set_voltage_sel, .get_voltage_sel = wm831x_buckv_get_voltage_sel, .list_voltage = wm831x_buckv_list_voltage, @@ -585,7 +585,7 @@ static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, int uV) return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, sel); } -static struct regulator_ops wm831x_buckp_ops = { +static const struct regulator_ops wm831x_buckp_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, @@ -725,7 +725,7 @@ static int wm831x_boostp_get_status(struct regulator_dev *rdev) return REGULATOR_STATUS_OFF; } -static struct regulator_ops wm831x_boostp_ops = { +static const struct regulator_ops wm831x_boostp_ops = { .get_status = wm831x_boostp_get_status, .is_enabled = regulator_is_enabled_regmap, @@ -818,7 +818,7 @@ static struct platform_driver wm831x_boostp_driver = { #define WM831X_EPE_BASE 6 -static struct regulator_ops wm831x_epe_ops = { +static const struct regulator_ops wm831x_epe_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -884,35 +884,22 @@ static struct platform_driver wm831x_epe_driver = { }, }; +static struct platform_driver * const drivers[] = { + &wm831x_buckv_driver, + &wm831x_buckp_driver, + &wm831x_boostp_driver, + &wm831x_epe_driver, +}; + static int __init wm831x_dcdc_init(void) { - int ret; - ret = platform_driver_register(&wm831x_buckv_driver); - if (ret != 0) - pr_err("Failed to register WM831x BUCKV driver: %d\n", ret); - - ret = platform_driver_register(&wm831x_buckp_driver); - if (ret != 0) - pr_err("Failed to register WM831x BUCKP driver: %d\n", ret); - - ret = platform_driver_register(&wm831x_boostp_driver); - if (ret != 0) - pr_err("Failed to register WM831x BOOST driver: %d\n", ret); - - ret = platform_driver_register(&wm831x_epe_driver); - if (ret != 0) - pr_err("Failed to register WM831x EPE driver: %d\n", ret); - - return 0; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } subsys_initcall(wm831x_dcdc_init); static void __exit wm831x_dcdc_exit(void) { - platform_driver_unregister(&wm831x_epe_driver); - platform_driver_unregister(&wm831x_boostp_driver); - platform_driver_unregister(&wm831x_buckp_driver); - platform_driver_unregister(&wm831x_buckv_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_exit(wm831x_dcdc_exit); diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 1442828fcd9a..6dd891d7eee3 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -128,7 +128,7 @@ static int wm831x_isink_get_current(struct regulator_dev *rdev) return wm831x_isinkv_values[ret]; } -static struct regulator_ops wm831x_isink_ops = { +static const struct regulator_ops wm831x_isink_ops = { .is_enabled = wm831x_isink_is_enabled, .enable = wm831x_isink_enable, .disable = wm831x_isink_disable, diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 5a7b65e8a529..e4a6f888484e 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -198,7 +198,7 @@ static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev, } -static struct regulator_ops wm831x_gp_ldo_ops = { +static const struct regulator_ops wm831x_gp_ldo_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -409,7 +409,7 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev) return regulator_mode_to_status(ret); } -static struct regulator_ops wm831x_aldo_ops = { +static const struct regulator_ops wm831x_aldo_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -557,7 +557,7 @@ static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev) return REGULATOR_STATUS_OFF; } -static struct regulator_ops wm831x_alive_ldo_ops = { +static const struct regulator_ops wm831x_alive_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -653,32 +653,21 @@ static struct platform_driver wm831x_alive_ldo_driver = { }, }; +static struct platform_driver * const drivers[] = { + &wm831x_gp_ldo_driver, + &wm831x_aldo_driver, + &wm831x_alive_ldo_driver, +}; + static int __init wm831x_ldo_init(void) { - int ret; - - ret = platform_driver_register(&wm831x_gp_ldo_driver); - if (ret != 0) - pr_err("Failed to register WM831x GP LDO driver: %d\n", ret); - - ret = platform_driver_register(&wm831x_aldo_driver); - if (ret != 0) - pr_err("Failed to register WM831x ALDO driver: %d\n", ret); - - ret = platform_driver_register(&wm831x_alive_ldo_driver); - if (ret != 0) - pr_err("Failed to register WM831x alive LDO driver: %d\n", - ret); - - return 0; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } subsys_initcall(wm831x_ldo_init); static void __exit wm831x_ldo_exit(void) { - platform_driver_unregister(&wm831x_alive_ldo_driver); - platform_driver_unregister(&wm831x_aldo_driver); - platform_driver_unregister(&wm831x_gp_ldo_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_exit(wm831x_ldo_exit); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 95f6b040186e..da9106bd2109 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -941,7 +941,7 @@ static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, return mode; } -static struct regulator_ops wm8350_dcdc_ops = { +static const struct regulator_ops wm8350_dcdc_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, @@ -958,7 +958,7 @@ static struct regulator_ops wm8350_dcdc_ops = { .set_suspend_mode = wm8350_dcdc_set_suspend_mode, }; -static struct regulator_ops wm8350_dcdc2_5_ops = { +static const struct regulator_ops wm8350_dcdc2_5_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -966,7 +966,7 @@ static struct regulator_ops wm8350_dcdc2_5_ops = { .set_suspend_disable = wm8350_dcdc25_set_suspend_disable, }; -static struct regulator_ops wm8350_ldo_ops = { +static const struct regulator_ops wm8350_ldo_ops = { .map_voltage = regulator_map_voltage_linear_range, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -980,7 +980,7 @@ static struct regulator_ops wm8350_ldo_ops = { .set_suspend_disable = wm8350_ldo_set_suspend_disable, }; -static struct regulator_ops wm8350_isink_ops = { +static const struct regulator_ops wm8350_isink_ops = { .set_current_limit = wm8350_isink_set_current, .get_current_limit = wm8350_isink_get_current, .enable = wm8350_isink_enable, diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 82d829000851..fb1837657b64 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -24,7 +24,7 @@ static const struct regulator_linear_range wm8400_ldo_ranges[] = { REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000), }; -static struct regulator_ops wm8400_ldo_ops = { +static const struct regulator_ops wm8400_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -106,7 +106,7 @@ static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev, return REGULATOR_MODE_NORMAL; } -static struct regulator_ops wm8400_dcdc_ops = { +static const struct regulator_ops wm8400_dcdc_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 750e0bd61f9f..7a4ce6df4f22 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -36,7 +36,7 @@ struct wm8994_ldo { #define WM8994_LDO1_MAX_SELECTOR 0x7 #define WM8994_LDO2_MAX_SELECTOR 0x3 -static struct regulator_ops wm8994_ldo1_ops = { +static const struct regulator_ops wm8994_ldo1_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -69,7 +69,7 @@ static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, } } -static struct regulator_ops wm8994_ldo2_ops = { +static const struct regulator_ops wm8994_ldo2_ops = { .list_voltage = wm8994_ldo2_list_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, |