/* * 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 #include #include #include #include #include #include #include #include #include /* 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 "); MODULE_DESCRIPTION("TPS6130X voltage regulator driver"); MODULE_LICENSE("GPL v2");