diff options
author | Girish S Ghongdemath <girishsg@ti.com> | 2011-11-23 16:04:14 -0600 |
---|---|---|
committer | Ziyann <jaraidaniel@gmail.com> | 2014-10-01 12:59:44 +0200 |
commit | 6b7bec685d80f0505e52ba17588234a2582960ef (patch) | |
tree | fb7fd697638ce25168e9d37fcaaeabeeaa7e5d42 | |
parent | 61e6cbba9a161d17787813dc74e2b97369f26346 (diff) | |
download | kernel_samsung_tuna-6b7bec685d80f0505e52ba17588234a2582960ef.zip kernel_samsung_tuna-6b7bec685d80f0505e52ba17588234a2582960ef.tar.gz kernel_samsung_tuna-6b7bec685d80f0505e52ba17588234a2582960ef.tar.bz2 |
Regulator: tps6130x: Add support for TI tps6130x regulator
Add initial support for TI tps6130x family. TPS6130x can
operate as a standard boost regulator, output voltage range
is 3.825-5.700V. Current source support is not added in this
initial version.
Ported from p-android-omap-2.6.35
Change-Id: I28c9ba1d87652b1269ab23da9e2609fb993b4460
Signed-off-by: Girish S G <girishsg@ti.com>
Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
Signed-off-by: Akash Choudhari <akashc@ti.com>
Signed-off-by: Gabriel M. Beddingfield <gabrbedd@ti.com>
-rw-r--r-- | drivers/regulator/Kconfig | 7 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/tps6130x-regulator.c | 419 | ||||
-rw-r--r-- | include/linux/regulator/tps6130x.h | 33 |
4 files changed, 460 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index d7ed20f..66b88b8 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -232,6 +232,13 @@ config REGULATOR_TPS6105X It is a single boost converter primarily for white LEDs and audio amplifiers. +config REGULATOR_TPS6130X + tristate "TI TPS6130X Power regulators" + depends on I2C + help + This driver supports TPS6130X family. TPS6130X can operate as a + regulated current source and/or as a standard voltage boost converter. + config REGULATOR_TPS65023 tristate "TI TPS65023 Power regulators" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3932d2e..5eb253c 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -34,6 +34,7 @@ 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_TPS6130X) += tps6130x-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o diff --git a/drivers/regulator/tps6130x-regulator.c b/drivers/regulator/tps6130x-regulator.c new file mode 100644 index 0000000..35fe37b --- /dev/null +++ b/drivers/regulator/tps6130x-regulator.c @@ -0,0 +1,419 @@ +/* + * tps6130x-regulator.c + * + * Regulator driver for TPS6130x + * + * Copyright (C) 2011 Texas Instrument 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/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/tps6130x.h> + +/* Regulator id */ +#define TPS6130X_VOUT 0 + +/* Register definitions */ +#define TPS6130X_REGISTER1 0x01 +#define TPS6130X_REGISTER2 0x02 +#define TPS6130X_REGISTER3 0x03 +#define TPS6130X_REGISTER4 0x04 +#define TPS6130X_REGISTER5 0x05 +#define TPS6130X_REGISTER6 0x06 +#define TPS6130X_REGISTER7 0x07 + +/* REGISTER1 bitfields */ +#define TPS6130X_ENVM 0x80 +#define TPS6130X_MODE_CTRL_MASK 0x60 +#define TPS6130X_SHUTDOWN_MODE (0 << 5) +#define TPS6130X_DC_LIGHT_MODE (1 << 5) +#define TPS6130X_DC_LIGHT_FLASH_MODE (2 << 5) +#define TPS6130X_VOUT_MODE (3 << 5) + +/* REGISTER6 bitfields */ +#define TPS6130X_OV_MASK 0x0F + +/* REGISTER7 bitfields */ +#define TPS6130X_REVID_MASK 0x07 + +/* Supported voltage values for voltage regulation mode */ +#define TPS6130X_VOUT_MIN_UV 3825000 +#define TPS6130X_VOUT_MAX_UV 5700000 +#define TPS6130X_VOUT_STEP 125000 +#define TPS6130X_VOUT_MAX_SEL 0x0F + +/* Regulator specific details */ +struct tps_info { + const char *name; + int min_uV; + int max_uV; + int step; + int vsel_max; + int vsel; +}; + +/* PMIC details */ +struct tps_pmic { + struct regulator_desc *desc; + struct i2c_client *client; + struct regulator_dev *rdev; + struct tps6130x_platform_data *pdata; + struct tps_info *info; + struct mutex io_lock; + int enabled; +}; + +static int tps6130x_reg_read(struct tps_pmic *tps, u8 reg) +{ + int data; + + data = i2c_smbus_read_byte_data(tps->client, reg); + if (data < 0) + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + + return data; +} + +static int tps6130x_reg_write(struct tps_pmic *tps, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(tps->client, reg, val); + if (ret < 0) + dev_err(&tps->client->dev, "Write to reg 0x%x failed\n", reg); + + return ret; +} + +static int tps6130x_update_bits(struct tps_pmic *tps, u8 reg, + u8 mask, u8 value) +{ + int old_val, new_val, ret = 0; + + mutex_lock(&tps->io_lock); + + old_val = tps6130x_reg_read(tps, reg); + if (old_val < 0) { + ret = old_val; + goto err; + } + + new_val = (old_val & ~mask) | value; + if (old_val != new_val) + ret = tps6130x_reg_write(tps, reg, new_val); + +err: + mutex_unlock(&tps->io_lock); + return ret; +} + +static int tps6130x_chip_enable(struct tps_pmic *tps, int on) +{ + struct i2c_client *client = tps->client; + int ret = 0; + + /* tps61301/tps61305 supports external NRESET */ + if (tps->pdata && tps->pdata->chip_enable) { + ret = tps->pdata->chip_enable(on); + if (ret) { + dev_err(&client->dev, "failed to enable %d\n", ret); + return ret; + } + } + + tps->enabled = on; + + return ret; +} + +/* Operations permitted on VOUT */ + +static int tps6130x_vout_is_enabled(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, vout = 0; + + if (!tps->enabled) + return 0; + + data = tps6130x_reg_read(tps, TPS6130X_REGISTER1); + if (data < 0) + return data; + + /* tps61300/tps61301 have ENVM to force voltage regulation mode */ + if (data & TPS6130X_ENVM) + vout = 1; + + /* device operating in voltage regulation mode */ + if ((data & TPS6130X_MODE_CTRL_MASK) == TPS6130X_VOUT_MODE) + vout = 1; + + return vout; +} + +static int tps6130x_vout_enable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps_info *info = tps->info; + int ret; + + ret = tps6130x_chip_enable(tps, 1); + if (ret) + return ret; + + /* set constant voltage source mode */ + ret = tps6130x_update_bits(tps, TPS6130X_REGISTER1, + TPS6130X_MODE_CTRL_MASK, + TPS6130X_VOUT_MODE); + if (ret) + goto err1; + + /* output voltage must be set after voltage mode has been enabled */ + ret = tps6130x_update_bits(tps, TPS6130X_REGISTER6, + TPS6130X_OV_MASK, info->vsel); + if (ret) + goto err2; + + return 0; + +err2: + tps6130x_update_bits(tps, TPS6130X_REGISTER1, + TPS6130X_MODE_CTRL_MASK, + TPS6130X_SHUTDOWN_MODE); +err1: + tps6130x_chip_enable(tps, 0); + return ret; +} + +static int tps6130x_vout_disable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ret; + + /* may not disable voltage output if ENVM is forced */ + ret = tps6130x_update_bits(tps, TPS6130X_REGISTER1, + TPS6130X_MODE_CTRL_MASK, + TPS6130X_SHUTDOWN_MODE); + if (ret) + return ret; + + ret = tps6130x_chip_enable(tps, 0); + + return ret; +} + +static int tps6130x_vout_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps_info *info = tps->info; + + if (selector > info->vsel_max) + return -EINVAL; + + return info->min_uV + selector * info->step; +} + +static int tps6130x_vout_get_voltage(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps_info *info = tps->info; + + /* output voltage cannot be read from REGISTER6 */ + return info->min_uV + info->vsel * info->step; +} + +static int tps6130x_vout_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, unsigned *selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps_info *info = tps->info; + int vsel, ret; + + if ((min_uV < info->min_uV) || (min_uV > info->max_uV)) + return -EINVAL; + + if ((max_uV < info->min_uV) || (max_uV > info->max_uV)) + return -EINVAL; + + vsel = (min_uV - info->min_uV + (info->step - 1)) / info->step; + + ret = tps6130x_vout_list_voltage(dev, vsel); + if ((ret < 0) || (ret > max_uV)) + return -EINVAL; + + /* + * new target voltage must be set after + * voltage mode operation has been enabled + */ + info->vsel = vsel; + *selector = vsel; + + BUG_ON(vsel < 0); + BUG_ON(vsel > TPS6130X_VOUT_MAX_SEL); + + return 0; +} + +static struct regulator_ops tps6130x_ops = { + .is_enabled = tps6130x_vout_is_enabled, + .enable = tps6130x_vout_enable, + .disable = tps6130x_vout_disable, + .get_voltage = tps6130x_vout_get_voltage, + .set_voltage = tps6130x_vout_set_voltage, + .list_voltage = tps6130x_vout_list_voltage, +}; + +static struct regulator_desc tps6130x_desc = { + .name = "VOUT", + .id = TPS6130X_VOUT, + .ops = &tps6130x_ops, + .n_voltages = TPS6130X_VOUT_MAX_SEL + 1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int tps6130x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps_info *info = (void *)id->driver_data; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + struct tps_pmic *tps; + int revid, ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + init_data = client->dev.platform_data; + if (!init_data) + return -EIO; + + tps = kzalloc(sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + mutex_init(&tps->io_lock); + tps->client = client; + tps->info = info; + tps->desc = &tps6130x_desc; + + if (init_data->driver_data) + tps->pdata = init_data->driver_data; + + /* register the regulator */ + rdev = regulator_register(tps->desc, &client->dev, init_data, tps); + if (IS_ERR(rdev)) { + dev_err(&client->dev, "failed to register %s\n", id->name); + ret = PTR_ERR(rdev); + goto reg_err; + } + + tps->rdev = rdev; + i2c_set_clientdata(client, tps); + + ret = tps6130x_chip_enable(tps, 1); + if (ret) + goto enable_err; + + revid = tps6130x_reg_read(tps, TPS6130X_REGISTER7); + if (revid < 0) { + dev_err(&client->dev, "failed to access device\n"); + ret = revid; + goto rev_err; + } + + dev_info(&client->dev, "Revision %d\n", revid & TPS6130X_REVID_MASK); + + /* default output voltage: 4.950V */ + tps->info->vsel = 9; + + ret = tps6130x_chip_enable(tps, 0); + if (ret) + goto rev_err; + + return 0; + +rev_err: + tps6130x_chip_enable(tps, 0); +enable_err: + i2c_set_clientdata(client, NULL); + regulator_unregister(tps->rdev); +reg_err: + mutex_destroy(&tps->io_lock); + tps->client = NULL; + kfree(tps); + return ret; +} + +static int __devexit tps6130x_remove(struct i2c_client *client) +{ + struct tps_pmic *tps = i2c_get_clientdata(client); + + regulator_unregister(tps->rdev); + mutex_destroy(&tps->io_lock); + tps->client = NULL; + i2c_set_clientdata(client, NULL); + kfree(tps); + + return 0; +} + +static struct tps_info tps6130x_regs = { + .name = "VOUT", + .min_uV = TPS6130X_VOUT_MIN_UV, + .max_uV = TPS6130X_VOUT_MAX_UV, + .step = TPS6130X_VOUT_STEP, + .vsel_max = TPS6130X_VOUT_MAX_SEL, +}; + +static const struct i2c_device_id tps6130x_id[] = { + { + .name = "tps6130x", + .driver_data = (unsigned long)&tps6130x_regs, + }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tps6130x_id); + +static struct i2c_driver tps6130x_i2c_driver = { + .driver = { + .name = "tps6130x", + .owner = THIS_MODULE, + }, + .probe = tps6130x_probe, + .remove = __devexit_p(tps6130x_remove), + .id_table = tps6130x_id, +}; + +static int __init tps6130x_init(void) +{ + return i2c_add_driver(&tps6130x_i2c_driver); +} +subsys_initcall(tps6130x_init); + +static void __exit tps6130x_cleanup(void) +{ + i2c_del_driver(&tps6130x_i2c_driver); +} +module_exit(tps6130x_cleanup); + +MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); +MODULE_DESCRIPTION("TPS6130X voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regulator/tps6130x.h b/include/linux/regulator/tps6130x.h new file mode 100644 index 0000000..41d5913 --- /dev/null +++ b/include/linux/regulator/tps6130x.h @@ -0,0 +1,33 @@ +/* + * tps6130x-regulator.h + * + * Regulator driver for TPS6130x + * + * Copyright (C) 2011 Texas Instrument 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. + */ + +#ifndef __LINUX_REGULATOR_TPS6130X_H +#define __LINUX_REGULATOR_TPS6130X_H + +/** + * tps6130x_platform_data - platform data for tps6130x regulator + * @enable: Put tps6130x in normal/reset mode (used for tps61301/tps61305) + * If not provided, it's assumed chip is always enabled: + * - tps61300/tps61306 + * - tps61301/tps61305 with NRESET forced to low by hardware + */ + +struct tps6130x_platform_data { + int (*chip_enable)(int on); +}; + +#endif |