From 8b385d9b97a63ba621342858f9921324032a9167 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Fri, 4 Jun 2010 11:46:04 +0800 Subject: regulator: new drivers for AD5398 and AD5821 The AD5398 and AD5821 are single 10-bit DAC with 120 mA output current sink capability. They feature an internal reference and operates from a single 2.7 V to 5.5 V supply. This driver supports both the AD5398 and the AD5821. It adapts into the voltage and current framework. Signed-off-by: Sonic Zhang Signed-off-by: Mike Frysinger Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 6 + drivers/regulator/Makefile | 1 + drivers/regulator/ad5398.c | 288 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 drivers/regulator/ad5398.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 04f2e085116a..679ea3759449 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -201,5 +201,11 @@ config REGULATOR_88PM8607 help This driver supports 88PM8607 voltage regulator chips. +config REGULATOR_AD5398 + tristate "Analog Devices AD5398/AD5821 regulators" + depends on I2C + help + This driver supports AD5398 and AD5821 current regulator chips. + If building into module, its name is ad5398.ko. endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 4e7feece22d5..c256668300a6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o +obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c new file mode 100644 index 000000000000..6de6bdaeff7c --- /dev/null +++ b/drivers/regulator/ad5398.c @@ -0,0 +1,288 @@ +/* + * Voltage and current regulation for AD5398 and AD5821 + * + * Copyright 2010 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define AD5398_CURRENT_EN_MASK 0x8000 + +struct ad5398_chip_info { + struct i2c_client *client; + int min_uA; + int max_uA; + unsigned int current_level; + unsigned int current_mask; + unsigned int current_offset; + struct regulator_dev rdev; +}; + +static int ad5398_calc_current(struct ad5398_chip_info *chip, + unsigned selector) +{ + unsigned range_uA = chip->max_uA - chip->min_uA; + + return chip->min_uA + (selector * range_uA / chip->current_level); +} + +static int ad5398_read_reg(struct i2c_client *client, unsigned short *data) +{ + unsigned short val; + int ret; + + ret = i2c_master_recv(client, (char *)&val, 2); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + *data = be16_to_cpu(val); + + return ret; +} + +static int ad5398_write_reg(struct i2c_client *client, const unsigned short data) +{ + unsigned short val; + int ret; + + val = cpu_to_be16(data); + ret = i2c_master_send(client, (char *)&val, 2); + if (ret < 0) + dev_err(&client->dev, "I2C write error\n"); + + return ret; +} + +static int ad5398_get_current_limit(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + ret = (data & chip->current_mask) >> chip->current_offset; + + return ad5398_calc_current(chip, ret); +} + +static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned range_uA = chip->max_uA - chip->min_uA; + unsigned selector; + unsigned short data; + int ret; + + if (min_uA > chip->max_uA || min_uA < chip->min_uA) + return -EINVAL; + 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; + if (ad5398_calc_current(chip, selector) > max_uA) + return -EINVAL; + + dev_dbg(&client->dev, "changing current %dmA\n", + ad5398_calc_current(chip, selector) / 1000); + + /* read chip enable bit */ + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + /* prepare register data */ + selector = (selector << chip->current_offset) & chip->current_mask; + data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK); + + /* write the new current value back as well as enable bit */ + ret = ad5398_write_reg(client, data); + + return ret; +} + +static int ad5398_is_enabled(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (data & AD5398_CURRENT_EN_MASK) + return 1; + else + return 0; +} + +static int ad5398_enable(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (data & AD5398_CURRENT_EN_MASK) + return 0; + + data |= AD5398_CURRENT_EN_MASK; + + ret = ad5398_write_reg(client, data); + + return ret; +} + +static int ad5398_disable(struct regulator_dev *rdev) +{ + struct ad5398_chip_info *chip = rdev_get_drvdata(rdev); + struct i2c_client *client = chip->client; + unsigned short data; + int ret; + + ret = ad5398_read_reg(client, &data); + if (ret < 0) + return ret; + + if (!(data & AD5398_CURRENT_EN_MASK)) + return 0; + + data &= ~AD5398_CURRENT_EN_MASK; + + ret = ad5398_write_reg(client, data); + + return ret; +} + +static struct regulator_ops ad5398_ops = { + .get_current_limit = ad5398_get_current_limit, + .set_current_limit = ad5398_set_current_limit, + .enable = ad5398_enable, + .disable = ad5398_disable, + .is_enabled = ad5398_is_enabled, +}; + +static struct regulator_desc ad5398_reg = { + .name = "isink", + .id = 0, + .ops = &ad5398_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, +}; + +struct ad5398_current_data_format { + int current_bits; + int current_offset; + int min_uA; + int max_uA; +}; + +static const struct ad5398_current_data_format df_10_4_120 = {10, 4, 0, 120000}; + +static const struct i2c_device_id ad5398_id[] = { + { "ad5398", (kernel_ulong_t)&df_10_4_120 }, + { "ad5821", (kernel_ulong_t)&df_10_4_120 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad5398_id); + +static int __devinit ad5398_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regulator_dev *rdev; + struct regulator_init_data *init_data = client->dev.platform_data; + struct ad5398_chip_info *chip; + const struct ad5398_current_data_format *df = + (struct ad5398_current_data_format *)id->driver_data; + int ret; + + if (!init_data) + return -EINVAL; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + + chip->min_uA = df->min_uA; + chip->max_uA = df->max_uA; + chip->current_level = 1 << df->current_bits; + chip->current_offset = df->current_offset; + chip->current_mask = (chip->current_level - 1) << chip->current_offset; + + rdev = regulator_register(&ad5398_reg, &client->dev, init_data, chip); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&client->dev, "failed to register %s %s\n", + id->name, ad5398_reg.name); + goto err; + } + + i2c_set_clientdata(client, chip); + dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name); + return 0; + +err: + kfree(chip); + return ret; +} + +static int __devexit ad5398_remove(struct i2c_client *client) +{ + struct ad5398_chip_info *chip = i2c_get_clientdata(client); + + regulator_unregister(&chip->rdev); + kfree(chip); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static struct i2c_driver ad5398_driver = { + .probe = ad5398_probe, + .remove = __devexit_p(ad5398_remove), + .driver = { + .name = "ad5398", + }, + .id_table = ad5398_id, +}; + +static int __init ad5398_init(void) +{ + return i2c_add_driver(&ad5398_driver); +} +module_init(ad5398_init); + +static void __exit ad5398_exit(void) +{ + i2c_del_driver(&ad5398_driver); +} +module_exit(ad5398_exit); + +MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver"); +MODULE_AUTHOR("Sonic Zhang"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("i2c:ad5398-regulator"); -- cgit v1.2.3 From 839b8362a78e02d123f6ac586349b357136a733f Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Thu, 10 Jun 2010 16:50:20 +0800 Subject: regulator: make sure the regulator is available prior to the consumers. Some systems are likely to want this to be subsys_initcall() to make sure the regulator is available prior to the consumers. Signed-off-by: Sonic Zhang Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/ad5398.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 6de6bdaeff7c..d59d2f2314af 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -274,7 +274,7 @@ static int __init ad5398_init(void) { return i2c_add_driver(&ad5398_driver); } -module_init(ad5398_init); +subsys_initcall(ad5398_init); static void __exit ad5398_exit(void) { -- cgit v1.2.3 From 51bd6943cd760a55f655830b3f7dca8a06b54c73 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Jun 2010 17:25:51 +0200 Subject: ISL6271A voltage regulator support. This device is very simple, it supports one buck and two LDOs. The LDOs are fixed-voltage. Only the buck is programable over the I2C bus to 16 possible voltages. Signed-off-by: Marek Vasut Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 6 + drivers/regulator/Makefile | 1 + drivers/regulator/isl6271a-regulator.c | 236 +++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/regulator/isl6271a-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 679ea3759449..6bf56c93a336 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -201,6 +201,12 @@ config REGULATOR_88PM8607 help This driver supports 88PM8607 voltage regulator chips. +config REGULATOR_ISL6271A + tristate "Intersil ISL6271A Power regulator" + depends on I2C + help + This driver supports ISL6271A voltage regulator chip. + config REGULATOR_AD5398 tristate "Analog Devices AD5398/AD5821 regulators" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c256668300a6..451a14a873f6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -32,5 +32,6 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o +obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c new file mode 100644 index 000000000000..e49d2bd393f2 --- /dev/null +++ b/drivers/regulator/isl6271a-regulator.c @@ -0,0 +1,236 @@ +/* + * isl6271a-regulator.c + * + * Support for Intersil ISL6271A voltage regulator + * + * Copyright (C) 2010 Marek Vasut + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISL6271A_VOLTAGE_MIN 850000 +#define ISL6271A_VOLTAGE_MAX 1600000 +#define ISL6271A_VOLTAGE_STEP 50000 + +/* PMIC details */ +struct isl_pmic { + struct i2c_client *client; + struct regulator_dev *rdev[3]; + struct mutex mtx; +}; + +static int isl6271a_get_voltage(struct regulator_dev *dev) +{ + struct isl_pmic *pmic = rdev_get_drvdata(dev); + int idx, data; + + mutex_lock(&pmic->mtx); + + idx = i2c_smbus_read_byte(pmic->client); + if (idx < 0) { + dev_err(&pmic->client->dev, "Error getting voltage\n"); + data = idx; + goto out; + } + + /* Convert the data from chip to microvolts */ + data = ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * (idx & 0xf)); + +out: + mutex_unlock(&pmic->mtx); + return data; +} + +static int isl6271a_set_voltage(struct regulator_dev *dev, int minuV, int maxuV) +{ + struct isl_pmic *pmic = rdev_get_drvdata(dev); + int vsel, 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; + + mutex_lock(&pmic->mtx); + + err = i2c_smbus_write_byte(pmic->client, data); + if (err < 0) + dev_err(&pmic->client->dev, "Error setting voltage\n"); + + mutex_unlock(&pmic->mtx); + return err; +} + +static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector) +{ + return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector); +} + +static struct regulator_ops isl_core_ops = { + .get_voltage = isl6271a_get_voltage, + .set_voltage = isl6271a_set_voltage, + .list_voltage = isl6271a_list_voltage, +}; + +static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) +{ + int id = rdev_get_id(dev); + return (id == 1) ? 1100000 : 1300000; +} + +static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) +{ + int id = rdev_get_id(dev); + return (id == 1) ? 1100000 : 1300000; +} + +static struct regulator_ops isl_fixed_ops = { + .get_voltage = isl6271a_get_fixed_voltage, + .list_voltage = isl6271a_list_fixed_voltage, +}; + +static struct regulator_desc isl_rd[] = { + { + .name = "Core Buck", + .id = 0, + .n_voltages = 16, + .ops = &isl_core_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO1", + .id = 1, + .n_voltages = 1, + .ops = &isl_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "LDO2", + .id = 2, + .n_voltages = 1, + .ops = &isl_fixed_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int __devinit isl6271a_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regulator_init_data *init_data = i2c->dev.platform_data; + struct isl_pmic *pmic; + int err, i; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + if (!init_data) { + dev_err(&i2c->dev, "no platform data supplied\n"); + return -EIO; + } + + pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + pmic->client = i2c; + + mutex_init(&pmic->mtx); + + for (i = 0; i < 3; i++) { + pmic->rdev[i] = regulator_register(&isl_rd[0], &i2c->dev, + init_data, pmic); + if (IS_ERR(pmic->rdev[i])) { + dev_err(&i2c->dev, "failed to register %s\n", id->name); + err = PTR_ERR(pmic->rdev); + goto error; + } + } + + i2c_set_clientdata(i2c, pmic); + + return 0; + +error: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return err; +} + +static int __devexit isl6271a_remove(struct i2c_client *i2c) +{ + struct isl_pmic *pmic = i2c_get_clientdata(i2c); + int i; + + i2c_set_clientdata(i2c, NULL); + + for (i = 0; i < 3; i++) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + + return 0; +} + +static const struct i2c_device_id isl6271a_id[] = { + {.name = "isl6271a", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, isl6271a_id); + +static struct i2c_driver isl6271a_i2c_driver = { + .driver = { + .name = "isl6271a", + .owner = THIS_MODULE, + }, + .probe = isl6271a_probe, + .remove = __devexit_p(isl6271a_remove), + .id_table = isl6271a_id, +}; + +static int __init isl6271a_init(void) +{ + return i2c_add_driver(&isl6271a_i2c_driver); +} + +static void __exit isl6271a_cleanup(void) +{ + i2c_del_driver(&isl6271a_i2c_driver); +} + +subsys_initcall(isl6271a_init); +module_exit(isl6271a_cleanup); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 156f252857dfd81f03d77d09e33b5f7d2b113e2b Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 16 Jun 2010 09:04:16 +0200 Subject: drivers: regulator: add Maxim 8998 driver Acked-by: Mark Brown This patch adds voltage regulator driver for Maxim 8998 chip. This chip is used on Samsung Aquila and GONI boards and provides following functionalities: - 4 BUCK voltage converters, 17 LDO power regulators and 5 other power controllers - battery charger This patch adds basic driver for voltage regulators and MAX 8998 MFD core. Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Acked-by: Samuel Ortiz Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/max8998.c | 160 ++++++++++ drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/max8998.c | 610 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/max8998-private.h | 112 +++++++ include/linux/mfd/max8998.h | 78 +++++ 8 files changed, 981 insertions(+) create mode 100644 drivers/mfd/max8998.c create mode 100644 drivers/regulator/max8998.c create mode 100644 include/linux/mfd/max8998-private.h create mode 100644 include/linux/mfd/max8998.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9da0e504bbe9..ad61a9e8e04e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -252,6 +252,16 @@ config MFD_MAX8925 accessing the device, additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX8998 + bool "Maxim Semiconductor MAX8998 PMIC Support" + depends on I2C=y + select MFD_CORE + help + Say yes here to support for Maxim Semiconductor MAX8998. This is + a Power Management IC. This driver provies common support for + accessing the device, additional drivers must be enabled in order + to use the functionality of the device. + config MFD_WM8400 tristate "Support Wolfson Microelectronics WM8400" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index fb503e77dc60..a362ccfe8997 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o +obj-$(CONFIG_MFD_MAX8998) += max8998.o pcf50633-objs := pcf50633-core.o pcf50633-irq.o obj-$(CONFIG_MFD_PCF50633) += pcf50633.o diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c new file mode 100644 index 000000000000..0d68de21ea9e --- /dev/null +++ b/drivers/mfd/max8998.c @@ -0,0 +1,160 @@ +/* + * max8698.c - mfd core driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electronics + * Kyungmin Park + * Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mfd_cell max8998_devs[] = { + { + .name = "max8998-pmic", + } +}; + +static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest) +{ + struct i2c_client *client = max8998->i2c_client; + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_read_byte_data(client, reg); + mutex_unlock(&max8998->iolock); + if (ret < 0) + return ret; + + ret &= 0xff; + *dest = ret; + return 0; +} + +static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value) +{ + struct i2c_client *client = max8998->i2c_client; + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_write_byte_data(client, reg, value); + mutex_unlock(&max8998->iolock); + return ret; +} + +static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg, + u8 val, u8 mask) +{ + struct i2c_client *client = max8998->i2c_client; + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + u8 old_val = ret & 0xff; + u8 new_val = (val & mask) | (old_val & (~mask)); + ret = i2c_smbus_write_byte_data(client, reg, new_val); + if (ret >= 0) + ret = 0; + } + mutex_unlock(&max8998->iolock); + return ret; +} + +static int max8998_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max8998_dev *max8998; + int ret = 0; + + max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL); + if (max8998 == NULL) { + kfree(i2c); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, max8998); + max8998->dev = &i2c->dev; + max8998->i2c_client = i2c; + max8998->dev_read = max8998_i2c_device_read; + max8998->dev_write = max8998_i2c_device_write; + max8998->dev_update = max8998_i2c_device_update; + mutex_init(&max8998->iolock); + + ret = mfd_add_devices(max8998->dev, -1, + max8998_devs, ARRAY_SIZE(max8998_devs), + NULL, 0); + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(max8998->dev); + kfree(max8998); + return ret; +} + +static int max8998_i2c_remove(struct i2c_client *i2c) +{ + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max8998->dev); + kfree(max8998); + + return 0; +} + +static const struct i2c_device_id max8998_i2c_id[] = { + { "max8998", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); + +static struct i2c_driver max8998_i2c_driver = { + .driver = { + .name = "max8998", + .owner = THIS_MODULE, + }, + .probe = max8998_i2c_probe, + .remove = max8998_i2c_remove, + .id_table = max8998_i2c_id, +}; + +static int __init max8998_i2c_init(void) +{ + return i2c_add_driver(&max8998_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max8998_i2c_init); + +static void __exit max8998_i2c_exit(void) +{ + i2c_del_driver(&max8998_i2c_driver); +} +module_exit(max8998_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 8998 multi-function core driver"); +MODULE_AUTHOR("Kyungmin Park "); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6bf56c93a336..671b81a39482 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -100,6 +100,15 @@ config REGULATOR_MAX8925 help Say y here to support the voltage regulaltor of Maxim MAX8925 PMIC. +config REGULATOR_MAX8998 + tristate "Maxim 8998 voltage regulator" + depends on I2C + default n + help + This driver controls a Maxim 8998 voltage output regulator + 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/TPS695x0 PMIC" depends on TWL4030_CORE diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 451a14a873f6..74a4638bd9a6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -17,6 +17,7 @@ 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_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c new file mode 100644 index 000000000000..174fd1957ae4 --- /dev/null +++ b/drivers/regulator/max8998.c @@ -0,0 +1,610 @@ +/* + * max8998.c - Voltage regulator driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electronics + * Kyungmin Park + * Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct max8998_data { + struct device *dev; + struct max8998_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; +}; + +struct voltage_map_desc { + int min; + int max; + int step; +}; + +/* Voltage maps */ +static const struct voltage_map_desc ldo23_voltage_map_desc = { + .min = 800, .step = 50, .max = 1300, +}; +static const struct voltage_map_desc ldo456711_voltage_map_desc = { + .min = 1600, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc ldo8_voltage_map_desc = { + .min = 3000, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc ldo9_voltage_map_desc = { + .min = 2800, .step = 100, .max = 3100, +}; +static const struct voltage_map_desc ldo10_voltage_map_desc = { + .min = 950, .step = 50, .max = 1300, +}; +static const struct voltage_map_desc ldo1213_voltage_map_desc = { + .min = 800, .step = 100, .max = 3300, +}; +static const struct voltage_map_desc ldo1415_voltage_map_desc = { + .min = 1200, .step = 100, .max = 3300, +}; +static const struct voltage_map_desc ldo1617_voltage_map_desc = { + .min = 1600, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc buck12_voltage_map_desc = { + .min = 750, .step = 25, .max = 1525, +}; +static const struct voltage_map_desc buck3_voltage_map_desc = { + .min = 1600, .step = 100, .max = 3600, +}; +static const struct voltage_map_desc buck4_voltage_map_desc = { + .min = 800, .step = 100, .max = 2300, +}; + +static const struct voltage_map_desc *ldo_voltage_map[] = { + NULL, + NULL, + &ldo23_voltage_map_desc, /* LDO2 */ + &ldo23_voltage_map_desc, /* LDO3 */ + &ldo456711_voltage_map_desc, /* LDO4 */ + &ldo456711_voltage_map_desc, /* LDO5 */ + &ldo456711_voltage_map_desc, /* LDO6 */ + &ldo456711_voltage_map_desc, /* LDO7 */ + &ldo8_voltage_map_desc, /* LDO8 */ + &ldo9_voltage_map_desc, /* LDO9 */ + &ldo10_voltage_map_desc, /* LDO10 */ + &ldo456711_voltage_map_desc, /* LDO11 */ + &ldo1213_voltage_map_desc, /* LDO12 */ + &ldo1213_voltage_map_desc, /* LDO13 */ + &ldo1415_voltage_map_desc, /* LDO14 */ + &ldo1415_voltage_map_desc, /* LDO15 */ + &ldo1617_voltage_map_desc, /* LDO16 */ + &ldo1617_voltage_map_desc, /* LDO17 */ + &buck12_voltage_map_desc, /* BUCK1 */ + &buck12_voltage_map_desc, /* BUCK2 */ + &buck3_voltage_map_desc, /* BUCK3 */ + &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 val; + + if (ldo > ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + + desc = ldo_voltage_map[ldo]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val * 1000; +} + +static int max8998_get_enable_register(struct regulator_dev *rdev, + int *reg, int *shift) +{ + int ldo = max8998_get_ldo(rdev); + + switch (ldo) { + case MAX8998_LDO2 ... MAX8998_LDO5: + *reg = MAX8998_REG_ONOFF1; + *shift = 3 - (ldo - MAX8998_LDO2); + break; + case MAX8998_LDO6 ... MAX8998_LDO13: + *reg = MAX8998_REG_ONOFF2; + *shift = 7 - (ldo - MAX8998_LDO6); + break; + case MAX8998_LDO14 ... MAX8998_LDO17: + *reg = MAX8998_REG_ONOFF3; + *shift = 7 - (ldo - MAX8998_LDO14); + break; + case MAX8998_BUCK1 ... MAX8998_BUCK4: + *reg = MAX8998_REG_ONOFF1; + *shift = 7 - (ldo - MAX8998_BUCK1); + break; + case MAX8998_EN32KHZ_AP ... MAX8998_ENVICHG: + *reg = MAX8998_REG_ONOFF4; + *shift = 7 - (ldo - MAX8998_EN32KHZ_AP); + break; + case MAX8998_ESAFEOUT1 ... MAX8998_ESAFEOUT2: + *reg = MAX8998_REG_CHGR2; + *shift = 7 - (ldo - MAX8998_ESAFEOUT1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max8998_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int ret, reg, shift = 8; + u8 val; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + ret = max8998_read_reg(max8998->iodev, reg, &val); + if (ret) + return ret; + + return val & (1 << shift); +} + +static int max8998_ldo_enable(struct regulator_dev *rdev) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int reg, shift = 8, ret; + + ret = max8998_get_enable_register(rdev, ®, &shift); + if (ret) + return ret; + + return max8998_update_reg(max8998->iodev, reg, 1<iodev, reg, 0, 1<iodev, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + return max8998_list_voltage(rdev, val); +} + +static int max8998_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct max8998_data *max8998 = rdev_get_drvdata(rdev); + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + const struct voltage_map_desc *desc; + int ldo = max8998_get_ldo(rdev); + int reg, shift = 0, mask, ret; + int i = 0; + + if (ldo > ARRAY_SIZE(ldo_voltage_map)) + return -EINVAL; + + desc = ldo_voltage_map[ldo]; + if (desc == NULL) + return -EINVAL; + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + while (desc->min + desc->step*i < max_vol && + desc->min + desc->step*i < desc->max) + i++; + + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + return max8998_update_reg(max8998->iodev, reg, i<dev.parent); + struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max8998_data *max8998; + int i, ret, size; + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied\n"); + return -ENODEV; + } + + max8998 = kzalloc(sizeof(struct max8998_data), GFP_KERNEL); + if (!max8998) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * (pdata->num_regulators + 1); + max8998->rdev = kzalloc(size, GFP_KERNEL); + if (!max8998->rdev) { + kfree(max8998); + return -ENOMEM; + } + + rdev = max8998->rdev; + max8998->iodev = iodev; + platform_set_drvdata(pdev, max8998); + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_desc *desc; + int id = pdata->regulators[i].id; + int index = id - MAX8998_LDO2; + + desc = ldo_voltage_map[id]; + if (desc && regulators[index].ops != &max8998_others_ops) { + int count = (desc->max - desc->min) / desc->step + 1; + regulators[index].n_voltages = count; + } + rdev[i] = regulator_register(®ulators[index], max8998->dev, + pdata->regulators[i].initdata, max8998); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max8998->dev, "regulator init failed\n"); + rdev[i] = NULL; + goto err; + } + } + + + return 0; +err: + for (i = 0; i <= max8998->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8998->rdev); + kfree(max8998); + + return ret; +} + +static int __devexit max8998_pmic_remove(struct platform_device *pdev) +{ + struct max8998_data *max8998 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max8998->rdev; + int i; + + for (i = 0; i <= max8998->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8998->rdev); + kfree(max8998); + + return 0; +} + +static struct platform_driver max8998_pmic_driver = { + .driver = { + .name = "max8998-pmic", + .owner = THIS_MODULE, + }, + .probe = max8998_pmic_probe, + .remove = __devexit_p(max8998_pmic_remove), +}; + +static int __init max8998_pmic_init(void) +{ + return platform_driver_register(&max8998_pmic_driver); +} +subsys_initcall(max8998_pmic_init); + +static void __exit max8998_pmic_cleanup(void) +{ + platform_driver_unregister(&max8998_pmic_driver); +} +module_exit(max8998_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 8998 voltage regulator driver"); +MODULE_AUTHOR("Kyungmin Park "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h new file mode 100644 index 000000000000..6dc75b3e2d33 --- /dev/null +++ b/include/linux/mfd/max8998-private.h @@ -0,0 +1,112 @@ +/* + * max8698.h - Voltage regulator driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electrnoics + * Kyungmin Park + * Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_MFD_MAX8998_PRIV_H +#define __LINUX_MFD_MAX8998_PRIV_H + +/* MAX 8998 registers */ +enum { + MAX8998_REG_IRQ1, + MAX8998_REG_IRQ2, + MAX8998_REG_IRQ3, + MAX8998_REG_IRQ4, + MAX8998_REG_IRQM1, + MAX8998_REG_IRQM2, + MAX8998_REG_IRQM3, + MAX8998_REG_IRQM4, + MAX8998_REG_STATUS1, + MAX8998_REG_STATUS2, + MAX8998_REG_STATUSM1, + MAX8998_REG_STATUSM2, + MAX8998_REG_CHGR1, + MAX8998_REG_CHGR2, + MAX8998_REG_LDO_ACTIVE_DISCHARGE1, + MAX8998_REG_LDO_ACTIVE_DISCHARGE2, + MAX8998_REG_BUCK_ACTIVE_DISCHARGE3, + MAX8998_REG_ONOFF1, + MAX8998_REG_ONOFF2, + MAX8998_REG_ONOFF3, + MAX8998_REG_ONOFF4, + MAX8998_REG_BUCK1_DVSARM1, + MAX8998_REG_BUCK1_DVSARM2, + MAX8998_REG_BUCK1_DVSARM3, + MAX8998_REG_BUCK1_DVSARM4, + MAX8998_REG_BUCK2_DVSINT1, + MAX8998_REG_BUCK2_DVSINT2, + MAX8998_REG_BUCK3, + MAX8998_REG_BUCK4, + MAX8998_REG_LDO2_LDO3, + MAX8998_REG_LDO4, + MAX8998_REG_LDO5, + MAX8998_REG_LDO6, + MAX8998_REG_LDO7, + MAX8998_REG_LDO8_LDO9, + MAX8998_REG_LDO10_LDO11, + MAX8998_REG_LDO12, + MAX8998_REG_LDO13, + MAX8998_REG_LDO14, + MAX8998_REG_LDO15, + MAX8998_REG_LDO16, + MAX8998_REG_LDO17, + MAX8998_REG_BKCHR, + MAX8998_REG_LBCNFG1, + MAX8998_REG_LBCNFG2, +}; + +/** + * struct max8998_dev - max8998 master device for sub-drivers + * @dev: master device of the chip (can be used to access platform data) + * @i2c_client: i2c client private data + * @dev_read(): chip register read function + * @dev_write(): chip register write function + * @dev_update(): chip register update function + * @iolock: mutex for serializing io access + */ + +struct max8998_dev { + struct device *dev; + struct i2c_client *i2c_client; + int (*dev_read)(struct max8998_dev *max8998, u8 reg, u8 *dest); + int (*dev_write)(struct max8998_dev *max8998, u8 reg, u8 val); + int (*dev_update)(struct max8998_dev *max8998, u8 reg, u8 val, u8 mask); + struct mutex iolock; +}; + +static inline int max8998_read_reg(struct max8998_dev *max8998, u8 reg, + u8 *value) +{ + return max8998->dev_read(max8998, reg, value); +} + +static inline int max8998_write_reg(struct max8998_dev *max8998, u8 reg, + u8 value) +{ + return max8998->dev_write(max8998, reg, value); +} + +static inline int max8998_update_reg(struct max8998_dev *max8998, u8 reg, + u8 value, u8 mask) +{ + return max8998->dev_update(max8998, reg, value, mask); +} + +#endif /* __LINUX_MFD_MAX8998_PRIV_H */ diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h new file mode 100644 index 000000000000..1d3601a2d853 --- /dev/null +++ b/include/linux/mfd/max8998.h @@ -0,0 +1,78 @@ +/* + * max8698.h - Voltage regulator driver for the Maxim 8998 + * + * Copyright (C) 2009-2010 Samsung Electrnoics + * Kyungmin Park + * Marek Szyprowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_MFD_MAX8998_H +#define __LINUX_MFD_MAX8998_H + +#include + +/* MAX 8998 regulator ids */ +enum { + MAX8998_LDO2 = 2, + MAX8998_LDO3, + MAX8998_LDO4, + MAX8998_LDO5, + MAX8998_LDO6, + MAX8998_LDO7, + MAX8998_LDO8, + MAX8998_LDO9, + MAX8998_LDO10, + MAX8998_LDO11, + MAX8998_LDO12, + MAX8998_LDO13, + MAX8998_LDO14, + MAX8998_LDO15, + MAX8998_LDO16, + MAX8998_LDO17, + MAX8998_BUCK1, + MAX8998_BUCK2, + MAX8998_BUCK3, + MAX8998_BUCK4, + MAX8998_EN32KHZ_AP, + MAX8998_EN32KHZ_CP, + MAX8998_ENVICHG, + MAX8998_ESAFEOUT1, + MAX8998_ESAFEOUT2, +}; + +/** + * max8998_regulator_data - regulator data + * @id: regulator id + * @initdata: regulator init data (contraints, supplies, ...) + */ +struct max8998_regulator_data { + int id; + struct regulator_init_data *initdata; +}; + +/** + * struct max8998_board - packages regulator init data + * @num_regulators: number of regultors used + * @regulators: array of defined regulators + */ + +struct max8998_platform_data { + int num_regulators; + struct max8998_regulator_data *regulators; +}; + +#endif /* __LINUX_MFD_MAX8998_H */ -- cgit v1.2.3 From 69f710083a5c49467ce3f76631d201771ae59539 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 21 Jun 2010 10:04:23 +0200 Subject: drivers: regulators: depend on MFD_MAX8998 The MAX8998-pmic driver requires a core driver for all io operations. Like other regulator driver make it depend of the required MFD io driver. Signed-off-by: Marek Szyprowski Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 671b81a39482..7cd8a2976e65 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -102,8 +102,7 @@ config REGULATOR_MAX8925 config REGULATOR_MAX8998 tristate "Maxim 8998 voltage regulator" - depends on I2C - default n + depends on MFD_MAX8998 help This driver controls a Maxim 8998 voltage output regulator via I2C bus. The provided regulator is suitable for S3C6410 -- cgit v1.2.3 From 1880a2fc59d2afabc142a311497cf7c320d64506 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 24 Jun 2010 15:49:49 +0200 Subject: tps65023: Allow registering similar TPS65021 TPS65021 is an older model and is slightly weaker (can supply less current), otherwise is the same. Signed-off-by: Marek Vasut Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index f50afc9f287a..cd6d4fc9d74f 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -585,6 +585,8 @@ static const struct tps_info tps65023_regs[] = { static const struct i2c_device_id tps_65023_id[] = { {.name = "tps65023", .driver_data = (unsigned long) tps65023_regs,}, + {.name = "tps65021", + .driver_data = (unsigned long) tps65023_regs,}, { }, }; -- cgit v1.2.3 From 549931f99e030d63a437c23943fd8dc9b7c0e41c Mon Sep 17 00:00:00 2001 From: Sundar R Iyer Date: Tue, 13 Jul 2010 11:51:28 +0530 Subject: ab8500-mfd: add regulator support to ab8500 mfd device Acked-by: Linus Walleij Acked-By: Mattias Wallin Acked-By: Bengt JONSSON Signed-off-by: Sundar R Iyer Acked-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/ab8500-core.c | 4 +++- include/linux/mfd/ab8500.h | 6 ++++++ include/linux/regulator/ab8500.h | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 include/linux/regulator/ab8500.h diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f3d26fa9c34d..defa786dee34 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -16,6 +16,7 @@ #include #include #include +#include /* * Interrupt register offsets @@ -352,6 +353,7 @@ static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-audio", }, { .name = "ab8500-usb", }, { .name = "ab8500-pwm", }, + { .name = "ab8500-regulator", }, }; int __devinit ab8500_init(struct ab8500 *ab8500) @@ -411,7 +413,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500) goto out_removeirq; } - ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs, + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, ARRAY_SIZE(ab8500_devs), NULL, ab8500->irq_base); if (ret) diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h index b63ff3ba3351..f5cec4500f38 100644 --- a/include/linux/mfd/ab8500.h +++ b/include/linux/mfd/ab8500.h @@ -76,6 +76,8 @@ #define AB8500_NR_IRQS 104 #define AB8500_NUM_IRQ_REGS 13 +#define AB8500_NUM_REGULATORS 15 + /** * struct ab8500 - ab8500 internal structure * @dev: parent device @@ -108,14 +110,18 @@ struct ab8500 { u8 oldmask[AB8500_NUM_IRQ_REGS]; }; +struct regulator_init_data; + /** * struct ab8500_platform_data - AB8500 platform data * @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used * @init: board-specific initialization after detection of ab8500 + * @regulator: machine-specific constraints for regulators */ struct ab8500_platform_data { int irq_base; void (*init) (struct ab8500 *); + struct regulator_init_data *regulator[AB8500_NUM_REGULATORS]; }; extern int ab8500_write(struct ab8500 *a8500, u16 addr, u8 data); diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h new file mode 100644 index 000000000000..f509877c2ed4 --- /dev/null +++ b/include/linux/regulator/ab8500.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * Author: Sundar Iyer for ST-Ericsson + * + */ + +#ifndef __LINUX_MFD_AB8500_REGULATOR_H +#define __LINUX_MFD_AB8500_REGULATOR_H + +/* AB8500 regulators */ +#define AB8500_LDO_AUX1 0 +#define AB8500_LDO_AUX2 1 +#define AB8500_LDO_AUX3 2 +#define AB8500_LDO_INTCORE 3 +#define AB8500_LDO_TVOUT 4 +#define AB8500_LDO_AUDIO 5 +#define AB8500_LDO_ANAMIC1 6 +#define AB8500_LDO_ANAMIC2 7 +#define AB8500_LDO_DMIC 8 +#define AB8500_LDO_ANA 9 + +#endif -- cgit v1.2.3 From c789ca202cae5adfab0208e7e8becbc602e5d079 Mon Sep 17 00:00:00 2001 From: Sundar R IYER Date: Tue, 13 Jul 2010 21:48:56 +0530 Subject: regulator: add support for regulators on the ab8500 MFD Hi Mark, > I think that's everything. Please find the updated patch set as below. >From f4bf7eec4d210db5075c0bce4521d9be6bc76c8c Mon Sep 17 00:00:00 2001 From: Sundar R Iyer Date: Sun, 6 Jun 2010 19:12:12 +0530 Subject: [PATCH v3 1/2] regulator: add support for regulators on the ab8500 MFD Acked-by: Linus Walleij Acked-by: Bengt JONSSON Signed-off-by: Sundar R Iyer Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/ab8500.c | 427 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 436 insertions(+) create mode 100644 drivers/regulator/ab8500.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7cd8a2976e65..6c14afd258b0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -221,5 +221,13 @@ config REGULATOR_AD5398 help This driver supports AD5398 and AD5821 current regulator chips. If building into module, its name is ad5398.ko. + +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 + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 74a4638bd9a6..fc696c50ee56 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -34,5 +34,6 @@ obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c new file mode 100644 index 000000000000..dc3f1a491675 --- /dev/null +++ b/drivers/regulator/ab8500.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * + * Author: Sundar Iyer for ST-Ericsson + * + * AB8500 peripheral regulators + * + * AB8500 supports the following regulators, + * LDOs - VAUDIO, VANAMIC2/2, VDIGMIC, VINTCORE12, VTVOUT, + * VAUX1/2/3, VANA + * + * for DB8500 cut 1.0 and previous versions of the silicon, all accesses + * to registers are through the DB8500 SPI. In cut 1.1 onwards, these + * accesses are through the DB8500 PRCMU I2C + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ab8500_regulator_info - ab8500 regulator information + * @desc: regulator description + * @ab8500: ab8500 parent + * @regulator_dev: regulator device + * @max_uV: maximum voltage (for variable voltage supplies) + * @min_uV: minimum voltage (for variable voltage supplies) + * @fixed_uV: typical voltage (for fixed voltage supplies) + * @update_reg: register to control on/off + * @mask: mask to enable/disable regulator + * @enable: bits to enable the regulator in normal(high power) mode + * @voltage_reg: register to control regulator voltage + * @voltage_mask: mask to control regulator voltage + * @supported_voltages: supported voltage table + * @voltages_len: number of supported voltages for the regulator + */ +struct ab8500_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct ab8500 *ab8500; + struct regulator_dev *regulator; + int max_uV; + int min_uV; + int fixed_uV; + int update_reg; + int mask; + int enable; + int voltage_reg; + int voltage_mask; + int const *supported_voltages; + int voltages_len; +}; + +/* voltage tables for the vauxn/vintcore supplies */ +static const int ldo_vauxn_voltages[] = { + 1100000, + 1200000, + 1300000, + 1400000, + 1500000, + 1800000, + 1850000, + 1900000, + 2500000, + 2650000, + 2700000, + 2750000, + 2800000, + 2900000, + 3000000, + 3300000, +}; + +static const int ldo_vintcore_voltages[] = { + 1200000, + 1225000, + 1250000, + 1275000, + 1300000, + 1325000, + 1350000, +}; + +static int ab8500_regulator_enable(struct regulator_dev *rdev) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_set_bits(info->ab8500, info->update_reg, + info->mask, info->enable); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set enable bits for regulator\n"); + return ret; +} + +static int ab8500_regulator_disable(struct regulator_dev *rdev) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_set_bits(info->ab8500, info->update_reg, + info->mask, 0x0); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set disable bits for regulator\n"); + return ret; +} + +static int ab8500_regulator_is_enabled(struct regulator_dev *rdev) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_read(info->ab8500, info->update_reg); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read 0x%x register\n", info->update_reg); + return ret; + } + + if (ret & info->mask) + return true; + else + return false; +} + +static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + int regulator_id; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + /* return the uV for the fixed regulators */ + if (info->fixed_uV) + return info->fixed_uV; + + if (selector > info->voltages_len) + return -EINVAL; + + return info->supported_voltages[selector]; +} + +static int ab8500_regulator_get_voltage(struct regulator_dev *rdev) +{ + int regulator_id, ret, val; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + ret = ab8500_read(info->ab8500, info->voltage_reg); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't read voltage reg for regulator\n"); + return ret; + } + + /* vintcore has a different layout */ + val = ret & info->voltage_mask; + if (regulator_id == AB8500_LDO_INTCORE) + ret = info->supported_voltages[val >> 0x3]; + else + ret = info->supported_voltages[val]; + + return ret; +} + +static int ab8500_get_best_voltage_index(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + int i; + + /* check the supported voltage */ + for (i = 0; i < info->voltages_len; i++) { + if ((info->supported_voltages[i] >= min_uV) && + (info->supported_voltages[i] <= max_uV)) + return i; + } + + return -EINVAL; +} + +static int ab8500_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int regulator_id, ret; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + /* get the appropriate voltages within the range */ + ret = ab8500_get_best_voltage_index(rdev, min_uV, max_uV); + if (ret < 0) { + dev_err(rdev_get_dev(rdev), + "couldn't get best voltage for regulator\n"); + return ret; + } + + /* set the registers for the request */ + ret = ab8500_set_bits(info->ab8500, info->voltage_reg, + info->voltage_mask, ret); + if (ret < 0) + dev_err(rdev_get_dev(rdev), + "couldn't set voltage reg for regulator\n"); + + return ret; +} + +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, + .set_voltage = ab8500_regulator_set_voltage, + .list_voltage = ab8500_list_voltage, +}; + +static int ab8500_fixed_get_voltage(struct regulator_dev *rdev) +{ + int regulator_id; + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= AB8500_NUM_REGULATORS) + return -EINVAL; + + return info->fixed_uV; +} + +static struct regulator_ops ab8500_ldo_fixed_ops = { + .enable = ab8500_regulator_enable, + .disable = ab8500_regulator_disable, + .is_enabled = ab8500_regulator_is_enabled, + .get_voltage = ab8500_fixed_get_voltage, + .list_voltage = ab8500_list_voltage, +}; + +#define AB8500_LDO(_id, min, max, reg, reg_mask, reg_enable, \ + volt_reg, volt_mask, voltages, \ + len_volts) \ +{ \ + .desc = { \ + .name = "LDO-" #_id, \ + .ops = &ab8500_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = AB8500_LDO_##_id, \ + .owner = THIS_MODULE, \ + }, \ + .min_uV = (min) * 1000, \ + .max_uV = (max) * 1000, \ + .update_reg = reg, \ + .mask = reg_mask, \ + .enable = reg_enable, \ + .voltage_reg = volt_reg, \ + .voltage_mask = volt_mask, \ + .supported_voltages = voltages, \ + .voltages_len = len_volts, \ + .fixed_uV = 0, \ +} + +#define AB8500_FIXED_LDO(_id, fixed, reg, reg_mask, \ + reg_enable) \ +{ \ + .desc = { \ + .name = "LDO-" #_id, \ + .ops = &ab8500_ldo_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = AB8500_LDO_##_id, \ + .owner = THIS_MODULE, \ + }, \ + .fixed_uV = fixed * 1000, \ + .update_reg = reg, \ + .mask = reg_mask, \ + .enable = reg_enable, \ +} + +static struct ab8500_regulator_info ab8500_regulator_info[] = { + /* + * Variable Voltage LDOs + * name, min uV, max uV, ctrl reg, reg mask, enable mask, + * volt ctrl reg, volt ctrl mask, volt table, num supported volts + */ + AB8500_LDO(AUX1, 1100, 3300, 0x0409, 0x3, 0x1, 0x041f, 0xf, + ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), + AB8500_LDO(AUX2, 1100, 3300, 0x0409, 0xc, 0x4, 0x0420, 0xf, + ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), + AB8500_LDO(AUX3, 1100, 3300, 0x040a, 0x3, 0x1, 0x0421, 0xf, + ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), + AB8500_LDO(INTCORE, 1100, 3300, 0x0380, 0x4, 0x4, 0x0380, 0x38, + ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)), + + /* + * Fixed Voltage LDOs + * name, o/p uV, ctrl reg, enable, disable + */ + AB8500_FIXED_LDO(TVOUT, 2000, 0x0380, 0x2, 0x2), + AB8500_FIXED_LDO(AUDIO, 2000, 0x0383, 0x2, 0x2), + AB8500_FIXED_LDO(ANAMIC1, 2050, 0x0383, 0x4, 0x4), + AB8500_FIXED_LDO(ANAMIC2, 2050, 0x0383, 0x8, 0x8), + AB8500_FIXED_LDO(DMIC, 1800, 0x0383, 0x10, 0x10), + AB8500_FIXED_LDO(ANA, 1200, 0x0383, 0xc, 0x4), +}; + +static inline struct ab8500_regulator_info *find_regulator_info(int id) +{ + struct ab8500_regulator_info *info; + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { + info = &ab8500_regulator_info[i]; + if (info->desc.id == id) + return info; + } + return NULL; +} + +static __devinit int ab8500_regulator_probe(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev); + int i, err; + + if (!ab8500) { + dev_err(&pdev->dev, "null mfd parent\n"); + return -EINVAL; + } + + /* register all regulators */ + for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { + struct ab8500_regulator_info *info = NULL; + + /* assign per-regulator data */ + info = &ab8500_regulator_info[i]; + info->dev = &pdev->dev; + info->ab8500 = ab8500; + + info->regulator = regulator_register(&info->desc, &pdev->dev, + pdata->regulator[i], info); + if (IS_ERR(info->regulator)) { + err = PTR_ERR(info->regulator); + dev_err(&pdev->dev, "failed to register regulator %s\n", + info->desc.name); + /* when we fail, un-register all earlier regulators */ + i--; + while (i > 0) { + info = &ab8500_regulator_info[i]; + regulator_unregister(info->regulator); + i--; + } + return err; + } + } + + return 0; +} + +static __devexit int ab8500_regulator_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { + struct ab8500_regulator_info *info = NULL; + info = &ab8500_regulator_info[i]; + regulator_unregister(info->regulator); + } + + return 0; +} + +static struct platform_driver ab8500_regulator_driver = { + .probe = ab8500_regulator_probe, + .remove = __devexit_p(ab8500_regulator_remove), + .driver = { + .name = "ab8500-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&ab8500_regulator_driver); + if (ret != 0) + pr_err("Failed to register ab8500 regulator: %d\n", ret); + + return ret; +} +subsys_initcall(ab8500_regulator_init); + +static void __exit ab8500_regulator_exit(void) +{ + platform_driver_unregister(&ab8500_regulator_driver); +} +module_exit(ab8500_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer "); +MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC"); +MODULE_ALIAS("platform:ab8500-regulator"); -- cgit v1.2.3 From c5a4655db9e0b5e49a0b17e1bf96462e93e3c326 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Tue, 20 Jul 2010 08:07:07 +0200 Subject: regulator: MAX8998: set_voltage bugfix. ramp_up delay and min/max voltage Two issues are addressed for max8998_set_voltage function. 1. Min/Max Voltage. max8998_set_voltage had been using the voltage value of min ( voltage[i] >= max_vol , i ) This is corrected to use: min ( voltage[i] >= min_vol , i ) 2. Ramp Up Delay. max8998_set_voltage should provide delay for BUCK1/2 if ENRAMP is on. It reads RAMP value from ONOFF4 register to determine RAMP delay length. However, when max8998_set_voltage's new voltage is lower than the previous, we don't care because it does not deteriorate the stability. Changes since v1: - rebased onto latest regulator-for-next tree Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max8998.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 174fd1957ae4..03e55a18a2ad 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -297,10 +298,13 @@ static int max8998_set_voltage(struct regulator_dev *rdev, { struct max8998_data *max8998 = rdev_get_drvdata(rdev); int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + int previous_vol = 0; const struct voltage_map_desc *desc; int ldo = max8998_get_ldo(rdev); int reg, shift = 0, mask, ret; int i = 0; + u8 val; + bool en_ramp = false; if (ldo > ARRAY_SIZE(ldo_voltage_map)) return -EINVAL; @@ -312,15 +316,36 @@ static int max8998_set_voltage(struct regulator_dev *rdev, if (max_vol < desc->min || min_vol > desc->max) return -EINVAL; - while (desc->min + desc->step*i < max_vol && + while (desc->min + desc->step*i < min_vol && desc->min + desc->step*i < desc->max) i++; + if (desc->min + desc->step*i > max_vol) + return -EINVAL; + ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); if (ret) return ret; - return max8998_update_reg(max8998->iodev, reg, i<iodev, MAX8998_REG_ONOFF4, &val); + if (val & (1 << 4)) { + en_ramp = true; + previous_vol = max8998_get_voltage(rdev); + } + } + + ret = max8998_update_reg(max8998->iodev, reg, i<min + desc->step*i - previous_vol/1000; + if (difference > 0) + udelay(difference / ((val & 0x0f) + 1)); + } + + return ret; } static struct regulator_ops max8998_ldo_ops = { -- cgit v1.2.3 From 49610235d58ac44617b4878eb731f65ec4e87aa2 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 27 Jul 2010 14:03:01 +0300 Subject: regulator: add TPS6586X regulator driver Signed-off-by: Mike Rapoport Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 6 + drivers/regulator/Makefile | 1 + drivers/regulator/tps6586x-regulator.c | 387 +++++++++++++++++++++++++++++++++ 3 files changed, 394 insertions(+) create mode 100644 drivers/regulator/tps6586x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6c14afd258b0..93bf5fe5660c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -229,5 +229,11 @@ config REGULATOR_AB8500 This driver supports the regulators found on the ST-Ericsson mixed signal AB8500 PMIC +config REGULATOR_TPS6586X + tristate "TI TPS6586X Power regulators" + depends on I2C + help + This driver supports TPS6586X voltage regulator chips. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index fc696c50ee56..8285fd832e16 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ 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_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c new file mode 100644 index 000000000000..76cc41ecbdf7 --- /dev/null +++ b/drivers/regulator/tps6586x-regulator.c @@ -0,0 +1,387 @@ +/* + * Regulator driver for TI TPS6586x + * + * Copyright (C) 2010 Compulab Ltd. + * Author: Mike Rapoport + * + * Based on da903x + * Copyright (C) 2006-2008 Marvell International Ltd. + * Copyright (C) 2008 Compulab Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include + +/* supply control and voltage setting */ +#define TPS6586X_SUPPLYENA 0x10 +#define TPS6586X_SUPPLYENB 0x11 +#define TPS6586X_SUPPLYENC 0x12 +#define TPS6586X_SUPPLYEND 0x13 +#define TPS6586X_SUPPLYENE 0x14 +#define TPS6586X_VCC1 0x20 +#define TPS6586X_VCC2 0x21 +#define TPS6586X_SM1V1 0x23 +#define TPS6586X_SM1V2 0x24 +#define TPS6586X_SM1SL 0x25 +#define TPS6586X_SM0V1 0x26 +#define TPS6586X_SM0V2 0x27 +#define TPS6586X_SM0SL 0x28 +#define TPS6586X_LDO2AV1 0x29 +#define TPS6586X_LDO2AV2 0x2A +#define TPS6586X_LDO2BV1 0x2F +#define TPS6586X_LDO2BV2 0x30 +#define TPS6586X_LDO4V1 0x32 +#define TPS6586X_LDO4V2 0x33 + +/* converter settings */ +#define TPS6586X_SUPPLYV1 0x41 +#define TPS6586X_SUPPLYV2 0x42 +#define TPS6586X_SUPPLYV3 0x43 +#define TPS6586X_SUPPLYV4 0x44 +#define TPS6586X_SUPPLYV5 0x45 +#define TPS6586X_SUPPLYV6 0x46 +#define TPS6586X_SMODE1 0x47 +#define TPS6586X_SMODE2 0x48 + +struct tps6586x_regulator { + struct regulator_desc desc; + + int volt_reg; + int volt_shift; + int volt_nbits; + int enable_bit[2]; + int enable_reg[2]; + + int *voltages; + + /* for DVM regulators */ + int go_reg; + int go_bit; +}; + +static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static int tps6586x_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct tps6586x_regulator *info = rdev_get_drvdata(rdev); + + return info->voltages[selector]; +} + + +static int __tps6586x_ldo_set_voltage(struct device *parent, + struct tps6586x_regulator *ri, + int min_uV, int max_uV) +{ + int val, uV; + uint8_t mask; + + for (val = 0; val < ri->desc.n_voltages; val++) { + uV = ri->voltages[val] * 1000; + + /* LDO0 has minimal voltage 1.2 rather than 1.25 */ + if (ri->desc.id == TPS6586X_ID_LDO_0 && val == 0) + uV -= 50 * 1000; + + /* use the first in-range value */ + if (min_uV <= uV && uV <= max_uV) { + + val <<= ri->volt_shift; + mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift; + + return tps6586x_update(parent, ri->volt_reg, val, mask); + } + } + + return -EINVAL; +} + +static int tps6586x_ldo_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + + return __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV); +} + +static int tps6586x_ldo_get_voltage(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + uint8_t val, mask; + int ret; + + ret = tps6586x_read(parent, ri->volt_reg, &val); + if (ret) + return ret; + + mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift; + val = (val & mask) >> ri->volt_shift; + + if (val > ri->desc.n_voltages) + BUG(); + + return ri->voltages[val]; +} + +static int tps6586x_dvm_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + int ret; + + ret = __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV); + if (ret) + return ret; + + return tps6586x_set_bits(parent, ri->go_reg, ri->go_bit); +} + +static int tps6586x_regulator_enable(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + + return tps6586x_set_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); +} + +static int tps6586x_regulator_disable(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + + return tps6586x_clr_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); +} + +static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct tps6586x_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps6586x_dev(rdev); + uint8_t reg_val; + int ret; + + ret = tps6586x_read(parent, ri->enable_reg[0], ®_val); + if (ret) + return ret; + + return !!(reg_val & (1 << ri->enable_bit[0])); +} + +static struct regulator_ops tps6586x_regulator_ldo_ops = { + .list_voltage = tps6586x_ldo_list_voltage, + .get_voltage = tps6586x_ldo_get_voltage, + .set_voltage = tps6586x_ldo_set_voltage, + + .is_enabled = tps6586x_regulator_is_enabled, + .enable = tps6586x_regulator_enable, + .disable = tps6586x_regulator_disable, +}; + +static struct regulator_ops tps6586x_regulator_dvm_ops = { + .list_voltage = tps6586x_ldo_list_voltage, + .get_voltage = tps6586x_ldo_get_voltage, + .set_voltage = tps6586x_dvm_set_voltage, + + .is_enabled = tps6586x_regulator_is_enabled, + .enable = tps6586x_regulator_enable, + .disable = tps6586x_regulator_disable, +}; + +static int tps6586x_ldo_voltages[] = { + 1250, 1500, 1800, 2500, 2700, 2850, 3100, 3300, +}; + +static int tps6586x_ldo4_voltages[] = { + 1700, 1725, 1750, 1775, 1800, 1825, 1850, 1875, + 1900, 1925, 1950, 1975, 2000, 2025, 2050, 2075, + 2100, 2125, 2150, 2175, 2200, 2225, 2250, 2275, + 2300, 2325, 2350, 2375, 2400, 2425, 2450, 2475, +}; + +static int tps6586x_dvm_voltages[] = { + 725, 750, 775, 800, 825, 850, 875, 900, + 925, 950, 975, 1000, 1025, 1050, 1075, 1100, + 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, + 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, +}; + +#define TPS6586X_REGULATOR(_id, vdata, _ops, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ +{ \ + .desc = { \ + .name = "REG-" #_id, \ + .ops = &tps6586x_regulator_##_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = TPS6586X_ID_##_id, \ + .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \ + .owner = THIS_MODULE, \ + }, \ + .volt_reg = TPS6586X_##vreg, \ + .volt_shift = (shift), \ + .volt_nbits = (nbits), \ + .enable_reg[0] = TPS6586X_SUPPLY##ereg0, \ + .enable_bit[0] = (ebit0), \ + .enable_reg[1] = TPS6586X_SUPPLY##ereg1, \ + .enable_bit[1] = (ebit1), \ + .voltages = tps6586x_##vdata##_voltages, \ +} + +#define TPS6586X_LDO(_id, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1) \ + TPS6586X_REGULATOR(_id, vdata, ldo_ops, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, 0, 0) + +#define TPS6586X_DVM(_id, vdata, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) \ + TPS6586X_REGULATOR(_id, vdata, dvm_ops, vreg, shift, nbits, \ + ereg0, ebit0, ereg1, ebit1, goreg, gobit) + +static struct tps6586x_regulator tps6586x_regulator[] = { + TPS6586X_LDO(LDO_0, ldo, SUPPLYV1, 5, 3, ENC, 0, END, 0), + TPS6586X_LDO(LDO_3, ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2), + TPS6586X_LDO(LDO_5, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6), + TPS6586X_LDO(LDO_6, ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4), + TPS6586X_LDO(LDO_7, ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5), + TPS6586X_LDO(LDO_8, ldo, SUPPLYV1, 5, 3, ENC, 6, END, 6), + TPS6586X_LDO(LDO_9, ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7), + TPS6586X_LDO(LDO_RTC, ldo, SUPPLYV4, 3, 3, ENE, 7, ENE, 7), + TPS6586X_LDO(LDO_1, dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1), + TPS6586X_LDO(SM_2, dvm, SUPPLYV2, 0, 5, ENC, 1, END, 1), + + TPS6586X_DVM(LDO_2, dvm, LDO2BV1, 0, 5, ENA, 3, ENB, 3, VCC2, 6), + TPS6586X_DVM(LDO_4, ldo4, LDO4V1, 0, 5, ENC, 3, END, 3, VCC1, 6), + TPS6586X_DVM(SM_0, dvm, SM0V1, 0, 5, ENA, 1, ENB, 1, VCC1, 2), + TPS6586X_DVM(SM_1, dvm, SM1V1, 0, 5, ENA, 0, ENB, 0, VCC1, 0), +}; + +/* + * TPS6586X has 2 enable bits that are OR'ed to determine the actual + * regulator state. Clearing one of this bits allows switching + * regulator on and of with single register write. + */ +static inline int tps6586x_regulator_preinit(struct device *parent, + struct tps6586x_regulator *ri) +{ + uint8_t val1, val2; + int ret; + + ret = tps6586x_read(parent, ri->enable_reg[0], &val1); + if (ret) + return ret; + + ret = tps6586x_read(parent, ri->enable_reg[1], &val2); + if (ret) + return ret; + + if (!(val2 & ri->enable_bit[1])) + return 0; + + /* + * The regulator is on, but it's enabled with the bit we don't + * want to use, so we switch the enable bits + */ + if (!(val1 & ri->enable_bit[0])) { + ret = tps6586x_set_bits(parent, ri->enable_reg[0], + 1 << ri->enable_bit[0]); + if (ret) + return ret; + } + + return tps6586x_clr_bits(parent, ri->enable_reg[1], + 1 << ri->enable_bit[1]); +} + +static inline struct tps6586x_regulator *find_regulator_info(int id) +{ + struct tps6586x_regulator *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(tps6586x_regulator); i++) { + ri = &tps6586x_regulator[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) +{ + struct tps6586x_regulator *ri = NULL; + struct regulator_dev *rdev; + int id = pdev->id; + int err; + + dev_dbg(&pdev->dev, "Probing reulator %d\n", id); + + ri = find_regulator_info(id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + err = tps6586x_regulator_preinit(pdev->dev.parent, ri); + if (err) + return err; + + rdev = regulator_register(&ri->desc, &pdev->dev, + pdev->dev.platform_data, ri); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, ri); + + return 0; +} + +static int __devexit tps6586x_regulator_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver tps6586x_regulator_driver = { + .driver = { + .name = "tps6586x-regulator", + .owner = THIS_MODULE, + }, + .probe = tps6586x_regulator_probe, + .remove = __devexit_p(tps6586x_regulator_remove), +}; + +static int __init tps6586x_regulator_init(void) +{ + return platform_driver_register(&tps6586x_regulator_driver); +} +subsys_initcall(tps6586x_regulator_init); + +static void __exit tps6586x_regulator_exit(void) +{ + platform_driver_unregister(&tps6586x_regulator_driver); +} +module_exit(tps6586x_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport "); +MODULE_DESCRIPTION("Regulator Driver for TI TPS6586X PMIC"); +MODULE_ALIAS("platform:tps6586x-regulator"); -- cgit v1.2.3 From 1b13f4bbcbe0d9797bcfd7c8bee7c077787313e1 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Wed, 28 Jul 2010 20:06:16 +0300 Subject: regulator: tps6586x: add dependancy on MFD_TPS6585x Signed-off-by: Mike Rapoport Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 93bf5fe5660c..172951bf23a4 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -231,7 +231,7 @@ config REGULATOR_AB8500 config REGULATOR_TPS6586X tristate "TI TPS6586X Power regulators" - depends on I2C + depends on MFD_TPS6586X help This driver supports TPS6586X voltage regulator chips. -- cgit v1.2.3 From 4cc2e3934bd74afbdff595ad2183e8aa25d0a0f2 Mon Sep 17 00:00:00 2001 From: Gary King Date: Fri, 30 Jul 2010 22:58:58 +0300 Subject: regulator: tps6586x: fix millivolt return values and SM2 table Hi Liam, Below are bugfixes for the tps6586x spotted by Gary King. Please add this patch to your regulator tree. Signed-off-by: Liam Girdwood --- drivers/regulator/tps6586x-regulator.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 76cc41ecbdf7..3c2eee8e7563 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -80,7 +80,7 @@ static int tps6586x_ldo_list_voltage(struct regulator_dev *rdev, { struct tps6586x_regulator *info = rdev_get_drvdata(rdev); - return info->voltages[selector]; + return info->voltages[selector] * 1000; } @@ -137,7 +137,7 @@ static int tps6586x_ldo_get_voltage(struct regulator_dev *rdev) if (val > ri->desc.n_voltages) BUG(); - return ri->voltages[val]; + return ri->voltages[val] * 1000; } static int tps6586x_dvm_set_voltage(struct regulator_dev *rdev, @@ -217,6 +217,13 @@ static int tps6586x_ldo4_voltages[] = { 2300, 2325, 2350, 2375, 2400, 2425, 2450, 2475, }; +static int tps6586x_sm2_voltages[] = { + 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350, + 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750, + 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150, + 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550, +}; + static int tps6586x_dvm_voltages[] = { 725, 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1075, 1100, @@ -265,7 +272,7 @@ static struct tps6586x_regulator tps6586x_regulator[] = { TPS6586X_LDO(LDO_9, ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7), TPS6586X_LDO(LDO_RTC, ldo, SUPPLYV4, 3, 3, ENE, 7, ENE, 7), TPS6586X_LDO(LDO_1, dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1), - TPS6586X_LDO(SM_2, dvm, SUPPLYV2, 0, 5, ENC, 1, END, 1), + TPS6586X_LDO(SM_2, sm2, SUPPLYV2, 0, 5, ENC, 1, END, 1), TPS6586X_DVM(LDO_2, dvm, LDO2BV1, 0, 5, ENA, 3, ENB, 3, VCC2, 6), TPS6586X_DVM(LDO_4, ldo4, LDO4V1, 0, 5, ENC, 3, END, 3, VCC1, 6), -- cgit v1.2.3 From 1b69d8e3fde65ab2b7c5e1d4adee59e8a67b0b4f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Aug 2010 14:34:10 +0800 Subject: max8998: fix off-by-one value range checking In max8998_list_voltage() and max8998_set_voltage(), we use ldo as array index of ldo_voltage_map. Thus the valid range should be 0 .. ARRAY_SIZE(ldo_voltage_map)-1. Signed-off-by: Axel Lin Acked-by: Kyungmin Park Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max8998.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 03e55a18a2ad..ab67298799f9 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -119,7 +119,7 @@ static int max8998_list_voltage(struct regulator_dev *rdev, int ldo = max8998_get_ldo(rdev); int val; - if (ldo > ARRAY_SIZE(ldo_voltage_map)) + if (ldo >= ARRAY_SIZE(ldo_voltage_map)) return -EINVAL; desc = ldo_voltage_map[ldo]; @@ -306,7 +306,7 @@ static int max8998_set_voltage(struct regulator_dev *rdev, u8 val; bool en_ramp = false; - if (ldo > ARRAY_SIZE(ldo_voltage_map)) + if (ldo >= ARRAY_SIZE(ldo_voltage_map)) return -EINVAL; desc = ldo_voltage_map[ldo]; -- cgit v1.2.3 From 27ef7f00c96e45192d5dcc7e4589376f3a2fded3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Aug 2010 08:09:14 +0800 Subject: regulator: lp3971 - remove unnecessary ret value checking in lp3971_i2c_write() We already check count value before calling i2c_smbus_read_byte_data(), no need to check it again. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/lp3971.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 8ae3732eb24b..5362289c7d69 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -377,7 +377,7 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count, if (count != 1) return -EIO; ret = i2c_smbus_read_byte_data(i2c, reg); - if (ret < 0 || count != 1) + if (ret < 0) return -EIO; *dest = ret; -- cgit v1.2.3 From c4604e49c1a5832a58789a22eba7ca982933e1be Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 6 Aug 2010 15:48:24 +0100 Subject: regulator: Default GPIO controlled WM8994 regulators to disabled This ensures that if the GPIO was not enabled prior to the driver starting the regulator API will insert the required powerup ramp delay when it enables the regulator. The gpiolib API does not provide this information. [Rewrote changelog to describe the actual change -- broonie.] Signed-off-by: Joonyoung Shim Signed-off-by: Mark Brown Cc: stable@kernel.org Signed-off-by: Liam Girdwood --- drivers/regulator/wm8994-regulator.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 5a1dc8a24d35..03713bc66e4a 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -219,8 +219,6 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) ldo->wm8994 = wm8994; - ldo->is_enabled = true; - if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) { ldo->enable = pdata->ldo[id].enable; @@ -237,7 +235,8 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) ret); goto err_gpio; } - } + } else + ldo->is_enabled = true; ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev, pdata->ldo[id].init_data, ldo); -- cgit v1.2.3 From 7c4c25e4bc37672e68d1c480a81fcb0efe6d2006 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Aug 2010 13:33:15 +0800 Subject: regulator: max1586 - fix a memory leak in max1586_pmic_remove() In max1586_pmic_probe(), we allocate memory for max1586. In max1586_pmic_remove(), current implementation only free rdev which is a member of struct max1586_data. Thus, there is a small memory leak when we unload the module. This patch fixes the memory leak by passing max1586 to i2c clientdata, and properly kfree(max1586) in max1586_pmic_remove(). Signed-off-by: Axel Lin Acked-by: Wolfram Sang Acked-by: Mark Brown Acked-by: Robert Jarzmik Signed-off-by: Liam Girdwood --- drivers/regulator/max1586.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 2b54d9d75f11..8867c2710a6d 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -223,7 +223,7 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client, } } - i2c_set_clientdata(client, rdev); + i2c_set_clientdata(client, max1586); dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n"); return 0; @@ -238,13 +238,13 @@ out: static int __devexit max1586_pmic_remove(struct i2c_client *client) { - struct regulator_dev **rdev = i2c_get_clientdata(client); + struct max1586_data *max1586 = i2c_get_clientdata(client); int i; for (i = 0; i <= MAX1586_V6; i++) - if (rdev[i]) - regulator_unregister(rdev[i]); - kfree(rdev); + if (max1586->rdev[i]) + regulator_unregister(max1586->rdev[i]); + kfree(max1586); return 0; } -- cgit v1.2.3 From 53a4befa4e3d1490a63dae1ac03a2e7c865b2f53 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Aug 2010 13:35:27 +0800 Subject: regulator: max8660 - fix a memory leak in max8660_remove() In max8660_probe(), we allocate memory for max660. In max8660_remove(), current implementation only free rdev which is a member of struct max8660. Thus, there is a small memory leak when we unload the module. This patch fixes the memory leak by passing max660 to i2c clientdata, and properly kfree(max8660) in max8660_remove(). Signed-off-by: Axel Lin Acked-by: Wolfram Sang Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max8660.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index d97220efae5a..c570e6eb0db2 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -450,7 +450,7 @@ static int __devinit max8660_probe(struct i2c_client *client, } } - i2c_set_clientdata(client, rdev); + i2c_set_clientdata(client, max8660); dev_info(&client->dev, "Maxim 8660/8661 regulator driver loaded\n"); return 0; @@ -465,13 +465,13 @@ out: static int __devexit max8660_remove(struct i2c_client *client) { - struct regulator_dev **rdev = i2c_get_clientdata(client); + struct max8660 *max8660 = i2c_get_clientdata(client); int i; for (i = 0; i < MAX8660_V_END; i++) - if (rdev[i]) - regulator_unregister(rdev[i]); - kfree(rdev); + if (max8660->rdev[i]) + regulator_unregister(max8660->rdev[i]); + kfree(max8660); return 0; } -- cgit v1.2.3 From 1bddc2f5c1727577183761581d5d4d1b00a8bd63 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 9 Aug 2010 17:01:21 +0800 Subject: regulator: lp3971 - remove unnecessary ret value checking in lp3971_i2c_write() i2c_smbus_write_byte_data() returns zero or negative value, therefore no need to check if ret is greater than zero or not. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/lp3971.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 5362289c7d69..3bb82b624e19 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -387,15 +387,9 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count, static int lp3971_i2c_write(struct i2c_client *i2c, char reg, int count, const u16 *src) { - int ret; - if (count != 1) return -EIO; - ret = i2c_smbus_write_byte_data(i2c, reg, *src); - if (ret >= 0) - return 0; - - return ret; + return i2c_smbus_write_byte_data(i2c, reg, *src); } static u8 lp3971_reg_read(struct lp3971 *lp3971, u8 reg) -- cgit v1.2.3 From 8f1f151ed8ae4063837221cc9f5386a7af59f4e0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 9 Aug 2010 14:48:16 +0800 Subject: mfd: max8998 - fix incorrect kfree(i2c) in i2c_driver probe callback handler The i2c_client received in probe() should not be kfree()'d. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz Signed-off-by: Liam Girdwood --- drivers/mfd/max8998.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 0d68de21ea9e..73e6f5c4efc9 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -89,10 +89,8 @@ static int max8998_i2c_probe(struct i2c_client *i2c, int ret = 0; max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL); - if (max8998 == NULL) { - kfree(i2c); + if (max8998 == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, max8998); max8998->dev = &i2c->dev; -- cgit v1.2.3 From e7973c3cb352e09751afabcb0c0f9fd3032965b6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 9 Aug 2010 15:58:06 +0800 Subject: regulator: tps6586x - add regulator_unregister() in tps6586x_regulator_remove() Signed-off-by: Axel Lin Acked-by: Mike Rapoport Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps6586x-regulator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 3c2eee8e7563..122193b5e269 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -357,13 +357,16 @@ static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) return PTR_ERR(rdev); } - platform_set_drvdata(pdev, ri); + platform_set_drvdata(pdev, rdev); return 0; } static int __devexit tps6586x_regulator_remove(struct platform_device *pdev) { + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); return 0; } -- cgit v1.2.3 From d7399fa88847ae93203ff5618edd97d94d36c762 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 9 Aug 2010 15:51:23 +0800 Subject: regulator: tps6507x - add missing platform_set_drvdata in tps6507x_pmic_probe We should call platform_set_drvdata() before calling platform_get_drvdata(). Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps6507x-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 8152d65220f5..c239f42aa4a3 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -614,6 +614,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev) } tps6507x_dev->pmic = tps; + platform_set_drvdata(pdev, tps6507x_dev); return 0; -- cgit v1.2.3 From 120be663285f80e3501e36ccbb92e7143585fd93 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 10 Aug 2010 16:02:13 +0100 Subject: regulator: Remove default DEBUG define from TPS6586x Signed-off-by: Mark Brown Acked-by: Mike Rapoport Signed-off-by: Liam Girdwood --- drivers/regulator/tps6586x-regulator.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 122193b5e269..8cff1413a147 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -13,7 +13,6 @@ * published by the Free Software Foundation. */ -#define DEBUG #include #include #include -- cgit v1.2.3