diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-03-18 21:37:37 +0000 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-03-18 21:37:37 +0000 |
commit | 63236f4038f7e14762114606d95769c32cf6cac1 (patch) | |
tree | 326303a0880c46ddadfc29d3bacad073ed1cef20 /drivers/regulator | |
parent | 16fbcc3b1139e90fe560fde5619169db74dc02c2 (diff) | |
parent | ca61a7bfcd1c68eba84a58070540684448216506 (diff) |
Merge remote-tracking branch 'regulator/topic/drivers' into regulator-next
Diffstat (limited to 'drivers/regulator')
33 files changed, 2971 insertions, 894 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7a61b17ddd0..a229de98ae6 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -74,13 +74,72 @@ config REGULATOR_GPIO and the platform has to provide a mapping of GPIO-states to target volts/amps. -config REGULATOR_BQ24022 - tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" +config REGULATOR_AD5398 + tristate "Analog Devices AD5398/AD5821 regulators" + depends on I2C help - This driver controls a TI bq24022 Charger attached via - GPIOs. The provided current regulator can enable/disable - charging select between 100 mA and 500 mA charging current - limit. + This driver supports AD5398 and AD5821 current regulator chips. + If building into module, its name is ad5398.ko. + +config REGULATOR_AAT2870 + tristate "AnalogicTech AAT2870 Regulators" + depends on MFD_AAT2870_CORE + help + If you have a AnalogicTech AAT2870 say Y to enable the + regulator driver. + +config REGULATOR_DA903X + tristate "Dialog Semiconductor DA9030/DA9034 regulators" + depends on PMIC_DA903X + help + Say y here to support the BUCKs and LDOs regulators found on + Dialog Semiconductor DA9030/DA9034 PMIC. + +config REGULATOR_DA9052 + tristate "Dialog Semiconductor DA9052/DA9053 regulators" + depends on PMIC_DA9052 + help + This driver supports the voltage regulators of DA9052-BC and + DA9053-AA/Bx PMIC. + +config REGULATOR_ANATOP + tristate "Freescale i.MX on-chip ANATOP LDO regulators" + depends on MFD_ANATOP + help + Say y here to support Freescale i.MX on-chip ANATOP LDOs + regulators. It is recommended that this option be + enabled on i.MX6 platform. + +config REGULATOR_MC13XXX_CORE + tristate + +config REGULATOR_MC13783 + tristate "Freescale MC13783 regulator driver" + depends on MFD_MC13783 + select REGULATOR_MC13XXX_CORE + help + Say y here to support the regulators found on the Freescale MC13783 + PMIC. + +config REGULATOR_MC13892 + tristate "Freescale MC13892 regulator driver" + depends on MFD_MC13XXX + select REGULATOR_MC13XXX_CORE + help + Say y here to support the regulators found on the Freescale MC13892 + PMIC. + +config REGULATOR_ISL6271A + tristate "Intersil ISL6271A Power regulator" + depends on I2C + help + This driver supports ISL6271A voltage regulator chip. + +config REGULATOR_88PM8607 + bool "Marvell 88PM8607 Power regulators" + depends on MFD_88PM860X=y + help + This driver supports 88PM8607 voltage regulator chips. config REGULATOR_MAX1586 tristate "Maxim 1586/1587 voltage regulator" @@ -136,61 +195,12 @@ config REGULATOR_MAX8998 via I2C bus. The provided regulator is suitable for S3C6410 and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. -config REGULATOR_TWL4030 - bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" - depends on TWL4030_CORE - help - This driver supports the voltage regulators provided by - this family of companion chips. - -config REGULATOR_WM831X - tristate "Wolfson Microelcronics WM831x PMIC regulators" - depends on MFD_WM831X - help - Support the voltage and current regulators of the WM831x series - of PMIC devices. - -config REGULATOR_WM8350 - tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC" - depends on MFD_WM8350 - help - This driver provides support for the voltage and current regulators - of the WM8350 AudioPlus PMIC. - -config REGULATOR_WM8400 - tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC" - depends on MFD_WM8400 - help - This driver provides support for the voltage regulators of the - WM8400 AudioPlus PMIC. - -config REGULATOR_WM8994 - tristate "Wolfson Microelectronics WM8994 CODEC" - depends on MFD_WM8994 - help - This driver provides support for the voltage regulators on the - WM8994 CODEC. - -config REGULATOR_DA903X - tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" - depends on PMIC_DA903X - help - Say y here to support the BUCKs and LDOs regulators found on - Dialog Semiconductor DA9030/DA9034 PMIC. - -config REGULATOR_DA9052 - tristate "Dialog DA9052/DA9053 regulators" - depends on PMIC_DA9052 - help - This driver supports the voltage regulators of DA9052-BC and - DA9053-AA/Bx PMIC. - -config REGULATOR_PCF50633 - tristate "PCF50633 regulator driver" - depends on MFD_PCF50633 +config REGULATOR_PCAP + tristate "Motorola PCAP2 regulator driver" + depends on EZX_PCAP help - Say Y here to support the voltage regulators and convertors - on PCF50633 + This driver provides support for the voltage regulators of the + PCAP2 PMIC. config REGULATOR_LP3971 tristate "National Semiconductors LP3971 PMIC regulator driver" @@ -206,31 +216,20 @@ config REGULATOR_LP3972 Say Y here to support the voltage regulators and convertors on National Semiconductors LP3972 PMIC -config REGULATOR_PCAP - tristate "PCAP2 regulator driver" - depends on EZX_PCAP - help - This driver provides support for the voltage regulators of the - PCAP2 PMIC. - -config REGULATOR_MC13XXX_CORE - tristate - -config REGULATOR_MC13783 - tristate "Support regulators on Freescale MC13783 PMIC" - depends on MFD_MC13783 - select REGULATOR_MC13XXX_CORE +config REGULATOR_PCF50633 + tristate "NXP PCF50633 regulator driver" + depends on MFD_PCF50633 help - Say y here to support the regulators found on the Freescale MC13783 - PMIC. + Say Y here to support the voltage regulators and convertors + on PCF50633 -config REGULATOR_MC13892 - tristate "Support regulators on Freescale MC13892 PMIC" - depends on MFD_MC13XXX - select REGULATOR_MC13XXX_CORE +config REGULATOR_S5M8767 + tristate "Samsung S5M8767A voltage regulator" + depends on MFD_S5M_CORE help - Say y here to support the regulators found on the Freescale MC13892 - PMIC. + This driver supports a Samsung S5M8767A voltage output regulator + via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and + supports DVS mode with 8bits of output voltage control. config REGULATOR_AB3100 tristate "ST-Ericsson AB3100 Regulator functions" @@ -241,6 +240,32 @@ config REGULATOR_AB3100 AB3100 analog baseband dealing with power regulators for the system. +config REGULATOR_AB8500 + bool "ST-Ericsson AB8500 Power Regulators" + depends on AB8500_CORE + help + This driver supports the regulators found on the ST-Ericsson mixed + signal AB8500 PMIC + +config REGULATOR_DBX500_PRCMU + bool + +config REGULATOR_DB8500_PRCMU + bool "ST-Ericsson DB8500 Voltage Domain Regulators" + depends on MFD_DB8500_PRCMU + select REGULATOR_DBX500_PRCMU + help + This driver supports the voltage domain regulators controlled by the + DB8500 PRCMU + +config REGULATOR_BQ24022 + tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" + help + This driver controls a TI bq24022 Charger attached via + GPIOs. The provided current regulator can enable/disable + charging select between 100 mA and 500 mA charging current + limit. + config REGULATOR_TPS6105X tristate "TI TPS6105X Power regulators" depends on TPS6105X @@ -250,6 +275,16 @@ config REGULATOR_TPS6105X It is a single boost converter primarily for white LEDs and audio amplifiers. +config REGULATOR_TPS62360 + tristate "TI TPS62360 Power Regulator" + depends on I2C + select REGMAP_I2C + help + This driver supports TPS62360 voltage regulator chip. This + regulator is meant for processor core supply. This chip is + high-frequency synchronous step down dc-dc converter optimized + for battery-powered portable applications. + config REGULATOR_TPS65023 tristate "TI TPS65023 Power regulators" depends on I2C @@ -267,73 +302,77 @@ 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_TPS65912 - tristate "TI TPS65912 Power regulator" - depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) +config REGULATOR_TPS65217 + tristate "TI TPS65217 Power regulators" + depends on MFD_TPS65217 help - This driver supports TPS65912 voltage regulator chip. + This driver supports TPS65217 voltage regulator chips. TPS65217 + provides three step-down converters and four general-purpose LDO + voltage regulators. It supports software based voltage control + for different voltage domains -config REGULATOR_88PM8607 - bool "Marvell 88PM8607 Power regulators" - depends on MFD_88PM860X=y +config REGULATOR_TPS6524X + tristate "TI TPS6524X Power regulators" + depends on SPI help - This driver supports 88PM8607 voltage regulator chips. + This driver supports TPS6524X voltage regulator chips. TPS6524X + provides three step-down converters and two general-purpose LDO + voltage regulators. This device is interfaced using a customized + serial interface currently supported on the sequencer serial + port controller. -config REGULATOR_ISL6271A - tristate "Intersil ISL6271A Power regulator" - depends on I2C +config REGULATOR_TPS6586X + tristate "TI TPS6586X Power regulators" + depends on MFD_TPS6586X help - This driver supports ISL6271A voltage regulator chip. + This driver supports TPS6586X voltage regulator chips. -config REGULATOR_AD5398 - tristate "Analog Devices AD5398/AD5821 regulators" - depends on I2C +config REGULATOR_TPS65910 + tristate "TI TPS65910/TPS65911 Power Regulators" + depends on MFD_TPS65910 help - This driver supports AD5398 and AD5821 current regulator chips. - If building into module, its name is ad5398.ko. + This driver supports TPS65910/TPS65911 voltage regulator chips. -config REGULATOR_AB8500 - bool "ST-Ericsson AB8500 Power Regulators" - depends on AB8500_CORE +config REGULATOR_TPS65912 + tristate "TI TPS65912 Power regulator" + depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) help - This driver supports the regulators found on the ST-Ericsson mixed - signal AB8500 PMIC + This driver supports TPS65912 voltage regulator chip. -config REGULATOR_DB8500_PRCMU - bool "ST-Ericsson DB8500 Voltage Domain Regulators" - depends on MFD_DB8500_PRCMU +config REGULATOR_TWL4030 + bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" + depends on TWL4030_CORE help - This driver supports the voltage domain regulators controlled by the - DB8500 PRCMU + This driver supports the voltage regulators provided by + this family of companion chips. -config REGULATOR_TPS6586X - tristate "TI TPS6586X Power regulators" - depends on MFD_TPS6586X +config REGULATOR_WM831X + tristate "Wolfson Microelectronics WM831x PMIC regulators" + depends on MFD_WM831X help - This driver supports TPS6586X voltage regulator chips. + Support the voltage and current regulators of the WM831x series + of PMIC devices. -config REGULATOR_TPS6524X - tristate "TI TPS6524X Power regulators" - depends on SPI +config REGULATOR_WM8350 + tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC" + depends on MFD_WM8350 help - This driver supports TPS6524X voltage regulator chips. TPS6524X - provides three step-down converters and two general-purpose LDO - voltage regulators. This device is interfaced using a customized - serial interface currently supported on the sequencer serial - port controller. + This driver provides support for the voltage and current regulators + of the WM8350 AudioPlus PMIC. -config REGULATOR_TPS65910 - tristate "TI TPS65910 Power Regulator" - depends on MFD_TPS65910 +config REGULATOR_WM8400 + tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC" + depends on MFD_WM8400 help - This driver supports TPS65910 voltage regulator chips. + This driver provides support for the voltage regulators of the + WM8400 AudioPlus PMIC. -config REGULATOR_AAT2870 - tristate "AnalogicTech AAT2870 Regulators" - depends on MFD_AAT2870_CORE +config REGULATOR_WM8994 + tristate "Wolfson Microelectronics WM8994 CODEC" + depends on MFD_WM8994 help - If you have a AnalogicTech AAT2870 say Y to enable the - regulator driver. + This driver provides support for the voltage regulators on the + WM8994 CODEC. endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 503bac87715..ab06474e5eb 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -10,43 +10,49 @@ obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o +obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o +obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o +obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o +obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_DA903X) += da903x.o +obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o +obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o +obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o +obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o -obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o -obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o -obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o -obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o -obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o -obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o -obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o -obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o -obj-$(CONFIG_REGULATOR_DA903X) += da903x.o -obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o -obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o -obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o -obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o +obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o +obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o +obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o 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_TPS65217) += tps65217-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o -obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o -obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o -obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o -obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o -obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o +obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o -obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o +obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o +obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o +obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o +obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o +obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o +obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o +obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o + ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index 685ad43b074..9ed5c5d84e1 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -31,7 +31,7 @@ #include <linux/mfd/aat2870.h> struct aat2870_regulator { - struct platform_device *pdev; + struct aat2870_data *aat2870; struct regulator_desc desc; const int *voltages; /* uV */ @@ -60,7 +60,7 @@ static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { struct aat2870_regulator *ri = rdev_get_drvdata(rdev); - struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + struct aat2870_data *aat2870 = ri->aat2870; return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask, selector << ri->voltage_shift); @@ -69,7 +69,7 @@ static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev, static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev) { struct aat2870_regulator *ri = rdev_get_drvdata(rdev); - struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + struct aat2870_data *aat2870 = ri->aat2870; u8 val; int ret; @@ -83,7 +83,7 @@ static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev) static int aat2870_ldo_enable(struct regulator_dev *rdev) { struct aat2870_regulator *ri = rdev_get_drvdata(rdev); - struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + struct aat2870_data *aat2870 = ri->aat2870; return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, ri->enable_mask); @@ -92,7 +92,7 @@ static int aat2870_ldo_enable(struct regulator_dev *rdev) static int aat2870_ldo_disable(struct regulator_dev *rdev) { struct aat2870_regulator *ri = rdev_get_drvdata(rdev); - struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + struct aat2870_data *aat2870 = ri->aat2870; return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0); } @@ -100,7 +100,7 @@ static int aat2870_ldo_disable(struct regulator_dev *rdev) static int aat2870_ldo_is_enabled(struct regulator_dev *rdev) { struct aat2870_regulator *ri = rdev_get_drvdata(rdev); - struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + struct aat2870_data *aat2870 = ri->aat2870; u8 val; int ret; @@ -185,7 +185,7 @@ static int aat2870_regulator_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id); return -EINVAL; } - ri->pdev = pdev; + ri->aat2870 = dev_get_drvdata(pdev->dev.parent); rdev = regulator_register(&ri->desc, &pdev->dev, pdev->dev.platform_data, ri, NULL); diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index c9b92531ae6..c7ee4c15d6f 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -201,7 +201,7 @@ static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector) return info->voltages[selector]; } -static int ab8500_regulator_get_voltage(struct regulator_dev *rdev) +static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev) { int ret, val; struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); @@ -229,11 +229,9 @@ static int ab8500_regulator_get_voltage(struct regulator_dev *rdev) /* vintcore has a different layout */ val = regval & info->voltage_mask; if (info->desc.id == AB8500_LDO_INTCORE) - ret = info->voltages[val >> 0x3]; + return val >> 0x3; else - ret = info->voltages[val]; - - return ret; + return val; } static int ab8500_get_best_voltage_index(struct regulator_dev *rdev, @@ -320,7 +318,7 @@ static struct regulator_ops ab8500_regulator_ops = { .enable = ab8500_regulator_enable, .disable = ab8500_regulator_disable, .is_enabled = ab8500_regulator_is_enabled, - .get_voltage = ab8500_regulator_get_voltage, + .get_voltage_sel = ab8500_regulator_get_voltage_sel, .set_voltage = ab8500_regulator_set_voltage, .list_voltage = ab8500_list_voltage, .enable_time = ab8500_regulator_enable_time, diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 483c8093085..26d23adfc66 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -94,8 +94,8 @@ static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int if (max_uA > chip->max_uA || max_uA < chip->min_uA) return -EINVAL; - selector = ((min_uA - chip->min_uA) * chip->current_level + - range_uA - 1) / range_uA; + selector = DIV_ROUND_UP((min_uA - chip->min_uA) * chip->current_level, + range_uA); if (ad5398_calc_current(chip, selector) > max_uA) return -EINVAL; diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c new file mode 100644 index 00000000000..17499a55113 --- /dev/null +++ b/drivers/regulator/anatop-regulator.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/mfd/anatop.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> + +struct anatop_regulator { + const char *name; + u32 control_reg; + struct anatop *mfd; + int vol_bit_shift; + int vol_bit_width; + int min_bit_val; + int min_voltage; + int max_voltage; + struct regulator_desc rdesc; + struct regulator_init_data *initdata; +}; + +static int anatop_set_voltage(struct regulator_dev *reg, int min_uV, + int max_uV, unsigned *selector) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + u32 val, sel; + int uv; + + uv = min_uV; + dev_dbg(®->dev, "%s: uv %d, min %d, max %d\n", __func__, + uv, anatop_reg->min_voltage, + anatop_reg->max_voltage); + + if (uv < anatop_reg->min_voltage) { + if (max_uV > anatop_reg->min_voltage) + uv = anatop_reg->min_voltage; + else + return -EINVAL; + } + + if (!anatop_reg->control_reg) + return -ENOTSUPP; + + sel = DIV_ROUND_UP(uv - anatop_reg->min_voltage, 25000); + if (sel * 25000 + anatop_reg->min_voltage > anatop_reg->max_voltage) + return -EINVAL; + val = anatop_reg->min_bit_val + sel; + *selector = sel; + dev_dbg(®->dev, "%s: calculated val %d\n", __func__, val); + anatop_set_bits(anatop_reg->mfd, + anatop_reg->control_reg, + anatop_reg->vol_bit_shift, + anatop_reg->vol_bit_width, + val); + + return 0; +} + +static int anatop_get_voltage_sel(struct regulator_dev *reg) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + u32 val; + + if (!anatop_reg->control_reg) + return -ENOTSUPP; + + val = anatop_get_bits(anatop_reg->mfd, + anatop_reg->control_reg, + anatop_reg->vol_bit_shift, + anatop_reg->vol_bit_width); + + return val - anatop_reg->min_bit_val; +} + +static int anatop_list_voltage(struct regulator_dev *reg, unsigned selector) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + int uv; + + uv = anatop_reg->min_voltage + selector * 25000; + dev_dbg(®->dev, "vddio = %d, selector = %u\n", uv, selector); + + return uv; +} + +static struct regulator_ops anatop_rops = { + .set_voltage = anatop_set_voltage, + .get_voltage_sel = anatop_get_voltage_sel, + .list_voltage = anatop_list_voltage, +}; + +static int __devinit anatop_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regulator_desc *rdesc; + struct regulator_dev *rdev; + struct anatop_regulator *sreg; + struct regulator_init_data *initdata; + struct anatop *anatopmfd = dev_get_drvdata(pdev->dev.parent); + int ret = 0; + + initdata = of_get_regulator_init_data(dev, np); + sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); + if (!sreg) + return -ENOMEM; + sreg->initdata = initdata; + sreg->name = kstrdup(of_get_property(np, "regulator-name", NULL), + GFP_KERNEL); + rdesc = &sreg->rdesc; + memset(rdesc, 0, sizeof(*rdesc)); + rdesc->name = sreg->name; + rdesc->ops = &anatop_rops; + rdesc->type = REGULATOR_VOLTAGE; + rdesc->owner = THIS_MODULE; + sreg->mfd = anatopmfd; + ret = of_property_read_u32(np, "reg", &sreg->control_reg); + if (ret) { + dev_err(dev, "no reg property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-vol-bit-width", + &sreg->vol_bit_width); + if (ret) { + dev_err(dev, "no anatop-vol-bit-width property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-vol-bit-shift", + &sreg->vol_bit_shift); + if (ret) { + dev_err(dev, "no anatop-vol-bit-shift property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-min-bit-val", + &sreg->min_bit_val); + if (ret) { + dev_err(dev, "no anatop-min-bit-val property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-min-voltage", + &sreg->min_voltage); + if (ret) { + dev_err(dev, "no anatop-min-voltage property set\n"); + goto anatop_probe_end; + } + ret = of_property_read_u32(np, "anatop-max-voltage", + &sreg->max_voltage); + if (ret) { + dev_err(dev, "no anatop-max-voltage property set\n"); + goto anatop_probe_end; + } + + rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage) + / 25000 + 1; + + /* register regulator */ + rdev = regulator_register(rdesc, dev, + initdata, sreg, pdev->dev.of_node); + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register %s\n", + rdesc->name); + ret = PTR_ERR(rdev); + goto anatop_probe_end; + } + + platform_set_drvdata(pdev, rdev); + +anatop_probe_end: + if (ret) + kfree(sreg->name); + + return ret; +} + +static int __devexit anatop_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct anatop_regulator *sreg = rdev_get_drvdata(rdev); + const char *name = sreg->name; + + regulator_unregister(rdev); + kfree(name); + + return 0; +} + +static struct of_device_id __devinitdata of_anatop_regulator_match_tbl[] = { + { .compatible = "fsl,anatop-regulator", }, + { /* end */ } +}; + +static struct platform_driver anatop_regulator = { + .driver = { + .name = "anatop_regulator", + .owner = THIS_MODULE, + .of_match_table = of_anatop_regulator_match_tbl, + }, + .probe = anatop_regulator_probe, + .remove = anatop_regulator_remove, +}; + +static int __init anatop_regulator_init(void) +{ + return platform_driver_register(&anatop_regulator); +} +postcore_initcall(anatop_regulator_init); + +static void __exit anatop_regulator_exit(void) +{ + platform_driver_unregister(&anatop_regulator); +} +module_exit(anatop_regulator_exit); + +MODULE_AUTHOR("Nancy Chen <Nancy.Chen@freescale.com>, " + "Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>"); +MODULE_DESCRIPTION("ANATOP Regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 8dbc54da7d7..1851f0929ef 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -119,7 +119,7 @@ static int da903x_set_ldo_voltage(struct regulator_dev *rdev, return -EINVAL; } - val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV); *selector = val; val <<= info->vol_shift; mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; @@ -202,7 +202,7 @@ static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev, return -EINVAL; } - val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV); *selector = val; val <<= info->vol_shift; mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; @@ -233,10 +233,10 @@ static int da9030_set_ldo14_voltage(struct regulator_dev *rdev, thresh = (info->max_uV + info->min_uV) / 2; if (min_uV < thresh) { - val = (thresh - min_uV + info->step_uV - 1) / info->step_uV; + val = DIV_ROUND_UP(thresh - min_uV, info->step_uV); val |= 0x4; } else { - val = (min_uV - thresh + info->step_uV - 1) / info->step_uV; + val = DIV_ROUND_UP(min_uV - thresh, info->step_uV); } *selector = val; @@ -281,7 +281,7 @@ static int da9034_set_dvc_voltage(struct regulator_dev *rdev, return -EINVAL; } - val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV); *selector = val; val <<= info->vol_shift; mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; @@ -307,7 +307,7 @@ static int da9034_set_ldo12_voltage(struct regulator_dev *rdev, return -EINVAL; } - val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV); val = (val >= 20) ? val - 12 : ((val > 7) ? 8 : val); *selector = val; val <<= info->vol_shift; diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 515443fcd26..4bd25e75efa 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -18,74 +18,11 @@ #include <linux/regulator/machine.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/module.h> - -/* - * power state reference count - */ -static int power_state_active_cnt; /* will initialize to zero */ -static DEFINE_SPINLOCK(power_state_active_lock); - -static void power_state_active_enable(void) -{ - unsigned long flags; - - spin_lock_irqsave(&power_state_active_lock, flags); - power_state_active_cnt++; - spin_unlock_irqrestore(&power_state_active_lock, flags); -} - -static int power_state_active_disable(void) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&power_state_active_lock, flags); - if (power_state_active_cnt <= 0) { - pr_err("power state: unbalanced enable/disable calls\n"); - ret = -EINVAL; - goto out; - } - - power_state_active_cnt--; -out: - spin_unlock_irqrestore(&power_state_active_lock, flags); - return ret; -} - -/* - * Exported interface for CPUIdle only. This function is called when interrupts - * are turned off. Hence, no locking. - */ -int power_state_active_is_enabled(void) -{ - return (power_state_active_cnt > 0); -} - -/** - * struct db8500_regulator_info - db8500 regulator information - * @dev: device pointer - * @desc: regulator description - * @rdev: regulator device pointer - * @is_enabled: status of the regulator - * @epod_id: id for EPOD (power domain) - * @is_ramret: RAM retention switch for EPOD (power domain) - * @operating_point: operating point (only for vape, to be removed) - * - */ -struct db8500_regulator_info { - struct device *dev; - struct regulator_desc desc; - struct regulator_dev *rdev; - bool is_enabled; - u16 epod_id; - bool is_ramret; - bool exclude_from_power_state; - unsigned int operating_point; -}; +#include "dbx500-prcmu.h" static int db8500_regulator_enable(struct regulator_dev *rdev) { - struct db8500_regulator_info *info = rdev_get_drvdata(rdev); + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); if (info == NULL) return -EINVAL; @@ -93,16 +30,18 @@ static int db8500_regulator_enable(struct regulator_dev *rdev) dev_vdbg(rdev_get_dev(rdev), "regulator-%s-enable\n", info->desc.name); - info->is_enabled = true; - if (!info->exclude_from_power_state) - power_state_active_enable(); + if (!info->is_enabled) { + info->is_enabled = true; + if (!info->exclude_from_power_state) + power_state_active_enable(); + } return 0; } static int db8500_regulator_disable(struct regulator_dev *rdev) { - struct db8500_regulator_info *info = rdev_get_drvdata(rdev); + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); int ret = 0; if (info == NULL) @@ -111,16 +50,18 @@ static int db8500_regulator_disable(struct regulator_dev *rdev) dev_vdbg(rdev_get_dev(rdev), "regulator-%s-disable\n", info->desc.name); - info->is_enabled = false; - if (!info->exclude_from_power_state) - ret = power_state_active_disable(); + if (info->is_enabled) { + info->is_enabled = false; + if (!info->exclude_from_power_state) + ret = power_state_active_disable(); + } return ret; } static int db8500_regulator_is_enabled(struct regulator_dev *rdev) { - struct db8500_regulator_info *info = rdev_get_drvdata(rdev); + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); if (info == NULL) return -EINVAL; @@ -197,7 +138,7 @@ static int disable_epod(u16 epod_id, bool ramret) */ static int db8500_regulator_switch_enable(struct regulator_dev *rdev) { - struct db8500_regulator_info *info = rdev_get_drvdata(rdev); + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); int ret; if (info == NULL) @@ -221,7 +162,7 @@ out: static int db8500_regulator_switch_disable(struct regulator_dev *rdev) { - struct db8500_regulator_info *info = rdev_get_drvdata(rdev); + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); int ret; if (info == NULL) @@ -245,7 +186,7 @@ out: static int db8500_regulator_switch_is_enabled(struct regulator_dev *rdev) { - struct db8500_regulator_info *info = rdev_get_drvdata(rdev); + struct dbx500_regulator_info *info = rdev_get_drvdata(rdev); if (info == NULL) return -EINVAL; @@ -266,8 +207,8 @@ static struct regulator_ops db8500_regulator_switch_ops = { /* * Regulator information */ -static struct db8500_regulator_info -db8500_regulator_info[DB8500_NUM_REGULATORS] = { +static struct dbx500_regulator_info +dbx500_regulator_info[DB8500_NUM_REGULATORS] = { [DB8500_REGULATOR_VAPE] = { .desc = { .name = "db8500-vape", @@ -476,12 +417,12 @@ static int __devinit db8500_regulator_probe(struct platform_device *pdev) int i, err; /* register all regulators */ - for (i = 0; i < ARRAY_SIZE(db8500_regulator_info); i++) { - struct db8500_regulator_info *info; + for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) { + struct dbx500_regulator_info *info; struct regulator_init_data *init_data = &db8500_init_data[i]; /* assign per-regulator data */ - info = &db8500_regulator_info[i]; + info = &dbx500_regulator_info[i]; info->dev = &pdev->dev; /* register with the regulator framework */ @@ -494,7 +435,7 @@ static int __devinit db8500_regulator_probe(struct platform_device *pdev) /* if failing, unregister all earlier regulators */ while (--i >= 0) { - info = &db8500_regulator_info[i]; + info = &dbx500_regulator_info[i]; regulator_unregister(info->rdev); } return err; @@ -503,17 +444,22 @@ static int __devinit db8500_regulator_probe(struct platform_device *pdev) dev_dbg(rdev_get_dev(info->rdev), "regulator-%s-probed\n", info->desc.name); } + err = ux500_regulator_debug_init(pdev, + dbx500_regulator_info, + ARRAY_SIZE(dbx500_regulator_info)); - return 0; + return err; } static int __exit db8500_regulator_remove(struct platform_device *pdev) { int i; - for (i = 0; i < ARRAY_SIZE(db8500_regulator_info); i++) { - struct db8500_regulator_info *info; - info = &db8500_regulator_info[i]; + ux500_regulator_debug_exit(); + + for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) { + struct dbx500_regulator_info *info; + info = &dbx500_regulator_info[i]; dev_vdbg(rdev_get_dev(info->rdev), "regulator-%s-remove\n", info->desc.name); diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c new file mode 100644 index 00000000000..f2e5ecdc586 --- /dev/null +++ b/drivers/regulator/dbx500-prcmu.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson + * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson + * + * UX500 common part of Power domain regulators + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/regulator/driver.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/slab.h> + +#include "dbx500-prcmu.h" + +/* + * power state reference count + */ +static int power_state_active_cnt; /* will initialize to zero */ +static DEFINE_SPINLOCK(power_state_active_lock); + +int power_state_active_get(void) +{ + unsigned long flags; + int cnt; + + spin_lock_irqsave(&power_state_active_lock, flags); + cnt = power_state_active_cnt; + spin_unlock_irqrestore(&power_state_active_lock, flags); + + return cnt; +} + +void power_state_active_enable(void) +{ + unsigned long flags; + + spin_lock_irqsave(&power_state_active_lock, flags); + power_state_active_cnt++; + spin_unlock_irqrestore(&power_state_active_lock, flags); +} + +int power_state_active_disable(void) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&power_state_active_lock, flags); + if (power_state_active_cnt <= 0) { + pr_err("power state: unbalanced enable/disable calls\n"); + ret = -EINVAL; + goto out; + } + + power_state_active_cnt--; +out: + spin_unlock_irqrestore(&power_state_active_lock, flags); + return ret; +} + +#ifdef CONFIG_REGULATOR_DEBUG + +static struct ux500_regulator_debug { + struct dentry *dir; + struct dentry *status_file; + struct dentry *power_state_cnt_file; + struct dbx500_regulator_info *regulator_array; + int num_regulators; + u8 *state_before_suspend; + u8 *state_after_suspend; +} rdebug; + +void ux500_regulator_suspend_debug(void) +{ + int i; + for (i = 0; i < rdebug.num_regulators; i++) + rdebug.state_before_suspend[i] = + rdebug.regulator_array[i].is_enabled; +} + +void ux500_regulator_resume_debug(void) +{ + int i; + for (i = 0; i < rdebug.num_regulators; i++) + rdebug.state_after_suspend[i] = + rdebug.regulator_array[i].is_enabled; +} + +static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) +{ + struct device *dev = s->private; + int err; + + /* print power state count */ + err = seq_printf(s, "ux500-regulator power state count: %i\n", + power_state_active_get()); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + + return 0; +} + +static int ux500_regulator_power_state_cnt_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ux500_regulator_power_state_cnt_print, + inode->i_private); +} + +static const struct file_operations ux500_regulator_power_state_cnt_fops = { + .open = ux500_regulator_power_state_cnt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ux500_regulator_status_print(struct seq_file *s, void *p) +{ + struct device *dev = s->private; + int err; + int i; + + /* print dump header */ + err = seq_printf(s, "ux500-regulator status:\n"); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + + err = seq_printf(s, "%31s : %8s : %8s\n", "current", + "before", "after"); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + + for (i = 0; i < rdebug.num_regulators; i++) { + struct dbx500_regulator_info *info; + /* Access per-regulator data */ + info = &rdebug.regulator_array[i]; + + /* print status */ + err = seq_printf(s, "%20s : %8s : %8s : %8s\n", info->desc.name, + info->is_enabled ? "enabled" : "disabled", + rdebug.state_before_suspend[i] ? "enabled" : "disabled", + rdebug.state_after_suspend[i] ? "enabled" : "disabled"); + if (err < 0) + dev_err(dev, "seq_printf overflow\n"); + } + + return 0; +} + +static int ux500_regulator_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, ux500_regulator_status_print, + inode->i_private); +} + +static const struct file_operations ux500_regulator_status_fops = { + .open = ux500_regulator_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +int __attribute__((weak)) dbx500_regulator_testcase( + struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + return 0; +} + +int __devinit +ux500_regulator_debug_init(struct platform_device *pdev, + struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + /* create directory */ + rdebug.dir = debugfs_create_dir("ux500-regulator", NULL); + if (!rdebug.dir) + goto exit_no_debugfs; + + /* create "status" file */ + rdebug.status_file = debugfs_create_file("status", + S_IRUGO, rdebug.dir, &pdev->dev, + &ux500_regulator_status_fops); + if (!rdebug.status_file) + goto exit_destroy_dir; + + /* create "power-state-count" file */ + rdebug.power_state_cnt_file = debugfs_create_file("power-state-count", + S_IRUGO, rdebug.dir, &pdev->dev, + &ux500_regulator_power_state_cnt_fops); + if (!rdebug.power_state_cnt_file) + goto exit_destroy_status; + + rdebug.regulator_array = regulator_info; + rdebug.num_regulators = num_regulators; + + rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL); + if (!rdebug.state_before_suspend) { + dev_err(&pdev->dev, + "could not allocate memory for saving state\n"); + goto exit_destroy_power_state; + } + + rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL); + if (!rdebug.state_after_suspend) { + dev_err(&pdev->dev, + "could not allocate memory for saving state\n"); + goto exit_free; + } + + dbx500_regulator_testcase(regulator_info, num_regulators); + return 0; + +exit_free: + kfree(rdebug.state_before_suspend); +exit_destroy_power_state: + debugfs_remove(rdebug.power_state_cnt_file); +exit_destroy_status: + debugfs_remove(rdebug.status_file); +exit_destroy_dir: + debugfs_remove(rdebug.dir); +exit_no_debugfs: + dev_err(&pdev->dev, "failed to create debugfs entries.\n"); + return -ENOMEM; +} + +int __devexit ux500_regulator_debug_exit(void) +{ + debugfs_remove_recursive(rdebug.dir); + kfree(rdebug.state_after_suspend); + kfree(rdebug.state_before_suspend); + + return 0; +} +#endif diff --git a/drivers/regulator/dbx500-prcmu.h b/drivers/regulator/dbx500-prcmu.h new file mode 100644 index 00000000000..e763883a44f --- /dev/null +++ b/drivers/regulator/dbx500-prcmu.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Bengt Jonsson <bengt.jonsson@stericsson.com> for ST-Ericsson, + * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson + * + * License Terms: GNU General Public License v2 + * + */ + +#ifndef DBX500_REGULATOR_H +#define DBX500_REGULATOR_H + +#include <linux/platform_device.h> + +/** + * struct dbx500_regulator_info - dbx500 regulator information + * @dev: device pointer + * @desc: regulator description + * @rdev: regulator device pointer + * @is_enabled: status of the regulator + * @epod_id: id for EPOD (power domain) + * @is_ramret: RAM retention switch for EPOD (power domain) + * @operating_point: operating point (only for vape, to be removed) + * + */ +struct dbx500_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + bool is_enabled; + u16 epod_id; + bool is_ramret; + bool exclude_from_power_state; + unsigned int operating_point; +}; + +void power_state_active_enable(void); +int power_state_active_disable(void); + + +#ifdef CONFIG_REGULATOR_DEBUG +int ux500_regulator_debug_init(struct platform_device *pdev, + struct dbx500_regulator_info *regulator_info, + int num_regulators); + +int ux500_regulator_debug_exit(void); +#else + +static inline int ux500_regulator_debug_init(struct platform_device *pdev, + struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + return 0; +} + +static inline int ux500_regulator_debug_exit(void) +{ + return 0; +} + +#endif +#endif diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index e24e3a174c4..40f38030b39 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -192,7 +192,9 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; drvdata->desc.ops = &fixed_voltage_ops; - drvdata->desc.n_voltages = 1; + + if (config->microvolts) + drvdata->desc.n_voltages = 1; drvdata->microvolts = config->microvolts; drvdata->gpio = config->gpio; diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index c1a456c4257..775f5fd208c 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -63,23 +63,15 @@ static int isl6271a_set_voltage(struct regulator_dev *dev, unsigned *selector) { struct isl_pmic *pmic = rdev_get_drvdata(dev); - int vsel, err, data; + int err, data; if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX) return -EINVAL; if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX) return -EINVAL; - /* Align to 50000 mV */ - vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP); - - /* If the result fell out of [minuV,maxuV] range, put it back */ - if (vsel < minuV) - vsel += ISL6271A_VOLTAGE_STEP; - - /* Convert the microvolts to data for the chip */ - data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP; - + data = DIV_ROUND_UP(minuV - ISL6271A_VOLTAGE_MIN, + ISL6271A_VOLTAGE_STEP); *selector = data; mutex_lock(&pmic->mtx); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 40e7a4db285..282d2ee0604 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -76,8 +76,8 @@ static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV, if (min_uV < max1586->min_uV) min_uV = max1586->min_uV; - *selector = ((min_uV - max1586->min_uV) * MAX1586_V3_MAX_VSEL + - range_uV - 1) / range_uV; + *selector = DIV_ROUND_UP((min_uV - max1586->min_uV) * + MAX1586_V3_MAX_VSEL, range_uV); if (max1586_v3_calc_voltage(max1586, *selector) > max_uV) return -EINVAL; diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index b06a2399587..82505fcb8d4 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -101,8 +101,7 @@ static int max8649_set_voltage(struct regulator_dev *rdev, min_uV, max_uV); return -EINVAL; } - data = (min_uV - MAX8649_DCDC_VMIN + MAX8649_DCDC_STEP - 1) - / MAX8649_DCDC_STEP; + data = DIV_ROUND_UP(min_uV - MAX8649_DCDC_VMIN, MAX8649_DCDC_STEP); mask = MAX8649_VOL_MASK; *selector = data & mask; @@ -270,7 +269,7 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client, ret); goto out; } - dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", ret); + dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", val); /* enable VID0 & VID1 */ regmap_update_bits(info->regmap, MAX8649_CONTROL, MAX8649_VID_MASK, 0); diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index a838e664569..4c5b05311f4 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -153,14 +153,15 @@ static int max8660_dcdc_set(struct regulator_dev *rdev, int min_uV, int max_uV, if (max_uV < MAX8660_DCDC_MIN_UV || max_uV > MAX8660_DCDC_MAX_UV) return -EINVAL; - selector = (min_uV - (MAX8660_DCDC_MIN_UV - MAX8660_DCDC_STEP + 1)) - / MAX8660_DCDC_STEP; - *s = selector; + selector = DIV_ROUND_UP(min_uV - MAX8660_DCDC_MIN_UV, + MAX8660_DCDC_STEP); ret = max8660_dcdc_list(rdev, selector); if (ret < 0 || ret > max_uV) return -EINVAL; + *s = selector; + reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; ret = max8660_write(max8660, reg, 0, selector); if (ret) @@ -210,8 +211,9 @@ static int max8660_ldo5_set(struct regulator_dev *rdev, int min_uV, int max_uV, if (max_uV < MAX8660_LDO5_MIN_UV || max_uV > MAX8660_LDO5_MAX_UV) return -EINVAL; - selector = (min_uV - (MAX8660_LDO5_MIN_UV - MAX8660_LDO5_STEP + 1)) - / MAX8660_LDO5_STEP; + selector = DIV_ROUND_UP(min_uV - MAX8660_LDO5_MIN_UV, + MAX8660_LDO5_STEP); + ret = max8660_ldo5_list(rdev, selector); if (ret < 0 || ret > max_uV) return -EINVAL; @@ -287,8 +289,8 @@ static int max8660_ldo67_set(struct regulator_dev *rdev, int min_uV, if (max_uV < MAX8660_LDO67_MIN_UV || max_uV > MAX8660_LDO67_MAX_UV) return -EINVAL; - selector = (min_uV - (MAX8660_LDO67_MIN_UV - MAX8660_LDO67_STEP + 1)) - / MAX8660_LDO67_STEP; + selector = DIV_ROUND_UP(min_uV - MAX8660_LDO67_MIN_UV, + MAX8660_LDO67_STEP); ret = max8660_ldo67_list(rdev, selector); if (ret < 0 || ret > max_uV) diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index cc290d37c46..2f242f43096 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -73,7 +73,7 @@ static int max8925_set_voltage(struct regulator_dev *rdev, min_uV, max_uV); return -EINVAL; } - data = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + data = DIV_ROUND_UP(min_uV - info->min_uV, info->step_uV); *selector = data; data <<= info->vol_shift; mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; @@ -140,7 +140,7 @@ static int max8925_set_dvm_voltage(struct regulator_dev *rdev, int uV) if (uV < SD1_DVM_VMIN || uV > SD1_DVM_VMAX) return -EINVAL; - data = (uV - SD1_DVM_VMIN + SD1_DVM_STEP - 1) / SD1_DVM_STEP; + data = DIV_ROUND_UP(uV - SD1_DVM_VMIN, SD1_DVM_STEP); data <<= SD1_DVM_SHIFT; mask = 3 << SD1_DVM_SHIFT; diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index d26e8646277..96579296f04 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -130,15 +130,10 @@ static const struct voltage_map_desc *reg_voltage_map[] = { [MAX8997_CHARGER_TOPOFF] = &topoff_current_map_desc, }; -static inline int max8997_get_rid(struct regulator_dev *rdev) -{ - return rdev_get_id(rdev); -} - static int max8997_list_voltage_safeout(struct regulator_dev *rdev, unsigned int selector) { - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); if (rid == MAX8997_ESAFEOUT1 || rid == MAX8997_ESAFEOUT2) { switch (selector) { @@ -161,7 +156,7 @@ static int max8997_list_voltage_safeout(struct regulator_dev *rdev, static int max8997_list_voltage_charger_cv(struct regulator_dev *rdev, unsigned int selector) { - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); if (rid != MAX8997_CHARGER_CV) goto err; @@ -184,7 +179,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev, unsigned int selector) { const struct voltage_map_desc *desc; - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); int val; if (rid >= ARRAY_SIZE(reg_voltage_map) || @@ -205,7 +200,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev, static int max8997_get_enable_register(struct regulator_dev *rdev, int *reg, int *mask, int *pattern) { - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); switch (rid) { case MAX8997_LDO1 ... MAX8997_LDO21: @@ -325,7 +320,7 @@ static int max8997_reg_disable(struct regulator_dev *rdev) static int max8997_get_voltage_register(struct regulator_dev *rdev, int *_reg, int *_shift, int *_mask) { - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); int reg, shift = 0, mask = 0x3f; switch (rid) { @@ -386,7 +381,7 @@ static int max8997_get_voltage(struct regulator_dev *rdev) struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; int reg, shift, mask, ret; - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); u8 val; ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); @@ -446,7 +441,7 @@ static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev, { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); int lb, ub; int reg, shift = 0, mask, ret = 0; u8 val = 0x0; @@ -503,7 +498,7 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, struct i2c_client *i2c = max8997->iodev->i2c; int min_vol = min_uV / 1000, max_vol = max_uV / 1000; const struct voltage_map_desc *desc; - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); int reg, shift = 0, mask, ret; int i; u8 org; @@ -564,7 +559,7 @@ static int max8997_assess_side_effect(struct regulator_dev *rdev, u8 new_val, int *best) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); u8 *buckx_val[3]; bool buckx_gpiodvs[3]; int side_effect[8]; @@ -641,7 +636,7 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct max8997_data *max8997 = rdev_get_drvdata(rdev); - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); const struct voltage_map_desc *desc; int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg; bool gpio_dvs_mode = false; @@ -724,7 +719,7 @@ static int max8997_set_voltage_safeout(struct regulator_dev *rdev, { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); int reg, shift = 0, mask, ret; int i = 0; u8 val; @@ -766,7 +761,7 @@ static int max8997_reg_disable_suspend(struct regulator_dev *rdev) struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; int ret, reg, mask, pattern; - int rid = max8997_get_rid(rdev); + int rid = rdev_get_id(rdev); ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); if (ret) @@ -908,13 +903,13 @@ static struct regulator_desc regulators[] = { }, regulator_desc_buck(7), { - .name = "EN32KHz AP", + .name = "EN32KHz_AP", .id = MAX8997_EN32KHZ_AP, .ops = &max8997_fixedvolt_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, { - .name = "EN32KHz CP", + .name = "EN32KHz_CP", .id = MAX8997_EN32KHZ_CP, .ops = &max8997_fixedvolt_ops, .type = REGULATOR_VOLTAGE, @@ -938,7 +933,7 @@ static struct regulator_desc regulators[] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, { - .name = "CHARGER CV", + .name = "CHARGER_CV", .id = MAX8997_CHARGER_CV, .ops = &max8997_fixedstate_ops, .type = REGULATOR_VOLTAGE, @@ -950,7 +945,7 @@ static struct regulator_desc regulators[] = { .type = REGULATOR_CURRENT, .owner = THIS_MODULE, }, { - .name = "CHARGER TOPOFF", + .name = "CHARGER_TOPOFF", .id = MAX8997_CHARGER_TOPOFF, .ops = &max8997_charger_fixedstate_ops, .type = REGULATOR_CURRENT, diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 2d38c2493a0..5890265eeac 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -112,16 +112,11 @@ static const struct voltage_map_desc *ldo_voltage_map[] = { &buck4_voltage_map_desc, /* BUCK4 */ }; -static inline int max8998_get_ldo(struct regulator_dev *rdev) -{ - return rdev_get_id(rdev); -} - static int max8998_list_voltage(struct regulator_dev *rdev, unsigned int selector) { const struct voltage_map_desc *desc; - int ldo = max8998_get_ldo(rdev); + int ldo = rdev_get_id(rdev); int val; if (ldo >= ARRAY_SIZE(ldo_voltage_map)) @@ -141,7 +136,7 @@ static int max8998_list_voltage(struct regulator_dev *rdev, static int max8998_get_enable_register(struct regulator_dev *rdev, int *reg, int *shift) { - int ldo = max8998_get_ldo(rdev); + int ldo = rdev_get_id(rdev); switch (ldo) { case MAX8998_LDO2 ... MAX8998_LDO5: @@ -222,7 +217,7 @@ static int max8998_ldo_disable(struct regulator_dev *rdev) static int max8998_get_voltage_register(struct regulator_dev *rdev, int *_reg, int *_shift, int *_mask) { - int ldo = max8998_get_ldo(rdev); + int ldo = rdev_get_id(rdev); struct max8998_data *max8998 = rdev_get_drvdata(rdev); int reg, shift = 0, mask = 0xff; @@ -310,7 +305,7 @@ static int max8998_set_voltage_ldo(struct regulator_dev *rdev, struct i2c_client *i2c = max8998->iodev->i2c; int min_vol = min_uV / 1000, max_vol = max_uV / 1000; const struct voltage_map_desc *desc; - int ldo = max8998_get_ldo(rdev); + int ldo = rdev_get_id(rdev); int reg, shift = 0, mask, ret; int i = 0; @@ -362,7 +357,7 @@ static int max8998_set_voltage_buck(struct regulator_dev *rdev, struct i2c_client *i2c = max8998->iodev->i2c; int min_vol = min_uV / 1000, max_vol = max_uV / 1000; const struct voltage_map_desc *desc; - int buck = max8998_get_ldo(rdev); + int buck = rdev_get_id(rdev); int reg, shift = 0, mask, ret; int difference = 0, i = 0, j = 0, previous_vol = 0; u8 val = 0; @@ -829,7 +824,6 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) buck12_voltage_map_desc.step*i < (pdata->buck2_voltage2 / 1000)) i++; - printk(KERN_ERR "i2:%d, buck2_idx:%d\n", i, max8998->buck2_idx); max8998->buck2_vol[1] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i); if (ret) diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 8e9b90ad88a..6c0face87ff 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -344,6 +344,9 @@ static int __devinit mc13783_regulator_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s id %d\n", __func__, pdev->id); + if (!pdata) + return -EINVAL; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv) + pdata->num_regulators * sizeof(priv->regulators[0]), GFP_KERNEL); diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 80ecafef1bc..62dcd0a432b 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -254,6 +254,7 @@ int __devinit mc13xxx_get_num_regulators_dt(struct platform_device *pdev) return num; } +EXPORT_SYMBOL_GPL(mc13xxx_get_num_regulators_dt); struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt( struct platform_device *pdev, struct mc13xxx_regulator *regulators, @@ -291,6 +292,7 @@ struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt( return data; } +EXPORT_SYMBOL_GPL(mc13xxx_parse_regulators_dt); #endif MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 1d1c3105629..6db46c632f1 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -142,6 +142,7 @@ static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev, case PCF50633_REGULATOR_LDO5: case PCF50633_REGULATOR_LDO6: case PCF50633_REGULATOR_HCLDO: + case PCF50633_REGULATOR_MEMLDO: volt_bits = ldo_voltage_bits(millivolts); break; default: @@ -175,6 +176,7 @@ static int pcf50633_regulator_voltage_value(enum pcf50633_regulator_id id, case PCF50633_REGULATOR_LDO5: case PCF50633_REGULATOR_LDO6: case PCF50633_REGULATOR_HCLDO: + case PCF50633_REGULATOR_MEMLDO: millivolts = ldo_voltage_value(bits); break; default: @@ -217,9 +219,6 @@ static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev, case PCF50633_REGULATOR_AUTO: index += 0x2f; break; - case PCF50633_REGULATOR_HCLDO: - index += 0x01; - break; default: break; } @@ -288,27 +287,27 @@ static struct regulator_ops pcf50633_regulator_ops = { static struct regulator_desc regulators[] = { [PCF50633_REGULATOR_AUTO] = - PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO, 80), + PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO, 81), [PCF50633_REGULATOR_DOWN1] = - PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1, 95), + PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1, 96), [PCF50633_REGULATOR_DOWN2] = - PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2, 95), + PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2, 96), [PCF50633_REGULATOR_LDO1] = - PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1, 27), + PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1, 28), [PCF50633_REGULATOR_LDO2] = - PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2, 27), + PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2, 28), [PCF50633_REGULATOR_LDO3] = - PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3, 27), + PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3, 28), [PCF50633_REGULATOR_LDO4] = - PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4, 27), + PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4, 28), [PCF50633_REGULATOR_LDO5] = - PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5, 27), + PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5, 28), [PCF50633_REGULATOR_LDO6] = - PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6, 27), + PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6, 28), [PCF50633_REGULATOR_HCLDO] = - PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO, 26), + PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO, 28), [PCF50633_REGULATOR_MEMLDO] = - PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO, 0), + PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO, 28), }; static int __devinit pcf50633_regulator_probe(struct platform_device *pdev) diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c new file mode 100644 index 00000000000..58447db15de --- /dev/null +++ b/drivers/regulator/s5m8767.c @@ -0,0 +1,790 @@ +/* + * s5m8767.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/s5m87xx/s5m-core.h> +#include <linux/mfd/s5m87xx/s5m-pmic.h> + +struct s5m8767_info { + struct device *dev; + struct s5m87xx_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + + int ramp_delay; + bool buck2_ramp; + bool buck3_ramp; + bool buck4_ramp; + + bool buck2_gpiodvs; + bool buck3_gpiodvs; + bool buck4_gpiodvs; + u8 buck2_vol[8]; + u8 buck3_vol[8]; + u8 buck4_vol[8]; + int buck_gpios[3]; + int buck_gpioindex; +}; + +struct s5m_voltage_desc { + int max; + int min; + int step; +}; + +static const struct s5m_voltage_desc buck_voltage_val1 = { + .max = 2225000, + .min = 650000, + .step = 6250, +}; + +static const struct s5m_voltage_desc buck_voltage_val2 = { + .max = 1600000, + .min = 600000, + .step = 6250, +}; + +static const struct s5m_voltage_desc buck_voltage_val3 = { + .max = 3000000, + .min = 750000, + .step = 12500, +}; + +static const struct s5m_voltage_desc ldo_voltage_val1 = { + .max = 3950000, + .min = 800000, + .step = 50000, +}; + +static const struct s5m_voltage_desc ldo_voltage_val2 = { + .max = 2375000, + .min = 800000, + .step = 25000, +}; + +static const struct s5m_voltage_desc *reg_voltage_map[] = { + [S5M8767_LDO1] = &ldo_voltage_val2, + [S5M8767_LDO2] = &ldo_voltage_val2, + [S5M8767_LDO3] = &ldo_voltage_val1, + [S5M8767_LDO4] = &ldo_voltage_val1, + [S5M8767_LDO5] = &ldo_voltage_val1, + [S5M8767_LDO6] = &ldo_voltage_val2, + [S5M8767_LDO7] = &ldo_voltage_val2, + [S5M8767_LDO8] = &ldo_voltage_val2, + [S5M8767_LDO9] = &ldo_voltage_val1, + [S5M8767_LDO10] = &ldo_voltage_val1, + [S5M8767_LDO11] = &ldo_voltage_val1, + [S5M8767_LDO12] = &ldo_voltage_val1, + [S5M8767_LDO13] = &ldo_voltage_val1, + [S5M8767_LDO14] = &ldo_voltage_val1, + [S5M8767_LDO15] = &ldo_voltage_val2, + [S5M8767_LDO16] = &ldo_voltage_val1, + [S5M8767_LDO17] = &ldo_voltage_val1, + [S5M8767_LDO18] = &ldo_voltage_val1, + [S5M8767_LDO19] = &ldo_voltage_val1, + [S5M8767_LDO20] = &ldo_voltage_val1, + [S5M8767_LDO21] = &ldo_voltage_val1, + [S5M8767_LDO22] = &ldo_voltage_val1, + [S5M8767_LDO23] = &ldo_voltage_val1, + [S5M8767_LDO24] = &ldo_voltage_val1, + [S5M8767_LDO25] = &ldo_voltage_val1, + [S5M8767_LDO26] = &ldo_voltage_val1, + [S5M8767_LDO27] = &ldo_voltage_val1, + [S5M8767_LDO28] = &ldo_voltage_val1, + [S5M8767_BUCK1] = &buck_voltage_val1, + [S5M8767_BUCK2] = &buck_voltage_val2, + [S5M8767_BUCK3] = &buck_voltage_val2, + [S5M8767_BUCK4] = &buck_voltage_val2, + [S5M8767_BUCK5] = &buck_voltage_val1, + [S5M8767_BUCK6] = &buck_voltage_val1, + [S5M8767_BUCK7] = NULL, + [S5M8767_BUCK8] = NULL, + [S5M8767_BUCK9] = &buck_voltage_val3, +}; + +static int s5m8767_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct s5m_voltage_desc *desc; + int reg_id = rdev_get_id(rdev); + int val; + + if (reg_id >= ARRAY_SIZE(reg_voltage_map) || reg_id < 0) + return -EINVAL; + + desc = reg_voltage_map[reg_id]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val; +} + +static int s5m8767_get_register(struct regulator_dev *rdev, int *reg) +{ + int reg_id = rdev_get_id(rdev); + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO2: + *reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1); + break; + case S5M8767_LDO3 ... S5M8767_LDO28: + *reg = S5M8767_REG_LDO3CTRL + (reg_id - S5M8767_LDO3); + break; + case S5M8767_BUCK1: + *reg = S5M8767_REG_BUCK1CTRL1; + break; + case S5M8767_BUCK2 ... S5M8767_BUCK4: + *reg = S5M8767_REG_BUCK2CTRL + (reg_id - S5M8767_BUCK2) * 9; + break; + case S5M8767_BUCK5: + *reg = S5M8767_REG_BUCK5CTRL1; + break; + case S5M8767_BUCK6 ... S5M8767_BUCK9: + *reg = S5M8767_REG_BUCK6CTRL1 + (reg_id - S5M8767_BUCK6) * 2; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int ret, reg; + int mask = 0xc0, pattern = 0xc0; + u8 val; + + ret = s5m8767_get_register(rdev, ®); + if (ret == -EINVAL) + return 1; + else if (ret) + return ret; + + ret = s5m_reg_read(s5m8767->iodev, reg, &val); + if (ret) + return ret; + + return (val & mask) == pattern; +} + +static int s5m8767_reg_enable(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int ret, reg; + int mask = 0xc0, pattern = 0xc0; + + ret = s5m8767_get_register(rdev, ®); + if (ret) + return ret; + + return s5m_reg_update(s5m8767->iodev, reg, pattern, mask); +} + +static int s5m8767_reg_disable(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int ret, reg; + int mask = 0xc0, pattern = 0xc0; + + ret = s5m8767_get_register(rdev, ®); + if (ret) + return ret; + + return s5m_reg_update(s5m8767->iodev, reg, ~pattern, mask); +} + +static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int reg_id = rdev_get_id(rdev); + int reg; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO2: + reg = S5M8767_REG_LDO1CTRL + (reg_id - S5M8767_LDO1); + break; + case S5M8767_LDO3 ... S5M8767_LDO28: + reg = S5M8767_REG_LDO3CTRL + (reg_id - S5M8767_LDO3); + break; + case S5M8767_BUCK1: + reg = S5M8767_REG_BUCK1CTRL2; + break; + case S5M8767_BUCK2: + reg = S5M8767_REG_BUCK2DVS1; + if (s5m8767->buck2_gpiodvs) + reg += s5m8767->buck_gpioindex; + break; + case S5M8767_BUCK3: + reg = S5M8767_REG_BUCK3DVS1; + if (s5m8767->buck3_gpiodvs) + reg += s5m8767->buck_gpioindex; + break; + case S5M8767_BUCK4: + reg = S5M8767_REG_BUCK4DVS1; + if (s5m8767->buck4_gpiodvs) + reg += s5m8767->buck_gpioindex; + break; + case S5M8767_BUCK5: + reg = S5M8767_REG_BUCK5CTRL2; + break; + case S5M8767_BUCK6 ... S5M8767_BUCK9: + reg = S5M8767_REG_BUCK6CTRL2 + (reg_id - S5M8767_BUCK6) * 2; + break; + default: + return -EINVAL; + } + + *_reg = reg; + + return 0; +} + +static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int reg, mask, ret; + int reg_id = rdev_get_id(rdev); + u8 val; + + ret = s5m8767_get_voltage_register(rdev, ®); + if (ret) + return ret; + + mask = (reg_id < S5M8767_BUCK1) ? 0x3f : 0xff; + + ret = s5m_reg_read(s5m8767->iodev, reg, &val); + if (ret) + return ret; + + val &= mask; + + return val; +} + +static int s5m8767_convert_voltage_to_sel( + const struct s5m_voltage_desc *desc, + int min_vol, int max_vol) +{ + int selector = 0; + + if (desc == NULL) + return -EINVAL; + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + selector = (min_vol - desc->min) / desc->step; + + if (desc->min + desc->step * selector > max_vol) + return -EINVAL; + + return selector; +} + +static int s5m8767_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + const struct s5m_voltage_desc *desc; + int reg_id = rdev_get_id(rdev); + int reg, mask, ret; + int i; + u8 val; + + switch (reg_id) { + case S5M8767_LDO1 ... S5M8767_LDO28: + mask = 0x3f; + break; + case S5M8767_BUCK1 ... S5M8767_BUCK6: + mask = 0xff; + break; + case S5M8767_BUCK7 ... S5M8767_BUCK8: + return -EINVAL; + case S5M8767_BUCK9: + mask = 0xff; + break; + default: + return -EINVAL; + } + + desc = reg_voltage_map[reg_id]; + + i = s5m8767_convert_voltage_to_sel(desc, min_uV, max_uV); + if (i < 0) + return i; + + ret = s5m8767_get_voltage_register(rdev, ®); + if (ret) + return ret; + + s5m_reg_read(s5m8767->iodev, reg, &val); + val = val & mask; + + ret = s5m_reg_write(s5m8767->iodev, reg, val); + *selector = i; + + return ret; +} + +static inline void s5m8767_set_high(struct s5m8767_info *s5m8767) +{ + int temp_index = s5m8767->buck_gpioindex; + + gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); + gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); + gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); +} + +static inline void s5m8767_set_low(struct s5m8767_info *s5m8767) +{ + int temp_index = s5m8767->buck_gpioindex; + + gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); + gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); + gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); +} + +static int s5m8767_set_voltage_buck(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + int reg_id = rdev_get_id(rdev); + const struct s5m_voltage_desc *desc; + int new_val, old_val, i = 0; + + if (reg_id < S5M8767_BUCK1 || reg_id > S5M8767_BUCK6) + return -EINVAL; + + switch (reg_id) { + case S5M8767_BUCK1: + return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); + case S5M8767_BUCK2 ... S5M8767_BUCK4: + break; + case S5M8767_BUCK5 ... S5M8767_BUCK6: + return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); + case S5M8767_BUCK9: + return s5m8767_set_voltage(rdev, min_uV, max_uV, selector); + } + + desc = reg_voltage_map[reg_id]; + new_val = s5m8767_convert_voltage_to_sel(desc, min_uV, max_uV); + if (new_val < 0) + return new_val; + + switch (reg_id) { + case S5M8767_BUCK2: + if (s5m8767->buck2_gpiodvs) { + while (s5m8767->buck2_vol[i] != new_val) + i++; + } else + return s5m8767_set_voltage(rdev, min_uV, + max_uV, selector); + break; + case S5M8767_BUCK3: + if (s5m8767->buck3_gpiodvs) { + while (s5m8767->buck3_vol[i] != new_val) + i++; + } else + return s5m8767_set_voltage(rdev, min_uV, + max_uV, selector); + break; + case S5M8767_BUCK4: + if (s5m8767->buck3_gpiodvs) { + while (s5m8767->buck4_vol[i] != new_val) + i++; + } else + return s5m8767_set_voltage(rdev, min_uV, + max_uV, selector); + break; + } + + old_val = s5m8767->buck_gpioindex; + s5m8767->buck_gpioindex = i; + + if (i > old_val) + s5m8767_set_high(s5m8767); + else + s5m8767_set_low(s5m8767); + + *selector = new_val; + return 0; +} + +static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); + const struct s5m_voltage_desc *desc; + int reg_id = rdev_get_id(rdev); + + desc = reg_voltage_map[reg_id]; + + if (old_sel < new_sel) + return DIV_ROUND_UP(desc->step * (new_sel - old_sel), + s5m8767->ramp_delay * 1000); + return 0; +} + +static struct regulator_ops s5m8767_ldo_ops = { + .list_voltage = s5m8767_list_voltage, + .is_enabled = s5m8767_reg_is_enabled, + .enable = s5m8767_reg_enable, + .disable = s5m8767_reg_disable, + .get_voltage_sel = s5m8767_get_voltage_sel, + .set_voltage = s5m8767_set_voltage, + .set_voltage_time_sel = s5m8767_set_voltage_time_sel, +}; + +static struct regulator_ops s5m8767_buck_ops = { + .list_voltage = s5m8767_list_voltage, + .is_enabled = s5m8767_reg_is_enabled, + .enable = s5m8767_reg_enable, + .disable = s5m8767_reg_disable, + .get_voltage_sel = s5m8767_get_voltage_sel, + .set_voltage = s5m8767_set_voltage_buck, + .set_voltage_time_sel = s5m8767_set_voltage_time_sel, +}; + +#define regulator_desc_ldo(num) { \ + .name = "LDO"#num, \ + .id = S5M8767_LDO##num, \ + .ops = &s5m8767_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} +#define regulator_desc_buck(num) { \ + .name = "BUCK"#num, \ + .id = S5M8767_BUCK##num, \ + .ops = &s5m8767_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_ldo(1), + regulator_desc_ldo(2), + regulator_desc_ldo(3), + regulator_desc_ldo(4), + regulator_desc_ldo(5), + regulator_desc_ldo(6), + regulator_desc_ldo(7), + regulator_desc_ldo(8), + regulator_desc_ldo(9), + regulator_desc_ldo(10), + regulator_desc_ldo(11), + regulator_desc_ldo(12), + regulator_desc_ldo(13), + regulator_desc_ldo(14), + regulator_desc_ldo(15), + regulator_desc_ldo(16), + regulator_desc_ldo(17), + regulator_desc_ldo(18), + regulator_desc_ldo(19), + regulator_desc_ldo(20), + regulator_desc_ldo(21), + regulator_desc_ldo(22), + regulator_desc_ldo(23), + regulator_desc_ldo(24), + regulator_desc_ldo(25), + regulator_desc_ldo(26), + regulator_desc_ldo(27), + regulator_desc_ldo(28), + regulator_desc_buck(1), + regulator_desc_buck(2), + regulator_desc_buck(3), + regulator_desc_buck(4), + regulator_desc_buck(5), + regulator_desc_buck(6), + regulator_desc_buck(7), + regulator_desc_buck(8), + regulator_desc_buck(9), +}; + +static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) +{ + struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct s5m8767_info *s5m8767; + int i, ret, size; + + if (!pdata) { + dev_err(pdev->dev.parent, "Platform data not supplied\n"); + return -ENODEV; + } + + if (pdata->buck2_gpiodvs) { + if (pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + return -EINVAL; + } + } + + if (pdata->buck3_gpiodvs) { + if (pdata->buck2_gpiodvs || pdata->buck4_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + return -EINVAL; + } + } + + if (pdata->buck4_gpiodvs) { + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs) { + dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n"); + return -EINVAL; + } + } + + s5m8767 = devm_kzalloc(&pdev->dev, sizeof(struct s5m8767_info), + GFP_KERNEL); + if (!s5m8767) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * (S5M8767_REG_MAX - 2); + s5m8767->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!s5m8767->rdev) + return -ENOMEM; + + rdev = s5m8767->rdev; + s5m8767->dev = &pdev->dev; + s5m8767->iodev = iodev; + s5m8767->num_regulators = S5M8767_REG_MAX - 2; + platform_set_drvdata(pdev, s5m8767); + + s5m8767->buck_gpioindex = pdata->buck_default_idx; + s5m8767->buck2_gpiodvs = pdata->buck2_gpiodvs; + s5m8767->buck3_gpiodvs = pdata->buck3_gpiodvs; + s5m8767->buck4_gpiodvs = pdata->buck4_gpiodvs; + s5m8767->buck_gpios[0] = pdata->buck_gpios[0]; + s5m8767->buck_gpios[1] = pdata->buck_gpios[1]; + s5m8767->buck_gpios[2] = pdata->buck_gpios[2]; + s5m8767->ramp_delay = pdata->buck_ramp_delay; + s5m8767->buck2_ramp = pdata->buck2_ramp_enable; + s5m8767->buck3_ramp = pdata->buck3_ramp_enable; + s5m8767->buck4_ramp = pdata->buck4_ramp_enable; + + for (i = 0; i < 8; i++) { + if (s5m8767->buck2_gpiodvs) { + s5m8767->buck2_vol[i] = + s5m8767_convert_voltage_to_sel( + &buck_voltage_val2, + pdata->buck2_voltage[i], + pdata->buck2_voltage[i] + + buck_voltage_val2.step); + } + + if (s5m8767->buck3_gpiodvs) { + s5m8767->buck3_vol[i] = + s5m8767_convert_voltage_to_sel( + &buck_voltage_val2, + pdata->buck3_voltage[i], + pdata->buck3_voltage[i] + + buck_voltage_val2.step); + } + + if (s5m8767->buck4_gpiodvs) { + s5m8767->buck4_vol[i] = + s5m8767_convert_voltage_to_sel( + &buck_voltage_val2, + pdata->buck4_voltage[i], + pdata->buck4_voltage[i] + + buck_voltage_val2.step); + } + } + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + if (gpio_is_valid(pdata->buck_gpios[0]) && + gpio_is_valid(pdata->buck_gpios[1]) && + gpio_is_valid(pdata->buck_gpios[2])) { + ret = gpio_request(pdata->buck_gpios[0], + "S5M8767 SET1"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for SET1\n"); + + ret = gpio_request(pdata->buck_gpios[1], + "S5M8767 SET2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for SET2\n"); + + ret = gpio_request(pdata->buck_gpios[2], + "S5M8767 SET3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for SET3\n"); + /* SET1 GPIO */ + gpio_direction_output(pdata->buck_gpios[0], + (s5m8767->buck_gpioindex >> 2) & 0x1); + /* SET2 GPIO */ + gpio_direction_output(pdata->buck_gpios[1], + (s5m8767->buck_gpioindex >> 1) & 0x1); + /* SET3 GPIO */ + gpio_direction_output(pdata->buck_gpios[2], + (s5m8767->buck_gpioindex >> 0) & 0x1); + ret = 0; + } else { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + ret = -EINVAL; + return ret; + } + } + + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, + (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, + (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, + (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); + + /* Initialize GPIO DVS registers */ + for (i = 0; i < 8; i++) { + if (s5m8767->buck2_gpiodvs) { + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i, + s5m8767->buck2_vol[i]); + } + + if (s5m8767->buck3_gpiodvs) { + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i, + s5m8767->buck3_vol[i]); + } + + if (s5m8767->buck4_gpiodvs) { + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i, + s5m8767->buck4_vol[i]); + } + } + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, 0x78, 0xff); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, 0x58, 0xff); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, 0x78, 0xff); + + if (s5m8767->buck2_ramp) + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08); + + if (s5m8767->buck3_ramp) + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04); + + if (s5m8767->buck4_ramp) + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02); + + if (s5m8767->buck2_ramp || s5m8767->buck3_ramp + || s5m8767->buck4_ramp) { + switch (s5m8767->ramp_delay) { + case 15: + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xc0, 0xf0); + break; + case 25: + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xd0, 0xf0); + break; + case 50: + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xe0, 0xf0); + break; + case 100: + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0xf0, 0xf0); + break; + default: + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0x90, 0xf0); + } + } + + for (i = 0; i < pdata->num_regulators; i++) { + const struct s5m_voltage_desc *desc; + int id = pdata->regulators[i].id; + + desc = reg_voltage_map[id]; + if (desc) + regulators[id].n_voltages = + (desc->max - desc->min) / desc->step + 1; + + rdev[i] = regulator_register(®ulators[id], s5m8767->dev, + pdata->regulators[i].initdata, s5m8767, NULL); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(s5m8767->dev, "regulator init failed for %d\n", + id); + rdev[i] = NULL; + goto err; + } + } + + return 0; +err: + for (i = 0; i < s5m8767->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + return ret; +} + +static int __devexit s5m8767_pmic_remove(struct platform_device *pdev) +{ + struct s5m8767_info *s5m8767 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = s5m8767->rdev; + int i; + + for (i = 0; i < s5m8767->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + return 0; +} + +static const struct platform_device_id s5m8767_pmic_id[] = { + { "s5m8767-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, s5m8767_pmic_id); + +static struct platform_driver s5m8767_pmic_driver = { + .driver = { + .name = "s5m8767-pmic", + .owner = THIS_MODULE, + }, + .probe = s5m8767_pmic_probe, + .remove = __devexit_p(s5m8767_pmic_remove), + .id_table = s5m8767_pmic_id, +}; + +static int __init s5m8767_pmic_init(void) +{ + return platform_driver_register(&s5m8767_pmic_driver); +} +subsys_initcall(s5m8767_pmic_init); + +static void __exit s5m8767_pmic_exit(void) +{ + platform_driver_unregister(&s5m8767_pmic_driver); +} +module_exit(s5m8767_pmic_exit); + +/* Module information */ +MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); +MODULE_DESCRIPTION("SAMSUNG S5M8767 Regulator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c new file mode 100644 index 00000000000..e2ec73068ee --- /dev/null +++ b/drivers/regulator/tps62360-regulator.c @@ -0,0 +1,472 @@ +/* + * tps62360.c -- TI tps62360 + * + * Driver for processor core supply tps62360 and tps62361B + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/tps62360.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +/* Register definitions */ +#define REG_VSET0 0 +#define REG_VSET1 1 +#define REG_VSET2 2 +#define REG_VSET3 3 +#define REG_CONTROL 4 +#define REG_TEMP 5 +#define REG_RAMPCTRL 6 +#define REG_CHIPID 8 + +enum chips {TPS62360, TPS62361}; + +#define TPS62360_BASE_VOLTAGE 770 +#define TPS62360_N_VOLTAGES 64 + +#define TPS62361_BASE_VOLTAGE 500 +#define TPS62361_N_VOLTAGES 128 + +/* tps 62360 chip information */ +struct tps62360_chip { + const char *name; + struct device *dev; + struct regulator_desc desc; + struct i2c_client *client; + struct regulator_dev *rdev; + struct regmap *regmap; + int chip_id; + int vsel0_gpio; + int vsel1_gpio; + int voltage_base; + u8 voltage_reg_mask; + bool en_internal_pulldn; + bool en_force_pwm; + bool en_discharge; + bool valid_gpios; + int lru_index[4]; + int curr_vset_vsel[4]; + int curr_vset_id; +}; + +/* + * find_voltage_set_register: Find new voltage configuration register + * (VSET) id. + * The finding of the new VSET register will be based on the LRU mechanism. + * Each VSET register will have different voltage configured . This + * Function will look if any of the VSET register have requested voltage set + * or not. + * - If it is already there then it will make that register as most + * recently used and return as found so that caller need not to set + * the VSET register but need to set the proper gpios to select this + * VSET register. + * - If requested voltage is not found then it will use the least + * recently mechanism to get new VSET register for new configuration + * and will return not_found so that caller need to set new VSET + * register and then gpios (both). + */ +static bool find_voltage_set_register(struct tps62360_chip *tps, + int req_vsel, int *vset_reg_id) +{ + int i; + bool found = false; + int new_vset_reg = tps->lru_index[3]; + int found_index = 3; + for (i = 0; i < 4; ++i) { + if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) { + new_vset_reg = tps->lru_index[i]; + found_index = i; + found = true; + goto update_lru_index; + } + } + +update_lru_index: + for (i = found_index; i > 0; i--) + tps->lru_index[i] = tps->lru_index[i - 1]; + + tps->lru_index[0] = new_vset_reg; + *vset_reg_id = new_vset_reg; + return found; +} + +static int tps62360_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct tps62360_chip *tps = rdev_get_drvdata(dev); + int vsel; + unsigned int data; + int ret; + + ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data); + if (ret < 0) { + dev_err(tps->dev, "%s: Error in reading register %d\n", + __func__, REG_VSET0 + tps->curr_vset_id); + return ret; + } + vsel = (int)data & tps->voltage_reg_mask; + return (tps->voltage_base + vsel * 10) * 1000; +} + +static int tps62360_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct tps62360_chip *tps = rdev_get_drvdata(dev); + int vsel; + int ret; + bool found = false; + int new_vset_id = tps->curr_vset_id; + + if (max_uV < min_uV) + return -EINVAL; + + if (min_uV > + ((tps->voltage_base + (tps->desc.n_voltages - 1) * 10) * 1000)) + return -EINVAL; + + if (max_uV < tps->voltage_base * 1000) + return -EINVAL; + + vsel = DIV_ROUND_UP(min_uV - (tps->voltage_base * 1000), 10000); + if (selector) + *selector = (vsel & tps->voltage_reg_mask); + + /* + * If gpios are available to select the VSET register then least + * recently used register for new configuration. + */ + if (tps->valid_gpios) + found = find_voltage_set_register(tps, vsel, &new_vset_id); + + if (!found) { + ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id, + tps->voltage_reg_mask, vsel); + if (ret < 0) { + dev_err(tps->dev, "%s: Error in updating register %d\n", + __func__, REG_VSET0 + new_vset_id); + return ret; + } + tps->curr_vset_id = new_vset_id; + tps->curr_vset_vsel[new_vset_id] = vsel; + } + + /* Select proper VSET register vio gpios */ + if (tps->valid_gpios) { + gpio_set_value_cansleep(tps->vsel0_gpio, + new_vset_id & 0x1); + gpio_set_value_cansleep(tps->vsel1_gpio, + (new_vset_id >> 1) & 0x1); + } + return 0; +} + +static int tps62360_dcdc_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps62360_chip *tps = rdev_get_drvdata(dev); + + if (selector >= tps->desc.n_voltages) + return -EINVAL; + return (tps->voltage_base + selector * 10) * 1000; +} + +static struct regulator_ops tps62360_dcdc_ops = { + .get_voltage = tps62360_dcdc_get_voltage, + .set_voltage = tps62360_dcdc_set_voltage, + .list_voltage = tps62360_dcdc_list_voltage, +}; + +static int tps62360_init_force_pwm(struct tps62360_chip *tps, + struct tps62360_regulator_platform_data *pdata, + int vset_id) +{ + unsigned int data; + int ret; + ret = regmap_read(tps->regmap, REG_VSET0 + vset_id, &data); + if (ret < 0) { + dev_err(tps->dev, "%s() fails in writing reg %d\n", + __func__, REG_VSET0 + vset_id); + return ret; + } + tps->curr_vset_vsel[vset_id] = data & tps->voltage_reg_mask; + if (pdata->en_force_pwm) + data |= BIT(7); + else + data &= ~BIT(7); + ret = regmap_write(tps->regmap, REG_VSET0 + vset_id, data); + if (ret < 0) + dev_err(tps->dev, "%s() fails in writing reg %d\n", + __func__, REG_VSET0 + vset_id); + return ret; +} + +static int tps62360_init_dcdc(struct tps62360_chip *tps, + struct tps62360_regulator_platform_data *pdata) +{ + int ret; + int i; + + /* Initailize internal pull up/down control */ + if (tps->en_internal_pulldn) + ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0); + else + ret = regmap_write(tps->regmap, REG_CONTROL, 0x0); + if (ret < 0) { + dev_err(tps->dev, "%s() fails in writing reg %d\n", + __func__, REG_CONTROL); + return ret; + } + + /* Initailize force PWM mode */ + if (tps->valid_gpios) { + for (i = 0; i < 4; ++i) { + ret = tps62360_init_force_pwm(tps, pdata, i); + if (ret < 0) + return ret; + } + } else { + ret = tps62360_init_force_pwm(tps, pdata, tps->curr_vset_id); + if (ret < 0) + return ret; + } + + /* Reset output discharge path to reduce power consumption */ + ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0); + if (ret < 0) + dev_err(tps->dev, "%s() fails in updating reg %d\n", + __func__, REG_RAMPCTRL); + return ret; +} + +static const struct regmap_config tps62360_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int __devinit tps62360_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps62360_regulator_platform_data *pdata; + struct regulator_dev *rdev; + struct tps62360_chip *tps; + int ret; + int i; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "%s() Err: Platform data not found\n", + __func__); + return -EIO; + } + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) { + dev_err(&client->dev, "%s() Err: Memory allocation fails\n", + __func__); + return -ENOMEM; + } + + tps->en_force_pwm = pdata->en_force_pwm; + tps->en_discharge = pdata->en_discharge; + tps->en_internal_pulldn = pdata->en_internal_pulldn; + tps->vsel0_gpio = pdata->vsel0_gpio; + tps->vsel1_gpio = pdata->vsel1_gpio; + tps->client = client; + tps->dev = &client->dev; + tps->name = id->name; + tps->voltage_base = (id->driver_data == TPS62360) ? + TPS62360_BASE_VOLTAGE : TPS62361_BASE_VOLTAGE; + tps->voltage_reg_mask = (id->driver_data == TPS62360) ? 0x3F : 0x7F; + + tps->desc.name = id->name; + tps->desc.id = 0; + tps->desc.n_voltages = (id->driver_data == TPS62360) ? + TPS62360_N_VOLTAGES : TPS62361_N_VOLTAGES; + tps->desc.ops = &tps62360_dcdc_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + tps->regmap = regmap_init_i2c(client, &tps62360_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(&client->dev, "%s() Err: Failed to allocate register" + "map: %d\n", __func__, ret); + return ret; + } + i2c_set_clientdata(client, tps); + + tps->curr_vset_id = (pdata->vsel1_def_state & 1) * 2 + + (pdata->vsel0_def_state & 1); + tps->lru_index[0] = tps->curr_vset_id; + tps->valid_gpios = false; + + if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) { + ret = gpio_request(tps->vsel0_gpio, "tps62360-vsel0"); + if (ret) { + dev_err(&client->dev, + "Err: Could not obtain vsel0 GPIO %d: %d\n", + tps->vsel0_gpio, ret); + goto err_gpio0; + } + ret = gpio_direction_output(tps->vsel0_gpio, + pdata->vsel0_def_state); + if (ret) { + dev_err(&client->dev, "Err: Could not set direction of" + "vsel0 GPIO %d: %d\n", tps->vsel0_gpio, ret); + gpio_free(tps->vsel0_gpio); + goto err_gpio0; + } + + ret = gpio_request(tps->vsel1_gpio, "tps62360-vsel1"); + if (ret) { + dev_err(&client->dev, + "Err: Could not obtain vsel1 GPIO %d: %d\n", + tps->vsel1_gpio, ret); + goto err_gpio1; + } + ret = gpio_direction_output(tps->vsel1_gpio, + pdata->vsel1_def_state); + if (ret) { + dev_err(&client->dev, "Err: Could not set direction of" + "vsel1 GPIO %d: %d\n", tps->vsel1_gpio, ret); + gpio_free(tps->vsel1_gpio); + goto err_gpio1; + } + tps->valid_gpios = true; + + /* + * Initialize the lru index with vset_reg id + * The index 0 will be most recently used and + * set with the tps->curr_vset_id */ + for (i = 0; i < 4; ++i) + tps->lru_index[i] = i; + tps->lru_index[0] = tps->curr_vset_id; + tps->lru_index[tps->curr_vset_id] = 0; + } + + ret = tps62360_init_dcdc(tps, pdata); + if (ret < 0) { + dev_err(tps->dev, "%s() Err: Init fails with = %d\n", + __func__, ret); + goto err_init; + } + + /* Register the regulators */ + rdev = regulator_register(&tps->desc, &client->dev, + &pdata->reg_init_data, tps, NULL); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "%s() Err: Failed to register %s\n", + __func__, id->name); + ret = PTR_ERR(rdev); + goto err_init; + } + + tps->rdev = rdev; + return 0; + +err_init: + if (gpio_is_valid(tps->vsel1_gpio)) + gpio_free(tps->vsel1_gpio); +err_gpio1: + if (gpio_is_valid(tps->vsel0_gpio)) + gpio_free(tps->vsel0_gpio); +err_gpio0: + regmap_exit(tps->regmap); + return ret; +} + +/** + * tps62360_remove - tps62360 driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister TPS driver as an i2c client device driver + */ +static int __devexit tps62360_remove(struct i2c_client *client) +{ + struct tps62360_chip *tps = i2c_get_clientdata(client); + + if (gpio_is_valid(tps->vsel1_gpio)) + gpio_free(tps->vsel1_gpio); + + if (gpio_is_valid(tps->vsel0_gpio)) + gpio_free(tps->vsel0_gpio); + + regulator_unregister(tps->rdev); + regmap_exit(tps->regmap); + return 0; +} + +static void tps62360_shutdown(struct i2c_client *client) +{ + struct tps62360_chip *tps = i2c_get_clientdata(client); + int st; + + if (!tps->en_discharge) + return; + + /* Configure the output discharge path */ + st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2)); + if (st < 0) + dev_err(tps->dev, "%s() fails in updating reg %d\n", + __func__, REG_RAMPCTRL); +} + +static const struct i2c_device_id tps62360_id[] = { + {.name = "tps62360", .driver_data = TPS62360}, + {.name = "tps62361", .driver_data = TPS62361}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tps62360_id); + +static struct i2c_driver tps62360_i2c_driver = { + .driver = { + .name = "tps62360", + .owner = THIS_MODULE, + }, + .probe = tps62360_probe, + .remove = __devexit_p(tps62360_remove), + .shutdown = tps62360_shutdown, + .id_table = tps62360_id, +}; + +static int __init tps62360_init(void) +{ + return i2c_add_driver(&tps62360_i2c_driver); +} +subsys_initcall(tps62360_init); + +static void __exit tps62360_cleanup(void) +{ + i2c_del_driver(&tps62360_i2c_driver); +} +module_exit(tps62360_cleanup); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("TPS62360 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 18d61a0529a..43e4902d7af 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -491,10 +491,6 @@ static int __devinit tps_65023_probe(struct i2c_client *client, regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, TPS65023_REG_CTRL2_CORE_ADJ, TPS65023_REG_CTRL2_CORE_ADJ); - /* Enable setting output voltage by I2C */ - regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, - TPS65023_REG_CTRL2_CORE_ADJ, TPS65023_REG_CTRL2_CORE_ADJ); - return 0; fail: diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 0b63ef71a5f..832833fe8aa 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -238,16 +238,16 @@ static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val) return err; } -static int tps6507x_pmic_dcdc_is_enabled(struct regulator_dev *dev) +static int tps6507x_pmic_is_enabled(struct regulator_dev *dev) { struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int data, dcdc = rdev_get_id(dev); + int data, rid = rdev_get_id(dev); u8 shift; - if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) + if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) return -EINVAL; - shift = TPS6507X_MAX_REG_ID - dcdc; + shift = TPS6507X_MAX_REG_ID - rid; data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1); if (data < 0) @@ -256,99 +256,65 @@ static int tps6507x_pmic_dcdc_is_enabled(struct regulator_dev *dev) return (data & 1<<shift) ? 1 : 0; } -static int tps6507x_pmic_ldo_is_enabled(struct regulator_dev *dev) +static int tps6507x_pmic_enable(struct regulator_dev *dev) { struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int data, ldo = rdev_get_id(dev); + int rid = rdev_get_id(dev); u8 shift; - if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) return -EINVAL; - shift = TPS6507X_MAX_REG_ID - ldo; - data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1); - - if (data < 0) - return data; - else - return (data & 1<<shift) ? 1 : 0; -} - -static int tps6507x_pmic_dcdc_enable(struct regulator_dev *dev) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int dcdc = rdev_get_id(dev); - u8 shift; - - if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) - return -EINVAL; - - shift = TPS6507X_MAX_REG_ID - dcdc; - return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); -} - -static int tps6507x_pmic_dcdc_disable(struct regulator_dev *dev) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int dcdc = rdev_get_id(dev); - u8 shift; - - if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) - return -EINVAL; - - shift = TPS6507X_MAX_REG_ID - dcdc; - return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1, - 1 << shift); -} - -static int tps6507x_pmic_ldo_enable(struct regulator_dev *dev) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int ldo = rdev_get_id(dev); - u8 shift; - - if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) - return -EINVAL; - - shift = TPS6507X_MAX_REG_ID - ldo; + shift = TPS6507X_MAX_REG_ID - rid; return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); } -static int tps6507x_pmic_ldo_disable(struct regulator_dev *dev) +static int tps6507x_pmic_disable(struct regulator_dev *dev) { struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int ldo = rdev_get_id(dev); + int rid = rdev_get_id(dev); u8 shift; - if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) return -EINVAL; - shift = TPS6507X_MAX_REG_ID - ldo; + shift = TPS6507X_MAX_REG_ID - rid; return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); } -static int tps6507x_pmic_dcdc_get_voltage(struct regulator_dev *dev) +static int tps6507x_pmic_get_voltage(struct regulator_dev *dev) { struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int data, dcdc = rdev_get_id(dev); - u8 reg; + int data, rid = rdev_get_id(dev); + u8 reg, mask; - switch (dcdc) { + switch (rid) { case TPS6507X_DCDC_1: reg = TPS6507X_REG_DEFDCDC1; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; break; case TPS6507X_DCDC_2: - if (tps->info[dcdc]->defdcdc_default) + if (tps->info[rid]->defdcdc_default) reg = TPS6507X_REG_DEFDCDC2_HIGH; else reg = TPS6507X_REG_DEFDCDC2_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; break; case TPS6507X_DCDC_3: - if (tps->info[dcdc]->defdcdc_default) + if (tps->info[rid]->defdcdc_default) reg = TPS6507X_REG_DEFDCDC3_HIGH; else reg = TPS6507X_REG_DEFDCDC3_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_LDO_1: + reg = TPS6507X_REG_LDO_CTRL1; + mask = TPS6507X_REG_LDO_CTRL1_LDO1_MASK; + break; + case TPS6507X_LDO_2: + reg = TPS6507X_REG_DEFLDO2; + mask = TPS6507X_REG_DEFLDO2_LDO2_MASK; break; default: return -EINVAL; @@ -358,193 +324,83 @@ static int tps6507x_pmic_dcdc_get_voltage(struct regulator_dev *dev) if (data < 0) return data; - data &= TPS6507X_DEFDCDCX_DCDC_MASK; - return tps->info[dcdc]->table[data] * 1000; + data &= mask; + return tps->info[rid]->table[data] * 1000; } -static int tps6507x_pmic_dcdc_set_voltage(struct regulator_dev *dev, - int min_uV, int max_uV, - unsigned *selector) +static int tps6507x_pmic_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) { struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int data, vsel, dcdc = rdev_get_id(dev); - u8 reg; + int data, rid = rdev_get_id(dev); + u8 reg, mask; - switch (dcdc) { + switch (rid) { case TPS6507X_DCDC_1: reg = TPS6507X_REG_DEFDCDC1; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; break; case TPS6507X_DCDC_2: - if (tps->info[dcdc]->defdcdc_default) + if (tps->info[rid]->defdcdc_default) reg = TPS6507X_REG_DEFDCDC2_HIGH; else reg = TPS6507X_REG_DEFDCDC2_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; break; case TPS6507X_DCDC_3: - if (tps->info[dcdc]->defdcdc_default) + if (tps->info[rid]->defdcdc_default) reg = TPS6507X_REG_DEFDCDC3_HIGH; else reg = TPS6507X_REG_DEFDCDC3_LOW; + mask = TPS6507X_DEFDCDCX_DCDC_MASK; + break; + case TPS6507X_LDO_1: + reg = TPS6507X_REG_LDO_CTRL1; + mask = TPS6507X_REG_LDO_CTRL1_LDO1_MASK; + break; + case TPS6507X_LDO_2: + reg = TPS6507X_REG_DEFLDO2; + mask = TPS6507X_REG_DEFLDO2_LDO2_MASK; break; default: return -EINVAL; } - if (min_uV < tps->info[dcdc]->min_uV - || min_uV > tps->info[dcdc]->max_uV) - return -EINVAL; - if (max_uV < tps->info[dcdc]->min_uV - || max_uV > tps->info[dcdc]->max_uV) - return -EINVAL; - - for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) { - int mV = tps->info[dcdc]->table[vsel]; - int uV = mV * 1000; - - /* Break at the first in-range value */ - if (min_uV <= uV && uV <= max_uV) - break; - } - - /* write to the register in case we found a match */ - if (vsel == tps->info[dcdc]->table_len) - return -EINVAL; - - *selector = vsel; - - data = tps6507x_pmic_reg_read(tps, reg); - if (data < 0) - return data; - - data &= ~TPS6507X_DEFDCDCX_DCDC_MASK; - data |= vsel; - - return tps6507x_pmic_reg_write(tps, reg, data); -} - -static int tps6507x_pmic_ldo_get_voltage(struct regulator_dev *dev) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int data, ldo = rdev_get_id(dev); - u8 reg, mask; - - if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) - return -EINVAL; - else { - reg = (ldo == TPS6507X_LDO_1 ? - TPS6507X_REG_LDO_CTRL1 : TPS6507X_REG_DEFLDO2); - mask = (ldo == TPS6507X_LDO_1 ? - TPS6507X_REG_LDO_CTRL1_LDO1_MASK : - TPS6507X_REG_DEFLDO2_LDO2_MASK); - } - - data = tps6507x_pmic_reg_read(tps, reg); - if (data < 0) - return data; - - data &= mask; - return tps->info[ldo]->table[data] * 1000; -} - -static int tps6507x_pmic_ldo_set_voltage(struct regulator_dev *dev, - int min_uV, int max_uV, - unsigned *selector) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int data, vsel, ldo = rdev_get_id(dev); - u8 reg, mask; - - if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) - return -EINVAL; - else { - reg = (ldo == TPS6507X_LDO_1 ? - TPS6507X_REG_LDO_CTRL1 : TPS6507X_REG_DEFLDO2); - mask = (ldo == TPS6507X_LDO_1 ? - TPS6507X_REG_LDO_CTRL1_LDO1_MASK : - TPS6507X_REG_DEFLDO2_LDO2_MASK); - } - - if (min_uV < tps->info[ldo]->min_uV || min_uV > tps->info[ldo]->max_uV) - return -EINVAL; - if (max_uV < tps->info[ldo]->min_uV || max_uV > tps->info[ldo]->max_uV) - return -EINVAL; - - for (vsel = 0; vsel < tps->info[ldo]->table_len; vsel++) { - int mV = tps->info[ldo]->table[vsel]; - int uV = mV * 1000; - - /* Break at the first in-range value */ - if (min_uV <= uV && uV <= max_uV) - break; - } - - if (vsel == tps->info[ldo]->table_len) - return -EINVAL; - - *selector = vsel; - data = tps6507x_pmic_reg_read(tps, reg); if (data < 0) return data; data &= ~mask; - data |= vsel; + data |= selector; return tps6507x_pmic_reg_write(tps, reg, data); } -static int tps6507x_pmic_dcdc_list_voltage(struct regulator_dev *dev, +static int tps6507x_pmic_list_voltage(struct regulator_dev *dev, unsigned selector) { struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int dcdc = rdev_get_id(dev); + int rid = rdev_get_id(dev); - if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) + if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) return -EINVAL; - if (selector >= tps->info[dcdc]->table_len) + if (selector >= tps->info[rid]->table_len) return -EINVAL; else - return tps->info[dcdc]->table[selector] * 1000; + return tps->info[rid]->table[selector] * 1000; } -static int tps6507x_pmic_ldo_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int ldo = rdev_get_id(dev); - - if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) - return -EINVAL; - - if (selector >= tps->info[ldo]->table_len) - return -EINVAL; - else - return tps->info[ldo]->table[selector] * 1000; -} - -/* Operations permitted on VDCDCx */ -static struct regulator_ops tps6507x_pmic_dcdc_ops = { - .is_enabled = tps6507x_pmic_dcdc_is_enabled, - .enable = tps6507x_pmic_dcdc_enable, - .disable = tps6507x_pmic_dcdc_disable, - .get_voltage = tps6507x_pmic_dcdc_get_voltage, - .set_voltage = tps6507x_pmic_dcdc_set_voltage, - .list_voltage = tps6507x_pmic_dcdc_list_voltage, +static struct regulator_ops tps6507x_pmic_ops = { + .is_enabled = tps6507x_pmic_is_enabled, + .enable = tps6507x_pmic_enable, + .disable = tps6507x_pmic_disable, + .get_voltage = tps6507x_pmic_get_voltage, + .set_voltage_sel = tps6507x_pmic_set_voltage_sel, + .list_voltage = tps6507x_pmic_list_voltage, }; -/* Operations permitted on LDOx */ -static struct regulator_ops tps6507x_pmic_ldo_ops = { - .is_enabled = tps6507x_pmic_ldo_is_enabled, - .enable = tps6507x_pmic_ldo_enable, - .disable = tps6507x_pmic_ldo_disable, - .get_voltage = tps6507x_pmic_ldo_get_voltage, - .set_voltage = tps6507x_pmic_ldo_set_voltage, - .list_voltage = tps6507x_pmic_ldo_list_voltage, -}; - -static __devinit -int tps6507x_pmic_probe(struct platform_device *pdev) +static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); struct tps_info *info = &tps6507x_pmic_regs[0]; @@ -593,8 +449,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev) tps->desc[i].name = info->name; tps->desc[i].id = i; tps->desc[i].n_voltages = info->table_len; - tps->desc[i].ops = (i > TPS6507X_DCDC_3 ? - &tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops); + tps->desc[i].ops = &tps6507x_pmic_ops; tps->desc[i].type = REGULATOR_VOLTAGE; tps->desc[i].owner = THIS_MODULE; @@ -648,22 +503,12 @@ static struct platform_driver tps6507x_pmic_driver = { .remove = __devexit_p(tps6507x_pmic_remove), }; -/** - * tps6507x_pmic_init - * - * Module init function - */ static int __init tps6507x_pmic_init(void) { return platform_driver_register(&tps6507x_pmic_driver); } subsys_initcall(tps6507x_pmic_init); -/** - * tps6507x_pmic_cleanup - * - * Module exit function - */ static void __exit tps6507x_pmic_cleanup(void) { platform_driver_unregister(&tps6507x_pmic_driver); diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c new file mode 100644 index 00000000000..e39521b4277 --- /dev/null +++ b/drivers/regulator/tps65217-regulator.c @@ -0,0 +1,378 @@ +/* + * tps65217-regulator.c + * + * Regulator driver for TPS65217 PMIC + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> + +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/tps65217.h> + +#define TPS65217_REGULATOR(_name, _id, _ops, _n) \ + { \ + .name = _name, \ + .id = _id, \ + .ops = &_ops, \ + .n_voltages = _n, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } \ + +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n, _em, _vr, _vm) \ + { \ + .name = _nm, \ + .min_uV = _min, \ + .max_uV = _max, \ + .vsel_to_uv = _f1, \ + .uv_to_vsel = _f2, \ + .table = _t, \ + .table_len = _n, \ + .enable_mask = _em, \ + .set_vout_reg = _vr, \ + .set_vout_mask = _vm, \ + } + +static const int LDO1_VSEL_table[] = { + 1000000, 1100000, 1200000, 1250000, + 1300000, 1350000, 1400000, 1500000, + 1600000, 1800000, 2500000, 2750000, + 2800000, 3000000, 3100000, 3300000, +}; + +static int tps65217_vsel_to_uv1(unsigned int vsel) +{ + int uV = 0; + + if (vsel > 63) + return -EINVAL; + + if (vsel <= 24) + uV = vsel * 25000 + 900000; + else if (vsel <= 52) + uV = (vsel - 24) * 50000 + 1500000; + else if (vsel < 56) + uV = (vsel - 52) * 100000 + 2900000; + else + uV = 3300000; + + return uV; +} + +static int tps65217_uv_to_vsel1(int uV, unsigned int *vsel) +{ + if ((uV < 0) && (uV > 3300000)) + return -EINVAL; + + if (uV <= 1500000) + *vsel = DIV_ROUND_UP(uV - 900000, 25000); + else if (uV <= 2900000) + *vsel = 24 + DIV_ROUND_UP(uV - 1500000, 50000); + else if (uV < 3300000) + *vsel = 52 + DIV_ROUND_UP(uV - 2900000, 100000); + else + *vsel = 56; + + return 0; +} + +static int tps65217_vsel_to_uv2(unsigned int vsel) +{ + int uV = 0; + + if (vsel > 31) + return -EINVAL; + + if (vsel <= 8) + uV = vsel * 50000 + 1500000; + else if (vsel <= 13) + uV = (vsel - 8) * 100000 + 1900000; + else + uV = (vsel - 13) * 50000 + 2400000; + + return uV; +} + +static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) +{ + if ((uV < 0) && (uV > 3300000)) + return -EINVAL; + + if (uV <= 1900000) + *vsel = DIV_ROUND_UP(uV - 1500000, 50000); + else if (uV <= 2400000) + *vsel = 8 + DIV_ROUND_UP(uV - 1900000, 100000); + else + *vsel = 13 + DIV_ROUND_UP(uV - 2400000, 50000); + + return 0; +} + +static struct tps_info tps65217_pmic_regs[] = { + TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC1_EN, + TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK), + TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC2_EN, + TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK), + TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC3_EN, + TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK), + TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, + 16, TPS65217_ENABLE_LDO1_EN, TPS65217_REG_DEFLDO1, + TPS65217_DEFLDO1_LDO1_MASK), + TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, + tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_LDO2_EN, + TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK), + TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, + tps65217_uv_to_vsel2, NULL, 32, + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, + TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK), + TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, + tps65217_uv_to_vsel2, NULL, 32, + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, + TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK), +}; + +static int tps65217_pmic_is_enabled(struct regulator_dev *dev) +{ + int ret; + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int data, rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + ret = tps65217_reg_read(tps, TPS65217_REG_ENABLE, &data); + if (ret) + return ret; + + return (data & tps->info[rid]->enable_mask) ? 1 : 0; +} + +static int tps65217_pmic_enable(struct regulator_dev *dev) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + /* Enable the regulator and password protection is level 1 */ + return tps65217_set_bits(tps, TPS65217_REG_ENABLE, + tps->info[rid]->enable_mask, + tps->info[rid]->enable_mask, + TPS65217_PROTECT_L1); +} + +static int tps65217_pmic_disable(struct regulator_dev *dev) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + /* Disable the regulator and password protection is level 1 */ + return tps65217_clear_bits(tps, TPS65217_REG_ENABLE, + tps->info[rid]->enable_mask, TPS65217_PROTECT_L1); +} + +static int tps65217_pmic_get_voltage_sel(struct regulator_dev *dev) +{ + int ret; + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int selector, rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + ret = tps65217_reg_read(tps, tps->info[rid]->set_vout_reg, &selector); + if (ret) + return ret; + + selector &= tps->info[rid]->set_vout_mask; + + return selector; +} + +static int tps65217_pmic_ldo1_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + + if (ldo != TPS65217_LDO_1) + return -EINVAL; + + if (selector >= tps->info[ldo]->table_len) + return -EINVAL; + + /* Set the voltage based on vsel value and write protect level is 2 */ + return tps65217_set_bits(tps, tps->info[ldo]->set_vout_reg, + tps->info[ldo]->set_vout_mask, + selector, TPS65217_PROTECT_L2); +} + +static int tps65217_pmic_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + int ret; + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + /* LDO1 implements set_voltage_sel callback */ + if (rid == TPS65217_LDO_1) + return -EINVAL; + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + if (min_uV < tps->info[rid]->min_uV + || min_uV > tps->info[rid]->max_uV) + return -EINVAL; + + if (max_uV < tps->info[rid]->min_uV + || max_uV > tps->info[rid]->max_uV) + return -EINVAL; + + ret = tps->info[rid]->uv_to_vsel(min_uV, selector); + if (ret) + return ret; + + /* Set the voltage based on vsel value and write protect level is 2 */ + ret = tps65217_set_bits(tps, tps->info[rid]->set_vout_reg, + tps->info[rid]->set_vout_mask, + *selector, TPS65217_PROTECT_L2); + + /* Set GO bit for DCDCx to initiate voltage transistion */ + switch (rid) { + case TPS65217_DCDC_1 ... TPS65217_DCDC_3: + ret = tps65217_set_bits(tps, TPS65217_REG_DEFSLEW, + TPS65217_DEFSLEW_GO, TPS65217_DEFSLEW_GO, + TPS65217_PROTECT_L2); + break; + } + + return ret; +} + +static int tps65217_pmic_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65217 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) + return -EINVAL; + + if (selector >= tps->info[rid]->table_len) + return -EINVAL; + + if (tps->info[rid]->table) + return tps->info[rid]->table[selector]; + + return tps->info[rid]->vsel_to_uv(selector); +} + +/* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ +static struct regulator_ops tps65217_pmic_ops = { + .is_enabled = tps65217_pmic_is_enabled, + .enable = tps65217_pmic_enable, + .disable = tps65217_pmic_disable, + .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .set_voltage = tps65217_pmic_set_voltage, + .list_voltage = tps65217_pmic_list_voltage, +}; + +/* Operations permitted on LDO1 */ +static struct regulator_ops tps65217_pmic_ldo1_ops = { + .is_enabled = tps65217_pmic_is_enabled, + .enable = tps65217_pmic_enable, + .disable = tps65217_pmic_disable, + .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .set_voltage_sel = tps65217_pmic_ldo1_set_voltage_sel, + .list_voltage = tps65217_pmic_list_voltage, +}; + +static struct regulator_desc regulators[] = { + TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64), + TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64), + TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64), + TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16), + TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64), + TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32), + TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32), +}; + +static int __devinit tps65217_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + struct tps65217 *tps; + struct tps_info *info = &tps65217_pmic_regs[pdev->id]; + + /* Already set by core driver */ + tps = dev_to_tps65217(pdev->dev.parent); + tps->info[pdev->id] = info; + + rdev = regulator_register(®ulators[pdev->id], &pdev->dev, + pdev->dev.platform_data, tps, NULL); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int __devexit tps65217_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver tps65217_regulator_driver = { + .driver = { + .name = "tps65217-pmic", + }, + .probe = tps65217_regulator_probe, + .remove = __devexit_p(tps65217_regulator_remove), +}; + +static int __init tps65217_regulator_init(void) +{ + return platform_driver_register(&tps65217_regulator_driver); +} +subsys_initcall(tps65217_regulator_init); + +static void __exit tps65217_regulator_exit(void) +{ + platform_driver_unregister(&tps65217_regulator_driver); +} +module_exit(tps65217_regulator_exit); + +MODULE_AUTHOR("AnilKumar Ch <anilkumar@ti.com>"); +MODULE_DESCRIPTION("TPS65217 voltage regulator driver"); +MODULE_ALIAS("platform:tps65217-pmic"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 70b7b1f4f00..60846e00c16 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -108,9 +108,7 @@ #define N_DCDC 3 #define N_LDO 2 #define N_SWITCH 2 -#define N_REGULATORS (3 /* DCDC */ + \ - 2 /* LDO */ + \ - 2 /* switch */) +#define N_REGULATORS (N_DCDC + N_LDO + N_SWITCH) #define FIXED_ILIMSEL BIT(0) #define FIXED_VOLTAGE BIT(1) diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index c75fb20faa5..29b615ce3af 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -383,7 +383,7 @@ static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) int id = pdev->id; int err; - dev_dbg(&pdev->dev, "Probing reulator %d\n", id); + dev_dbg(&pdev->dev, "Probing regulator %d\n", id); ri = find_regulator_info(id); if (ri == NULL) { diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 5c15ba01e9c..3d1370f9bc7 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -26,6 +26,10 @@ #include <linux/mfd/tps65910.h> #define TPS65910_SUPPLY_STATE_ENABLED 0x1 +#define EXT_SLEEP_CONTROL (TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1 | \ + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2 | \ + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3 | \ + TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) /* supported VIO voltages in milivolts */ static const u16 VIO_VSEL_table[] = { @@ -83,161 +87,235 @@ struct tps_info { const char *name; unsigned min_uV; unsigned max_uV; - u8 table_len; - const u16 *table; + u8 n_voltages; + const u16 *voltage_table; + int enable_time_us; }; static struct tps_info tps65910_regs[] = { { .name = "VRTC", + .enable_time_us = 2200, }, { .name = "VIO", .min_uV = 1500000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(VIO_VSEL_table), - .table = VIO_VSEL_table, + .n_voltages = ARRAY_SIZE(VIO_VSEL_table), + .voltage_table = VIO_VSEL_table, + .enable_time_us = 350, }, { .name = "VDD1", .min_uV = 600000, .max_uV = 4500000, + .enable_time_us = 350, }, { .name = "VDD2", .min_uV = 600000, .max_uV = 4500000, + .enable_time_us = 350, }, { .name = "VDD3", .min_uV = 5000000, .max_uV = 5000000, - .table_len = ARRAY_SIZE(VDD3_VSEL_table), - .table = VDD3_VSEL_table, + .n_voltages = ARRAY_SIZE(VDD3_VSEL_table), + .voltage_table = VDD3_VSEL_table, + .enable_time_us = 200, }, { .name = "VDIG1", .min_uV = 1200000, .max_uV = 2700000, - .table_len = ARRAY_SIZE(VDIG1_VSEL_table), - .table = VDIG1_VSEL_table, + .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table), + .voltage_table = VDIG1_VSEL_table, + .enable_time_us = 100, }, { .name = "VDIG2", .min_uV = 1000000, .max_uV = 1800000, - .table_len = ARRAY_SIZE(VDIG2_VSEL_table), - .table = VDIG2_VSEL_table, + .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table), + .voltage_table = VDIG2_VSEL_table, + .enable_time_us = 100, }, { .name = "VPLL", .min_uV = 1000000, .max_uV = 2500000, - .table_len = ARRAY_SIZE(VPLL_VSEL_table), - .table = VPLL_VSEL_table, + .n_voltages = ARRAY_SIZE(VPLL_VSEL_table), + .voltage_table = VPLL_VSEL_table, + .enable_time_us = 100, }, { .name = "VDAC", .min_uV = 1800000, .max_uV = 2850000, - .table_len = ARRAY_SIZE(VDAC_VSEL_table), - .table = VDAC_VSEL_table, + .n_voltages = ARRAY_SIZE(VDAC_VSEL_table), + .voltage_table = VDAC_VSEL_table, + .enable_time_us = 100, }, { .name = "VAUX1", .min_uV = 1800000, .max_uV = 2850000, - .table_len = ARRAY_SIZE(VAUX1_VSEL_table), - .table = VAUX1_VSEL_table, + .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table), + .voltage_table = VAUX1_VSEL_table, + .enable_time_us = 100, }, { .name = "VAUX2", .min_uV = 1800000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(VAUX2_VSEL_table), - .table = VAUX2_VSEL_table, + .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table), + .voltage_table = VAUX2_VSEL_table, + .enable_time_us = 100, }, { .name = "VAUX33", .min_uV = 1800000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(VAUX33_VSEL_table), - .table = VAUX33_VSEL_table, + .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table), + .voltage_table = VAUX33_VSEL_table, + .enable_time_us = 100, }, { .name = "VMMC", .min_uV = 1800000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(VMMC_VSEL_table), - .table = VMMC_VSEL_table, + .n_voltages = ARRAY_SIZE(VMMC_VSEL_table), + .voltage_table = VMMC_VSEL_table, + .enable_time_us = 100, }, }; static struct tps_info tps65911_regs[] = { { + .name = "VRTC", + .enable_time_us = 2200, + }, + { .name = "VIO", .min_uV = 1500000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(VIO_VSEL_table), - .table = VIO_VSEL_table, + .n_voltages = ARRAY_SIZE(VIO_VSEL_table), + .voltage_table = VIO_VSEL_table, + .enable_time_us = 350, }, { .name = "VDD1", .min_uV = 600000, .max_uV = 4500000, + .n_voltages = 73, + .enable_time_us = 350, }, { .name = "VDD2", .min_uV = 600000, .max_uV = 4500000, + .n_voltages = 73, + .enable_time_us = 350, }, { .name = "VDDCTRL", .min_uV = 600000, .max_uV = 1400000, + .n_voltages = 65, + .enable_time_us = 900, }, { .name = "LDO1", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 47, + .enable_time_us = 420, }, { .name = "LDO2", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 47, + .enable_time_us = 420, }, { .name = "LDO3", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 24, + .enable_time_us = 230, }, { .name = "LDO4", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 47, + .enable_time_us = 230, }, { .name = "LDO5", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 24, + .enable_time_us = 230, }, { .name = "LDO6", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 24, + .enable_time_us = 230, }, { .name = "LDO7", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 24, + .enable_time_us = 230, }, { .name = "LDO8", .min_uV = 1000000, .max_uV = 3300000, + .n_voltages = 24, + .enable_time_us = 230, }, }; +#define EXT_CONTROL_REG_BITS(id, regs_offs, bits) (((regs_offs) << 8) | (bits)) +static unsigned int tps65910_ext_sleep_control[] = { + 0, + EXT_CONTROL_REG_BITS(VIO, 1, 0), + EXT_CONTROL_REG_BITS(VDD1, 1, 1), + EXT_CONTROL_REG_BITS(VDD2, 1, 2), + EXT_CONTROL_REG_BITS(VDD3, 1, 3), + EXT_CONTROL_REG_BITS(VDIG1, 0, 1), + EXT_CONTROL_REG_BITS(VDIG2, 0, 2), + EXT_CONTROL_REG_BITS(VPLL, 0, 6), + EXT_CONTROL_REG_BITS(VDAC, 0, 7), + EXT_CONTROL_REG_BITS(VAUX1, 0, 3), + EXT_CONTROL_REG_BITS(VAUX2, 0, 4), + EXT_CONTROL_REG_BITS(VAUX33, 0, 5), + EXT_CONTROL_REG_BITS(VMMC, 0, 0), +}; + +static unsigned int tps65911_ext_sleep_control[] = { + 0, + EXT_CONTROL_REG_BITS(VIO, 1, 0), + EXT_CONTROL_REG_BITS(VDD1, 1, 1), + EXT_CONTROL_REG_BITS(VDD2, 1, 2), + EXT_CONTROL_REG_BITS(VDDCTRL, 1, 3), + EXT_CONTROL_REG_BITS(LDO1, 0, 1), + EXT_CONTROL_REG_BITS(LDO2, 0, 2), + EXT_CONTROL_REG_BITS(LDO3, 0, 7), + EXT_CONTROL_REG_BITS(LDO4, 0, 6), + EXT_CONTROL_REG_BITS(LDO5, 0, 3), + EXT_CONTROL_REG_BITS(LDO6, 0, 0), + EXT_CONTROL_REG_BITS(LDO7, 0, 5), + EXT_CONTROL_REG_BITS(LDO8, 0, 4), +}; + struct tps65910_reg { struct regulator_desc *desc; struct tps65910 *mfd; @@ -247,6 +325,8 @@ struct tps65910_reg { int num_regulators; int mode; int (*get_ctrl_reg)(int); + unsigned int *ext_sleep_control; + unsigned int board_ext_control[TPS65910_NUM_REGS]; }; static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg) @@ -429,6 +509,12 @@ static int tps65910_disable(struct regulator_dev *dev) return tps65910_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED); } +static int tps65910_enable_time(struct regulator_dev *dev) +{ + struct tps65910_reg *pmic = rdev_get_drvdata(dev); + int id = rdev_get_id(dev); + return pmic->info[id]->enable_time_us; +} static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) { @@ -467,7 +553,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) if (value < 0) return value; - if (value & LDO_ST_ON_BIT) + if (!(value & LDO_ST_ON_BIT)) return REGULATOR_MODE_STANDBY; else if (value & LDO_ST_MODE_BIT) return REGULATOR_MODE_IDLE; @@ -475,10 +561,10 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev) return REGULATOR_MODE_NORMAL; } -static int tps65910_get_voltage_dcdc(struct regulator_dev *dev) +static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev), voltage = 0; + int id = rdev_get_id(dev); int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0; switch (id) { @@ -522,9 +608,7 @@ static int tps65910_get_voltage_dcdc(struct regulator_dev *dev) srvsel = 3; if (srvsel > vselmax) srvsel = vselmax; - srvsel -= 3; - - voltage = (srvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100; + return srvsel - 3; } else { /* normalise to valid range*/ @@ -532,14 +616,9 @@ static int tps65910_get_voltage_dcdc(struct regulator_dev *dev) opvsel = 3; if (opvsel > vselmax) opvsel = vselmax; - opvsel -= 3; - - voltage = (opvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100; + return opvsel - 3; } - - voltage *= mult; - - return voltage; + return -EINVAL; } static int tps65910_get_voltage(struct regulator_dev *dev) @@ -572,7 +651,7 @@ static int tps65910_get_voltage(struct regulator_dev *dev) return -EINVAL; } - voltage = pmic->info[id]->table[value] * 1000; + voltage = pmic->info[id]->voltage_table[value] * 1000; return voltage; } @@ -622,8 +701,9 @@ static int tps65911_get_voltage(struct regulator_dev *dev) step_mv = 100; break; case TPS65910_REG_VIO: - return pmic->info[id]->table[value] * 1000; - break; + value &= LDO_SEL_MASK; + value >>= LDO_SEL_SHIFT; + return pmic->info[id]->voltage_table[value] * 1000; default: return -EINVAL; } @@ -631,8 +711,8 @@ static int tps65911_get_voltage(struct regulator_dev *dev) return (LDO_MIN_VOLT + value * step_mv) * 1000; } -static int tps65910_set_voltage_dcdc(struct regulator_dev *dev, - unsigned selector) +static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev, + unsigned selector) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); int id = rdev_get_id(dev), vsel; @@ -669,7 +749,8 @@ static int tps65910_set_voltage_dcdc(struct regulator_dev *dev, return 0; } -static int tps65910_set_voltage(struct regulator_dev *dev, unsigned selector) +static int tps65910_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); int reg, id = rdev_get_id(dev); @@ -695,7 +776,8 @@ static int tps65910_set_voltage(struct regulator_dev *dev, unsigned selector) return -EINVAL; } -static int tps65911_set_voltage(struct regulator_dev *dev, unsigned selector) +static int tps65911_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); int reg, id = rdev_get_id(dev); @@ -715,9 +797,11 @@ static int tps65911_set_voltage(struct regulator_dev *dev, unsigned selector) case TPS65911_REG_LDO6: case TPS65911_REG_LDO7: case TPS65911_REG_LDO8: - case TPS65910_REG_VIO: return tps65910_modify_bits(pmic, reg, (selector << LDO_SEL_SHIFT), LDO3_SEL_MASK); + case TPS65910_REG_VIO: + return tps65910_modify_bits(pmic, reg, + (selector << LDO_SEL_SHIFT), LDO_SEL_MASK); } return -EINVAL; @@ -756,10 +840,10 @@ static int tps65910_list_voltage(struct regulator_dev *dev, if (id < TPS65910_REG_VIO || id > TPS65910_REG_VMMC) return -EINVAL; - if (selector >= pmic->info[id]->table_len) + if (selector >= pmic->info[id]->n_voltages) return -EINVAL; else - voltage = pmic->info[id]->table[selector] * 1000; + voltage = pmic->info[id]->voltage_table[selector] * 1000; return voltage; } @@ -795,7 +879,7 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) step_mv = 100; break; case TPS65910_REG_VIO: - return pmic->info[id]->table[selector] * 1000; + return pmic->info[id]->voltage_table[selector] * 1000; default: return -EINVAL; } @@ -803,15 +887,42 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) return (LDO_MIN_VOLT + selector * step_mv) * 1000; } +static int tps65910_set_voltage_dcdc_time_sel(struct regulator_dev *dev, + unsigned int old_selector, unsigned int new_selector) +{ + int id = rdev_get_id(dev); + int old_volt, new_volt; + + old_volt = tps65910_list_voltage_dcdc(dev, old_selector); + if (old_volt < 0) + return old_volt; + + new_volt = tps65910_list_voltage_dcdc(dev, new_selector); + if (new_volt < 0) + return new_volt; + + /* VDD1 and VDD2 are 12.5mV/us, VDDCTRL is 100mV/20us */ + switch (id) { + case TPS65910_REG_VDD1: + case TPS65910_REG_VDD2: + return DIV_ROUND_UP(abs(old_volt - new_volt), 12500); + case TPS65911_REG_VDDCTRL: + return DIV_ROUND_UP(abs(old_volt - new_volt), 5000); + } + return -EINVAL; +} + /* Regulator ops (except VRTC) */ static struct regulator_ops tps65910_ops_dcdc = { .is_enabled = tps65910_is_enabled, .enable = tps65910_enable, .disable = tps65910_disable, + .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, - .get_voltage = tps65910_get_voltage_dcdc, - .set_voltage_sel = tps65910_set_voltage_dcdc, + .get_voltage_sel = tps65910_get_voltage_dcdc_sel, + .set_voltage_sel = tps65910_set_voltage_dcdc_sel, + .set_voltage_time_sel = tps65910_set_voltage_dcdc_time_sel, .list_voltage = tps65910_list_voltage_dcdc, }; @@ -819,6 +930,7 @@ static struct regulator_ops tps65910_ops_vdd3 = { .is_enabled = tps65910_is_enabled, .enable = tps65910_enable, .disable = tps65910_disable, + .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage = tps65910_get_voltage_vdd3, @@ -829,10 +941,11 @@ static struct regulator_ops tps65910_ops = { .is_enabled = tps65910_is_enabled, .enable = tps65910_enable, .disable = tps65910_disable, + .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage = tps65910_get_voltage, - .set_voltage_sel = tps65910_set_voltage, + .set_voltage_sel = tps65910_set_voltage_sel, .list_voltage = tps65910_list_voltage, }; @@ -840,13 +953,147 @@ static struct regulator_ops tps65911_ops = { .is_enabled = tps65910_is_enabled, .enable = tps65910_enable, .disable = tps65910_disable, + .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage = tps65911_get_voltage, - .set_voltage_sel = tps65911_set_voltage, + .set_voltage_sel = tps65911_set_voltage_sel, .list_voltage = tps65911_list_voltage, }; +static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, + int id, int ext_sleep_config) +{ + struct tps65910 *mfd = pmic->mfd; + u8 regoffs = (pmic->ext_sleep_control[id] >> 8) & 0xFF; + u8 bit_pos = (1 << pmic->ext_sleep_control[id] & 0xFF); + int ret; + + /* + * Regulator can not be control from multiple external input EN1, EN2 + * and EN3 together. + */ + if (ext_sleep_config & EXT_SLEEP_CONTROL) { + int en_count; + en_count = ((ext_sleep_config & + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) != 0); + en_count += ((ext_sleep_config & + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) != 0); + en_count += ((ext_sleep_config & + TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) != 0); + en_count += ((ext_sleep_config & + TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) != 0); + if (en_count > 1) { + dev_err(mfd->dev, + "External sleep control flag is not proper\n"); + return -EINVAL; + } + } + + pmic->board_ext_control[id] = ext_sleep_config; + + /* External EN1 control */ + if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) + ret = tps65910_set_bits(mfd, + TPS65910_EN1_LDO_ASS + regoffs, bit_pos); + else + ret = tps65910_clear_bits(mfd, + TPS65910_EN1_LDO_ASS + regoffs, bit_pos); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring external control EN1\n"); + return ret; + } + + /* External EN2 control */ + if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) + ret = tps65910_set_bits(mfd, + TPS65910_EN2_LDO_ASS + regoffs, bit_pos); + else + ret = tps65910_clear_bits(mfd, + TPS65910_EN2_LDO_ASS + regoffs, bit_pos); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring external control EN2\n"); + return ret; + } + + /* External EN3 control for TPS65910 LDO only */ + if ((tps65910_chip_id(mfd) == TPS65910) && + (id >= TPS65910_REG_VDIG1)) { + if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) + ret = tps65910_set_bits(mfd, + TPS65910_EN3_LDO_ASS + regoffs, bit_pos); + else + ret = tps65910_clear_bits(mfd, + TPS65910_EN3_LDO_ASS + regoffs, bit_pos); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring external control EN3\n"); + return ret; + } + } + + /* Return if no external control is selected */ + if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) { + /* Clear all sleep controls */ + ret = tps65910_clear_bits(mfd, + TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); + if (!ret) + ret = tps65910_clear_bits(mfd, + TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); + if (ret < 0) + dev_err(mfd->dev, + "Error in configuring SLEEP register\n"); + return ret; + } + + /* + * For regulator that has separate operational and sleep register make + * sure that operational is used and clear sleep register to turn + * regulator off when external control is inactive + */ + if ((id == TPS65910_REG_VDD1) || + (id == TPS65910_REG_VDD2) || + ((id == TPS65911_REG_VDDCTRL) && + (tps65910_chip_id(mfd) == TPS65911))) { + int op_reg_add = pmic->get_ctrl_reg(id) + 1; + int sr_reg_add = pmic->get_ctrl_reg(id) + 2; + int opvsel = tps65910_reg_read(pmic, op_reg_add); + int srvsel = tps65910_reg_read(pmic, sr_reg_add); + if (opvsel & VDD1_OP_CMD_MASK) { + u8 reg_val = srvsel & VDD1_OP_SEL_MASK; + ret = tps65910_reg_write(pmic, op_reg_add, reg_val); + if (ret < 0) { + dev_err(mfd->dev, + "Error in configuring op register\n"); + return ret; + } + } + ret = tps65910_reg_write(pmic, sr_reg_add, 0); + if (ret < 0) { + dev_err(mfd->dev, "Error in settting sr register\n"); + return ret; + } + } + + ret = tps65910_clear_bits(mfd, + TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos); + if (!ret) { + if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) + ret = tps65910_set_bits(mfd, + TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); + else + ret = tps65910_clear_bits(mfd, + TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos); + } + if (ret < 0) + dev_err(mfd->dev, + "Error in configuring SLEEP register\n"); + + return ret; +} + static __devinit int tps65910_probe(struct platform_device *pdev) { struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); @@ -877,11 +1124,13 @@ static __devinit int tps65910_probe(struct platform_device *pdev) case TPS65910: pmic->get_ctrl_reg = &tps65910_get_ctrl_register; pmic->num_regulators = ARRAY_SIZE(tps65910_regs); + pmic->ext_sleep_control = tps65910_ext_sleep_control; info = tps65910_regs; break; case TPS65911: pmic->get_ctrl_reg = &tps65911_get_ctrl_register; pmic->num_regulators = ARRAY_SIZE(tps65911_regs); + pmic->ext_sleep_control = tps65911_ext_sleep_control; info = tps65911_regs; break; default: @@ -926,7 +1175,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->desc[i].name = info->name; pmic->desc[i].id = i; - pmic->desc[i].n_voltages = info->table_len; + pmic->desc[i].n_voltages = info->n_voltages; if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) { pmic->desc[i].ops = &tps65910_ops_dcdc; @@ -944,6 +1193,16 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->desc[i].ops = &tps65911_ops; } + err = tps65910_set_ext_sleep_config(pmic, i, + pmic_plat_data->regulator_ext_sleep_control[i]); + /* + * Failing on regulator for configuring externally control + * is not a serious issue, just throw warning. + */ + if (err < 0) + dev_warn(tps65910->dev, + "Failed to initialise ext control config\n"); + pmic->desc[i].type = REGULATOR_VOLTAGE; pmic->desc[i].owner = THIS_MODULE; @@ -990,6 +1249,36 @@ static int __devexit tps65910_remove(struct platform_device *pdev) return 0; } +static void tps65910_shutdown(struct platform_device *pdev) +{ + struct tps65910_reg *pmic = platform_get_drvdata(pdev); + int i; + + /* + * Before bootloader jumps to kernel, it makes sure that required + * external control signals are in desired state so that given rails + * can be configure accordingly. + * If rails are configured to be controlled from external control + * then before shutting down/rebooting the system, the external + * control configuration need to be remove from the rails so that + * its output will be available as per register programming even + * if external controls are removed. This is require when the POR + * value of the control signals are not in active state and before + * bootloader initializes it, the system requires the rail output + * to be active for booting. + */ + for (i = 0; i < pmic->num_regulators; i++) { + int err; + if (!pmic->rdev[i]) + continue; + + err = tps65910_set_ext_sleep_config(pmic, i, 0); + if (err < 0) + dev_err(&pdev->dev, + "Error in clearing external control\n"); + } +} + static struct platform_driver tps65910_driver = { .driver = { .name = "tps65910-pmic", @@ -997,6 +1286,7 @@ static struct platform_driver tps65910_driver = { }, .probe = tps65910_probe, .remove = __devexit_p(tps65910_remove), + .shutdown = tps65910_shutdown, }; static int __init tps65910_init(void) @@ -1012,6 +1302,6 @@ static void __exit tps65910_cleanup(void) module_exit(tps65910_cleanup); MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); -MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); +MODULE_DESCRIPTION("TPS65910/TPS65911 voltage regulator driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:tps65910-pmic"); diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index da00d88f94b..b36799b1f53 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -114,10 +114,7 @@ struct tps65912_reg { struct mutex io_lock; int mode; int (*get_ctrl_reg)(int); - int dcdc1_range; - int dcdc2_range; - int dcdc3_range; - int dcdc4_range; + int dcdc_range[TPS65912_NUM_DCDC]; int pwm_mode_reg; int eco_reg; }; @@ -125,46 +122,31 @@ struct tps65912_reg { static int tps65912_get_range(struct tps65912_reg *pmic, int id) { struct tps65912 *mfd = pmic->mfd; - - if (id > TPS65912_REG_DCDC4) - return 0; + int range; switch (id) { case TPS65912_REG_DCDC1: - pmic->dcdc1_range = tps65912_reg_read(mfd, - TPS65912_DCDC1_LIMIT); - if (pmic->dcdc1_range < 0) - return pmic->dcdc1_range; - pmic->dcdc1_range = (pmic->dcdc1_range & - DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; - return pmic->dcdc1_range; + range = tps65912_reg_read(mfd, TPS65912_DCDC1_LIMIT); + break; case TPS65912_REG_DCDC2: - pmic->dcdc2_range = tps65912_reg_read(mfd, - TPS65912_DCDC2_LIMIT); - if (pmic->dcdc2_range < 0) - return pmic->dcdc2_range; - pmic->dcdc2_range = (pmic->dcdc2_range & - DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; - return pmic->dcdc2_range; + range = tps65912_reg_read(mfd, TPS65912_DCDC2_LIMIT); + break; case TPS65912_REG_DCDC3: - pmic->dcdc3_range = tps65912_reg_read(mfd, - TPS65912_DCDC3_LIMIT); - if (pmic->dcdc3_range < 0) - return pmic->dcdc3_range; - pmic->dcdc3_range = (pmic->dcdc3_range & - DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; - return pmic->dcdc3_range; + range = tps65912_reg_read(mfd, TPS65912_DCDC3_LIMIT); + break; case TPS65912_REG_DCDC4: - pmic->dcdc4_range = tps65912_reg_read(mfd, - TPS65912_DCDC4_LIMIT); - if (pmic->dcdc4_range < 0) - return pmic->dcdc4_range; - pmic->dcdc4_range = (pmic->dcdc4_range & - DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; - return pmic->dcdc4_range; + range = tps65912_reg_read(mfd, TPS65912_DCDC4_LIMIT); + break; default: return 0; } + + if (range >= 0) + range = (range & DCDC_LIMIT_RANGE_MASK) + >> DCDC_LIMIT_RANGE_SHIFT; + + pmic->dcdc_range[id] = range; + return range; } static unsigned long tps65912_vsel_to_uv_range0(u8 vsel) @@ -219,146 +201,30 @@ static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel) static int tps65912_get_ctrl_register(int id) { - switch (id) { - case TPS65912_REG_DCDC1: - return TPS65912_DCDC1_AVS; - case TPS65912_REG_DCDC2: - return TPS65912_DCDC2_AVS; - case TPS65912_REG_DCDC3: - return TPS65912_DCDC3_AVS; - case TPS65912_REG_DCDC4: - return TPS65912_DCDC4_AVS; - case TPS65912_REG_LDO1: - return TPS65912_LDO1_AVS; - case TPS65912_REG_LDO2: - return TPS65912_LDO2_AVS; - case TPS65912_REG_LDO3: - return TPS65912_LDO3_AVS; - case TPS65912_REG_LDO4: - return TPS65912_LDO4_AVS; - case TPS65912_REG_LDO5: - return TPS65912_LDO5; - case TPS65912_REG_LDO6: - return TPS65912_LDO6; - case TPS65912_REG_LDO7: - return TPS65912_LDO7; - case TPS65912_REG_LDO8: - return TPS65912_LDO8; - case TPS65912_REG_LDO9: - return TPS65912_LDO9; - case TPS65912_REG_LDO10: - return TPS65912_LDO10; - default: + if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) + return id * 3 + TPS65912_DCDC1_AVS; + else if (id >= TPS65912_REG_LDO5 && id <= TPS65912_REG_LDO10) + return id - TPS65912_REG_LDO5 + TPS65912_LDO5; + else return -EINVAL; - } } -static int tps65912_get_dcdc_sel_register(struct tps65912_reg *pmic, int id) +static int tps65912_get_sel_register(struct tps65912_reg *pmic, int id) { struct tps65912 *mfd = pmic->mfd; - int opvsel = 0, sr = 0; + int opvsel; u8 reg = 0; - if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_DCDC4) - return -EINVAL; - - switch (id) { - case TPS65912_REG_DCDC1: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP); - sr = ((opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT); - if (sr) - reg = TPS65912_DCDC1_AVS; - else - reg = TPS65912_DCDC1_OP; - break; - case TPS65912_REG_DCDC2: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_DCDC2_AVS; - else - reg = TPS65912_DCDC2_OP; - break; - case TPS65912_REG_DCDC3: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_DCDC3_AVS; - else - reg = TPS65912_DCDC3_OP; - break; - case TPS65912_REG_DCDC4: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_DCDC4_AVS; + if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) { + opvsel = tps65912_reg_read(mfd, id * 3 + TPS65912_DCDC1_OP); + if (opvsel & OP_SELREG_MASK) + reg = id * 3 + TPS65912_DCDC1_AVS; else - reg = TPS65912_DCDC4_OP; - break; - } - return reg; -} - -static int tps65912_get_ldo_sel_register(struct tps65912_reg *pmic, int id) -{ - struct tps65912 *mfd = pmic->mfd; - int opvsel = 0, sr = 0; - u8 reg = 0; - - if (id < TPS65912_REG_LDO1 || id > TPS65912_REG_LDO10) + reg = id * 3 + TPS65912_DCDC1_OP; + } else if (id >= TPS65912_REG_LDO5 && id <= TPS65912_REG_LDO10) { + reg = id - TPS65912_REG_LDO5 + TPS65912_LDO5; + } else { return -EINVAL; - - switch (id) { - case TPS65912_REG_LDO1: - opvsel = tps65912_reg_read(mfd, TPS65912_LDO1_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_LDO1_AVS; - else - reg = TPS65912_LDO1_OP; - break; - case TPS65912_REG_LDO2: - opvsel = tps65912_reg_read(mfd, TPS65912_LDO2_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_LDO2_AVS; - else - reg = TPS65912_LDO2_OP; - break; - case TPS65912_REG_LDO3: - opvsel = tps65912_reg_read(mfd, TPS65912_LDO3_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_LDO3_AVS; - else - reg = TPS65912_LDO3_OP; - break; - case TPS65912_REG_LDO4: - opvsel = tps65912_reg_read(mfd, TPS65912_LDO4_OP); - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - reg = TPS65912_LDO4_AVS; - else - reg = TPS65912_LDO4_OP; - break; - case TPS65912_REG_LDO5: - reg = TPS65912_LDO5; - break; - case TPS65912_REG_LDO6: - reg = TPS65912_LDO6; - break; - case TPS65912_REG_LDO7: - reg = TPS65912_LDO7; - break; - case TPS65912_REG_LDO8: - reg = TPS65912_LDO8; - break; - case TPS65912_REG_LDO9: - reg = TPS65912_LDO9; - break; - case TPS65912_REG_LDO10: - reg = TPS65912_LDO10; - break; } return reg; @@ -506,151 +372,83 @@ static unsigned int tps65912_get_mode(struct regulator_dev *dev) return mode; } -static int tps65912_get_voltage_dcdc(struct regulator_dev *dev) +static int tps65912_list_voltage_dcdc(struct regulator_dev *dev, + unsigned selector) { struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev), voltage = 0, range; - int opvsel = 0, avsel = 0, sr, vsel; + int range, voltage = 0, id = rdev_get_id(dev); - switch (id) { - case TPS65912_REG_DCDC1: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP); - avsel = tps65912_reg_read(mfd, TPS65912_DCDC1_AVS); - range = pmic->dcdc1_range; - break; - case TPS65912_REG_DCDC2: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP); - avsel = tps65912_reg_read(mfd, TPS65912_DCDC2_AVS); - range = pmic->dcdc2_range; - break; - case TPS65912_REG_DCDC3: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP); - avsel = tps65912_reg_read(mfd, TPS65912_DCDC3_AVS); - range = pmic->dcdc3_range; - break; - case TPS65912_REG_DCDC4: - opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP); - avsel = tps65912_reg_read(mfd, TPS65912_DCDC4_AVS); - range = pmic->dcdc4_range; - break; - default: + if (id > TPS65912_REG_DCDC4) return -EINVAL; - } - sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; - if (sr) - vsel = avsel; - else - vsel = opvsel; - vsel &= 0x3F; + range = pmic->dcdc_range[id]; switch (range) { case 0: /* 0.5 - 1.2875V in 12.5mV steps */ - voltage = tps65912_vsel_to_uv_range0(vsel); + voltage = tps65912_vsel_to_uv_range0(selector); break; case 1: /* 0.7 - 1.4875V in 12.5mV steps */ - voltage = tps65912_vsel_to_uv_range1(vsel); + voltage = tps65912_vsel_to_uv_range1(selector); break; case 2: /* 0.5 - 2.075V in 25mV steps */ - voltage = tps65912_vsel_to_uv_range2(vsel); + voltage = tps65912_vsel_to_uv_range2(selector); break; case 3: /* 0.5 - 3.8V in 50mV steps */ - voltage = tps65912_vsel_to_uv_range3(vsel); + voltage = tps65912_vsel_to_uv_range3(selector); break; } return voltage; } -static int tps65912_set_voltage_dcdc(struct regulator_dev *dev, - unsigned selector) +static int tps65912_get_voltage_dcdc(struct regulator_dev *dev) { struct tps65912_reg *pmic = rdev_get_drvdata(dev); struct tps65912 *mfd = pmic->mfd; int id = rdev_get_id(dev); - int value; - u8 reg; - - reg = tps65912_get_dcdc_sel_register(pmic, id); - value = tps65912_reg_read(mfd, reg); - value &= 0xC0; - return tps65912_reg_write(mfd, reg, selector | value); -} + int reg, vsel; -static int tps65912_get_voltage_ldo(struct regulator_dev *dev) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev); - int vsel = 0; - u8 reg; + reg = tps65912_get_sel_register(pmic, id); + if (reg < 0) + return reg; - reg = tps65912_get_ldo_sel_register(pmic, id); vsel = tps65912_reg_read(mfd, reg); vsel &= 0x3F; - return tps65912_vsel_to_uv_ldo(vsel); + return tps65912_list_voltage_dcdc(dev, vsel); } -static int tps65912_set_voltage_ldo(struct regulator_dev *dev, - unsigned selector) +static int tps65912_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) { struct tps65912_reg *pmic = rdev_get_drvdata(dev); struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev), reg, value; + int id = rdev_get_id(dev); + int value; + u8 reg; - reg = tps65912_get_ldo_sel_register(pmic, id); + reg = tps65912_get_sel_register(pmic, id); value = tps65912_reg_read(mfd, reg); value &= 0xC0; return tps65912_reg_write(mfd, reg, selector | value); } -static int tps65912_list_voltage_dcdc(struct regulator_dev *dev, - unsigned selector) +static int tps65912_get_voltage_ldo(struct regulator_dev *dev) { struct tps65912_reg *pmic = rdev_get_drvdata(dev); - int range, voltage = 0, id = rdev_get_id(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int vsel = 0; + u8 reg; - switch (id) { - case TPS65912_REG_DCDC1: - range = pmic->dcdc1_range; - break; - case TPS65912_REG_DCDC2: - range = pmic->dcdc2_range; - break; - case TPS65912_REG_DCDC3: - range = pmic->dcdc3_range; - break; - case TPS65912_REG_DCDC4: - range = pmic->dcdc4_range; - break; - default: - return -EINVAL; - } + reg = tps65912_get_sel_register(pmic, id); + vsel = tps65912_reg_read(mfd, reg); + vsel &= 0x3F; - switch (range) { - case 0: - /* 0.5 - 1.2875V in 12.5mV steps */ - voltage = tps65912_vsel_to_uv_range0(selector); - break; - case 1: - /* 0.7 - 1.4875V in 12.5mV steps */ - voltage = tps65912_vsel_to_uv_range1(selector); - break; - case 2: - /* 0.5 - 2.075V in 25mV steps */ - voltage = tps65912_vsel_to_uv_range2(selector); - break; - case 3: - /* 0.5 - 3.8V in 50mV steps */ - voltage = tps65912_vsel_to_uv_range3(selector); - break; - } - return voltage; + return tps65912_vsel_to_uv_ldo(vsel); } static int tps65912_list_voltage_ldo(struct regulator_dev *dev, @@ -672,7 +470,7 @@ static struct regulator_ops tps65912_ops_dcdc = { .set_mode = tps65912_set_mode, .get_mode = tps65912_get_mode, .get_voltage = tps65912_get_voltage_dcdc, - .set_voltage_sel = tps65912_set_voltage_dcdc, + .set_voltage_sel = tps65912_set_voltage_sel, .list_voltage = tps65912_list_voltage_dcdc, }; @@ -682,7 +480,7 @@ static struct regulator_ops tps65912_ops_ldo = { .enable = tps65912_reg_enable, .disable = tps65912_reg_disable, .get_voltage = tps65912_get_voltage_ldo, - .set_voltage_sel = tps65912_set_voltage_ldo, + .set_voltage_sel = tps65912_set_voltage_sel, .list_voltage = tps65912_list_voltage_ldo, }; @@ -770,22 +568,12 @@ static struct platform_driver tps65912_driver = { .remove = __devexit_p(tps65912_remove), }; -/** - * tps65912_init - * - * Module init function - */ static int __init tps65912_init(void) { return platform_driver_register(&tps65912_driver); } subsys_initcall(tps65912_init); -/** - * tps65912_cleanup - * - * Module exit function - */ static void __exit tps65912_cleanup(void) { platform_driver_unregister(&tps65912_driver); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 6894009d815..ab1e183a74b 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -186,7 +186,7 @@ static int wm8350_isink_get_current(struct regulator_dev *rdev) return 0; } - return (isink_cur[val] + 50) / 100; + return DIV_ROUND_CLOSEST(isink_cur[val], 100); } /* turn on ISINK followed by DCDC */ @@ -1544,7 +1544,7 @@ int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink, return -ENOMEM; } - led->isink_consumer.dev = &pdev->dev; + led->isink_consumer.dev_name = dev_name(&pdev->dev); led->isink_consumer.supply = "led_isink"; led->isink_init.num_consumer_supplies = 1; led->isink_init.consumer_supplies = &led->isink_consumer; @@ -1559,7 +1559,7 @@ int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink, return ret; } - led->dcdc_consumer.dev = &pdev->dev; + led->dcdc_consumer.dev_name = dev_name(&pdev->dev); led->dcdc_consumer.supply = "led_vcc"; led->dcdc_init.num_consumer_supplies = 1; led->dcdc_init.consumer_supplies = &led->dcdc_consumer; diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 706f39563a7..8477153780b 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -78,14 +78,14 @@ static int wm8400_ldo_set_voltage(struct regulator_dev *dev, if (min_uV < 1700000) { /* Steps of 50mV from 900mV; */ - val = (min_uV - 850001) / 50000; + val = DIV_ROUND_UP(min_uV - 900000, 50000); if ((val * 50000) + 900000 > max_uV) return -EINVAL; BUG_ON((val * 50000) + 900000 < min_uV); } else { /* Steps of 100mV from 1700mV */ - val = ((min_uV - 1600001) / 100000); + val = DIV_ROUND_UP(min_uV - 1700000, 100000); if ((val * 100000) + 1700000 > max_uV) return -EINVAL; @@ -168,7 +168,7 @@ static int wm8400_dcdc_set_voltage(struct regulator_dev *dev, if (min_uV < 850000) return -EINVAL; - val = (min_uV - 825001) / 25000; + val = DIV_ROUND_UP(min_uV - 850000, 25000); if (850000 + (25000 * val) > max_uV) return -EINVAL; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 435e335d6e6..75ed402d9f4 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -241,7 +241,7 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) if (!pdata) return -ENODEV; - ldo = kzalloc(sizeof(struct wm8994_ldo), GFP_KERNEL); + ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_ldo), GFP_KERNEL); if (ldo == NULL) { dev_err(&pdev->dev, "Unable to allocate private data\n"); return -ENOMEM; @@ -285,7 +285,6 @@ err_gpio: if (gpio_is_valid(ldo->enable)) gpio_free(ldo->enable); err: - kfree(ldo); return ret; } @@ -298,7 +297,6 @@ static __devexit int wm8994_ldo_remove(struct platform_device *pdev) regulator_unregister(ldo->regulator); if (gpio_is_valid(ldo->enable)) gpio_free(ldo->enable); - kfree(ldo); return 0; } |