From 597351697ec700f5c87cf511722f4fe3b7b02acd Mon Sep 17 00:00:00 2001 From: Jake Park Date: Fri, 2 Dec 2011 11:11:31 +0900 Subject: regulator: fan53555: Adding driver Adding regulator driver for the device FAN53555. Bug 892117 Change-Id: I895094d3e0aaeb85cfd33f1bc16008c66961b403 Reviewed-on: http://git-master/r/67862 Reviewed-by: Laxman Dewangan Tested-by: Jake Park --- drivers/regulator/Kconfig | 13 + drivers/regulator/Makefile | 2 +- drivers/regulator/fan53555-regulator.c | 567 +++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+), 1 deletion(-) create mode 100644 drivers/regulator/fan53555-regulator.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 3d8793a790b2..fe311c2af398 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -368,5 +368,18 @@ config REGULATOR_GPIO_SWITCH Say Yes if the given platform have the rail enable through the gpios. +config REGULATOR_AAT2870 + tristate "AnalogicTech AAT2870 Regulators" + depends on MFD_AAT2870_CORE + help + If you have a AnalogicTech AAT2870 say Y to enable the + regulator driver. + +config REGULATOR_FAN53555 + tristate "Fairchild FAN53555 DC-DC CPU power supply regulators" + default n + help + This driver supports FAN53555 DC-DC CPU power supply. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bbfa1d15bea7..3de5dafa0a0f 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -52,5 +52,5 @@ obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_GPIO_SWITCH) += gpio-switch-regulator.o - +obj-$(CONFIG_REGULATOR_FAN53555) += fan53555-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/fan53555-regulator.c b/drivers/regulator/fan53555-regulator.c new file mode 100644 index 000000000000..13fa79c4ba3a --- /dev/null +++ b/drivers/regulator/fan53555-regulator.c @@ -0,0 +1,567 @@ +/* + * driver/regultor/fan53555-regulator.c + * + * Driver for FAN53555UC00X, FAN53555UC01X, FAN53555UC03X, + * FAN53555UC04X, FAN53555UC05X + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define FAN53555_REG_VSEL0 0 +#define FAN53555_REG_VSEL1 1 +#define FAN53555_REG_CONTROL 2 +#define FAN53555_REG_ID1 3 +#define FAN53555_REG_ID2 4 +#define FAN53555_REG_MONITOR 5 + +#define FAN53555_VSEL_BUCK_EN BIT(7) +#define FAN53555_VSEL_MODE BIT(6) +#define FAN53555_VSEL_NSEL_SHIFT 0 +#define FAN53555_VSEL_NSEL_MASK 0x3F + +#define FAN53555_CONTROL_DISCHARGE BIT(7) +#define FAN53555_CONTROL_SLEW_SHIFT 4 +#define FAN53555_CONTROL_SLEW_MASK 0x70 +#define FAN53555_CONTROL_RESET BIT(2) + +#define FAN53555_ID1_VENDOR_SHIFT 4 +#define FAN53555_ID1_VENDOR_MASK 0xF0 +#define FAN53555_ID1_DIE_ID_SHIFT 0 +#define FAN53555_ID1_DIE_ID_MASK 0x0F + +#define FAN53555_ID2_REV_SHIFT 0 +#define FAN53555_ID2_REV_MASK 0x0F + +#define FAN53555_MONITOR_ILIM BIT(7) +#define FAN53555_MONITOR_UVLO BIT(6) +#define FAN53555_MONITOR_OVP BIT(5) +#define FAN53555_MONITOR_POS BIT(4) +#define FAN53555_MONITOR_NEG BIT(3) +#define FAN53555_MONITOR_RESET_STAT BIT(2) +#define FAN53555_MONITOR_OT BIT(1) +#define FAN53555_MONITOR_BUCK_STATUS BIT(0) + +#define FAN53555_VSEL0_ID 0 +#define FAN53555_VSEL1_ID 1 + +#define FAN53555UC00X_ID 0x80 +#define FAN53555UC01X_ID 0x81 +#define FAN53555UC03X_ID 0x83 +#define FAN53555UC04X_ID 0x84 +#define FAN53555UC05X_ID 0x85 + +#define FAN53555_N_VOLTAGES 64 + +/* FAN53555 chip information */ +struct fan53555_chip { + const char *name; + struct device *dev; + struct regulator_desc desc; + struct i2c_client *client; + struct regulator_dev *rdev; + struct mutex io_lock; + int chip_id; + int vsel_id; + u8 shadow[6]; +}; + +#define FAN53555_VOLTAGE(chip_id, vsel) \ + (((chip_id) == FAN53555UC04X_ID) ? \ + ((vsel) * 12826 + 600000) : ((vsel) * 10000 + 600000)) + +static int fan53555_read(struct fan53555_chip *fan, u8 reg) +{ + u8 data; + u8 val; + int ret; + + data = reg; + + ret = i2c_master_send(fan->client, &data, 1); + if (ret < 0) + goto out; + + ret = i2c_master_recv(fan->client, &val, 1); + if (ret < 0) + goto out; + + ret = val; +out: + return ret; +} + +static inline int fan53555_write(struct fan53555_chip *fan, u8 reg, u8 val) +{ + u8 msg[2]; + int ret; + + msg[0] = reg; + msg[1] = val; + + ret = i2c_master_send(fan->client, msg, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + return 0; +} + +static int fan53555_read_reg(struct fan53555_chip *fan, u8 reg) +{ + int data; + + mutex_lock(&fan->io_lock); + data = fan53555_read(fan, reg); + if (data < 0) + dev_err(fan->dev, "Read from reg 0x%x failed\n", reg); + mutex_unlock(&fan->io_lock); + + return data; +} + +static int fan53555_set_bits(struct fan53555_chip *fan, u8 reg, u8 mask, u8 val) +{ + int err; + u8 data; + + mutex_lock(&fan->io_lock); + data = fan->shadow[reg]; + data &= ~mask; + val &= mask; + data |= val; + err = fan53555_write(fan, reg, data); + if (err) + dev_err(fan->dev, "write for reg 0x%x failed\n", reg); + else + fan->shadow[reg] = data; + mutex_unlock(&fan->io_lock); + + return err; +} + +static int __fan53555_dcdc_set_voltage(struct fan53555_chip *fan, + int vsel_id, int min_uV, int max_uV, + unsigned *selector) +{ + int nsel; + int uV; + int chip_id; + int n_voltages; + + chip_id = fan->chip_id; + n_voltages = fan->desc.n_voltages; + + if (max_uV < min_uV) { + dev_err(fan->dev, "max_uV(%d) < min_uV(%d)\n", max_uV, min_uV); + return -EINVAL; + } + if (min_uV > FAN53555_VOLTAGE(chip_id, n_voltages - 1)) { + dev_err(fan->dev, "min_uV(%d) > %d[uV]\n", + min_uV, FAN53555_VOLTAGE(chip_id, n_voltages - 1)); + return -EINVAL; + } + if (max_uV < FAN53555_VOLTAGE(chip_id, 0)) { + dev_err(fan->dev, "max_uV(%d) < %d[uV]\n", + max_uV, FAN53555_VOLTAGE(chip_id, 0)); + return -EINVAL; + } + if ((vsel_id != FAN53555_VSEL0_ID) && (vsel_id != FAN53555_VSEL1_ID)) { + dev_err(fan->dev, + "%d is not valid VSEL register ID\n", vsel_id); + return -EINVAL; + } + for (nsel = 0; nsel < n_voltages; nsel++) { + uV = FAN53555_VOLTAGE(chip_id, nsel); + if (min_uV <= uV && uV <= max_uV) { + if (selector) + *selector = nsel; + return fan53555_set_bits(fan, + FAN53555_REG_VSEL0 + vsel_id, + FAN53555_VSEL_NSEL_MASK, + nsel << + FAN53555_VSEL_NSEL_SHIFT); + } + } + + return -EINVAL; +} + +#ifdef CONFIG_DEBUG_FS + +#include +#include + +static int dbg_fan_show(struct seq_file *s, void *unused) +{ + struct fan53555_chip *fan = s->private; + int val; + + seq_printf(s, "FAN53555 Registers\n"); + seq_printf(s, "------------------\n"); + + val = fan53555_read_reg(fan, FAN53555_REG_VSEL0); + if (val >= 0) + seq_printf(s, "Reg VSEL0 Value 0x%02x\n", val); + + val = fan53555_read_reg(fan, FAN53555_REG_VSEL1); + if (val >= 0) + seq_printf(s, "Reg VSEL1 Value 0x%02x\n", val); + + val = fan53555_read_reg(fan, FAN53555_REG_CONTROL); + if (val >= 0) + seq_printf(s, "Reg CONTROL Value 0x%02x\n", val); + + val = fan53555_read_reg(fan, FAN53555_REG_ID1); + if (val >= 0) + seq_printf(s, "Reg ID1 Value 0x%02x\n", val); + + val = fan53555_read_reg(fan, FAN53555_REG_ID2); + if (val >= 0) + seq_printf(s, "Reg ID2 Value 0x%02x\n", val); + + val = fan53555_read_reg(fan, FAN53555_REG_MONITOR); + if (val >= 0) + seq_printf(s, "Reg MONITOR Value 0x%02x\n", val); + + return 0; +} + +static int dbg_fan_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_fan_show, inode->i_private); +} + +static const struct file_operations debug_fops = { + .open = dbg_fan_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void __init fan53555_debuginit(struct fan53555_chip *fan) +{ + (void)debugfs_create_file("fan53555", S_IRUGO, NULL, fan, &debug_fops); +} +#else +static void __init fan53555_debuginit(struct fan53555_chip *fan) +{ +} +#endif + +static int fan53555_dcdc_init(struct fan53555_chip *fan, + struct i2c_client *client, + struct fan53555_regulator_platform_data *pdata) +{ + int err; + int val; + + err = fan53555_read_reg(fan, FAN53555_REG_VSEL0); + if (err < 0) + return err; + fan->shadow[FAN53555_REG_VSEL0] = (u8)err; + + err = fan53555_read_reg(fan, FAN53555_REG_VSEL1); + if (err < 0) + return err; + fan->shadow[FAN53555_REG_VSEL1] = (u8)err; + + err = fan53555_read_reg(fan, FAN53555_REG_CONTROL); + if (err < 0) + return err; + fan->shadow[FAN53555_REG_CONTROL] = (u8)err; + + err = __fan53555_dcdc_set_voltage(fan, + FAN53555_VSEL0_ID, + pdata->init_vsel0_min_uV, + pdata->init_vsel0_max_uV, + NULL); + if (err < 0) + return err; + + val = pdata->vsel0_buck_en ? FAN53555_VSEL_BUCK_EN : 0; + val |= pdata->vsel0_mode ? FAN53555_VSEL_MODE : 0; + err = fan53555_set_bits(fan, + FAN53555_REG_VSEL0, + FAN53555_VSEL_BUCK_EN | FAN53555_VSEL_MODE, + val); + if (err < 0) + return err; + + err = __fan53555_dcdc_set_voltage(fan, + FAN53555_VSEL1_ID, + pdata->init_vsel1_min_uV, + pdata->init_vsel1_max_uV, + NULL); + if (err < 0) + return err; + + val = pdata->vsel1_buck_en ? FAN53555_VSEL_BUCK_EN : 0; + val |= pdata->vsel1_mode ? FAN53555_VSEL_MODE : 0; + err = fan53555_set_bits(fan, + FAN53555_REG_VSEL1, + FAN53555_VSEL_BUCK_EN | FAN53555_VSEL_MODE, + val); + if (err < 0) + return err; + + val = pdata->slew_rate; + val <<= FAN53555_CONTROL_SLEW_SHIFT; + val |= pdata->output_discharge ? FAN53555_CONTROL_DISCHARGE : 0; + err = fan53555_set_bits(fan, + FAN53555_REG_CONTROL, + FAN53555_CONTROL_DISCHARGE | + FAN53555_CONTROL_SLEW_MASK, val); + return err; +} + +static int fan53555_dcdc_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct fan53555_chip *fan = rdev_get_drvdata(dev); + + if ((selector < 0) || (selector >= fan->desc.n_voltages)) + return -EINVAL; + + return FAN53555_VOLTAGE(fan->chip_id, selector); +} + +static int fan53555_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, + unsigned *selector) +{ + struct fan53555_chip *fan = rdev_get_drvdata(dev); + + return __fan53555_dcdc_set_voltage(fan, fan->vsel_id, min_uV, max_uV, + selector); +} + +static int fan53555_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct fan53555_chip *fan = rdev_get_drvdata(dev); + u8 data; + + if ((fan->vsel_id != FAN53555_VSEL0_ID) && + (fan->vsel_id != FAN53555_VSEL1_ID)) { + dev_err(fan->dev, + "%d is not valid VSEL register ID\n", fan->vsel_id); + return -EINVAL; + } + data = fan->shadow[FAN53555_REG_VSEL0 + fan->vsel_id]; + data &= FAN53555_VSEL_NSEL_MASK; + data >>= FAN53555_VSEL_NSEL_SHIFT; + + return FAN53555_VOLTAGE(fan->chip_id, data); +} + +static int fan53555_dcdc_enable(struct regulator_dev *dev) +{ + struct fan53555_chip *fan = rdev_get_drvdata(dev); + + if ((fan->vsel_id != FAN53555_VSEL0_ID) && + (fan->vsel_id != FAN53555_VSEL1_ID)) { + dev_err(fan->dev, + "%d is not valid VSEL register ID\n", fan->vsel_id); + return -EINVAL; + } + + return fan53555_set_bits(fan, + FAN53555_REG_VSEL0 + fan->vsel_id, + FAN53555_VSEL_BUCK_EN, FAN53555_VSEL_BUCK_EN); +} + +static int fan53555_dcdc_disable(struct regulator_dev *dev) +{ + struct fan53555_chip *fan = rdev_get_drvdata(dev); + + if ((fan->vsel_id != FAN53555_VSEL0_ID) && + (fan->vsel_id != FAN53555_VSEL1_ID)) { + dev_err(fan->dev, + "%d is not valid VSEL register ID\n", fan->vsel_id); + return -EINVAL; + } + + return fan53555_set_bits(fan, + FAN53555_REG_VSEL0 + fan->vsel_id, + FAN53555_VSEL_BUCK_EN, 0); +} + +static int fan53555_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct fan53555_chip *fan = rdev_get_drvdata(dev); + u8 data; + + if ((fan->vsel_id != FAN53555_VSEL0_ID) && + (fan->vsel_id != FAN53555_VSEL1_ID)) { + dev_err(fan->dev, + "%d is not valid VSEL register ID\n", fan->vsel_id); + return -EINVAL; + } + data = fan->shadow[FAN53555_REG_VSEL0 + fan->vsel_id]; + + return (data & FAN53555_VSEL_BUCK_EN) ? 1 : 0; +} + +static struct regulator_ops fan53555_dcdc_ops = { + .list_voltage = fan53555_dcdc_list_voltage, + .set_voltage = fan53555_dcdc_set_voltage, + .get_voltage = fan53555_dcdc_get_voltage, + .enable = fan53555_dcdc_enable, + .disable = fan53555_dcdc_disable, + .is_enabled = fan53555_dcdc_is_enabled, +}; + +static int __devinit fan53555_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct fan53555_regulator_platform_data *pdata; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + struct fan53555_chip *fan; + int chip_id; + int err; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "Err: Platform data not found\n"); + return -EIO; + } + init_data = &pdata->reg_init_data; + fan = kzalloc(sizeof(*fan), GFP_KERNEL); + if (!fan) { + dev_err(&client->dev, "Err: Memory allocation fails\n"); + return -ENOMEM; + } + mutex_init(&fan->io_lock); + fan->client = client; + fan->dev = &client->dev; + fan->vsel_id = pdata->vsel_id; + fan->name = id->name; + fan->desc.name = id->name; + fan->desc.id = 0; + fan->desc.irq = 0; + fan->desc.ops = &fan53555_dcdc_ops; + fan->desc.type = REGULATOR_VOLTAGE; + fan->desc.owner = THIS_MODULE; + fan->desc.n_voltages = FAN53555_N_VOLTAGES; + i2c_set_clientdata(client, fan); + + chip_id = fan53555_read_reg(fan, FAN53555_REG_ID1); + if (chip_id < 0) { + err = chip_id; + dev_err(fan->dev, "Error in reading device %d\n", err); + goto fail; + } + + switch (chip_id) { + case FAN53555UC00X_ID: + case FAN53555UC01X_ID: + case FAN53555UC03X_ID: + case FAN53555UC04X_ID: + case FAN53555UC05X_ID: + fan->chip_id = chip_id; + break; + default: + dev_err(fan->dev, "Err: not supported device chip id 0x%x", + chip_id); + err = -ENODEV; + goto fail; + } + + err = fan53555_dcdc_init(fan, client, pdata); + if (err < 0) { + dev_err(fan->dev, "FAN53555 init fails with %d\n", err); + goto fail; + } + + rdev = regulator_register(&fan->desc, &client->dev, init_data, fan); + if (IS_ERR(rdev)) { + dev_err(fan->dev, "Failed to register %s\n", id->name); + err = PTR_ERR(rdev); + goto fail; + } + fan->rdev = rdev; + + fan53555_debuginit(fan); + return 0; + +fail: + kfree(fan); + return err; +} + +/** + * fan53555_remove - fan53555 driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister fan53555 driver as an i2c client device driver + */ +static int __devexit fan53555_remove(struct i2c_client *client) +{ + struct fan53555_chip *chip = i2c_get_clientdata(client); + + regulator_unregister(chip->rdev); + kfree(chip); + return 0; +} + +static const struct i2c_device_id fan53555_id[] = { + {.name = "fan53555", .driver_data = 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, fan53555_id); + +static struct i2c_driver fan53555_i2c_driver = { + .driver = { + .name = "fan53555", + .owner = THIS_MODULE, + }, + .probe = fan53555_probe, + .remove = __devexit_p(fan53555_remove), + .id_table = fan53555_id, +}; + +/* Module init function */ +static int __init fan53555_init(void) +{ + return i2c_add_driver(&fan53555_i2c_driver); +} +subsys_initcall_sync(fan53555_init); + +/* Module exit function */ +static void __exit fan53555_cleanup(void) +{ + i2c_del_driver(&fan53555_i2c_driver); +} +module_exit(fan53555_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jake Park"); +MODULE_DESCRIPTION("Regulator Driver for Fairchild FAN53555 Regulator"); +MODULE_ALIAS("platform:fan53555-regulator"); -- cgit v1.2.3