/* * 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");