/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include /* registers */ #define REG_NCP6335D_PID 0x03 #define REG_NCP6335D_PROGVSEL1 0x10 #define REG_NCP6335D_PROGVSEL0 0x11 #define REG_NCP6335D_PGOOD 0x12 #define REG_NCP6335D_TIMING 0x13 #define REG_NCP6335D_COMMAND 0x14 /* constraints */ #define NCP6335D_MIN_VOLTAGE_UV 600000 #define NCP6335D_STEP_VOLTAGE_UV 6250 #define NCP6335D_MIN_SLEW_NS 166 #define NCP6335D_MAX_SLEW_NS 1333 /* bits */ #define NCP6335D_ENABLE BIT(7) #define NCP6335D_DVS_PWM_MODE BIT(5) #define NCP6335D_PWM_MODE1 BIT(6) #define NCP6335D_PWM_MODE0 BIT(7) #define NCP6335D_PGOOD_DISCHG BIT(4) #define NCP6335D_SLEEP_MODE BIT(4) #define NCP6335D_VOUT_SEL_MASK 0x7F #define NCP6335D_SLEW_MASK 0x18 #define NCP6335D_SLEW_SHIFT 0x3 struct ncp6335d_info { struct regulator_dev *regulator; struct regulator_init_data *init_data; struct regmap *regmap; struct device *dev; unsigned int vsel_reg; unsigned int mode_bit; int curr_voltage; int slew_rate; }; static void dump_registers(struct ncp6335d_info *dd, unsigned int reg, const char *func) { unsigned int val = 0; regmap_read(dd->regmap, reg, &val); dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val); } static void ncp633d_slew_delay(struct ncp6335d_info *dd, int prev_uV, int new_uV) { u8 val; int delay; val = abs(prev_uV - new_uV) / NCP6335D_STEP_VOLTAGE_UV; delay = (val * dd->slew_rate / 1000) + 1; dev_dbg(dd->dev, "Slew Delay = %d\n", delay); udelay(delay); } static int ncp6335d_enable(struct regulator_dev *rdev) { int rc; struct ncp6335d_info *dd = rdev_get_drvdata(rdev); rc = regmap_update_bits(dd->regmap, dd->vsel_reg, NCP6335D_ENABLE, NCP6335D_ENABLE); if (rc) dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc); dump_registers(dd, dd->vsel_reg, __func__); return rc; } static int ncp6335d_disable(struct regulator_dev *rdev) { int rc; struct ncp6335d_info *dd = rdev_get_drvdata(rdev); rc = regmap_update_bits(dd->regmap, dd->vsel_reg, NCP6335D_ENABLE, 0); if (rc) dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc); dump_registers(dd, dd->vsel_reg, __func__); return rc; } static int ncp6335d_get_voltage(struct regulator_dev *rdev) { unsigned int val; int rc; struct ncp6335d_info *dd = rdev_get_drvdata(rdev); rc = regmap_read(dd->regmap, dd->vsel_reg, &val); if (rc) { dev_err(dd->dev, "Unable to get volatge rc(%d)", rc); return rc; } dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV; dump_registers(dd, dd->vsel_reg, __func__); return dd->curr_voltage; } static int ncp6335d_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { int rc, set_val, new_uV; struct ncp6335d_info *dd = rdev_get_drvdata(rdev); set_val = DIV_ROUND_UP(min_uV - NCP6335D_MIN_VOLTAGE_UV, NCP6335D_STEP_VOLTAGE_UV); new_uV = (set_val * NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV; if (new_uV > max_uV) { dev_err(dd->dev, "Unable to set volatge (%d %d)\n", min_uV, max_uV); return -EINVAL; } rc = regmap_update_bits(dd->regmap, dd->vsel_reg, NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK)); if (rc) { dev_err(dd->dev, "Unable to set volatge (%d %d)\n", min_uV, max_uV); } else { ncp633d_slew_delay(dd, dd->curr_voltage, new_uV); dd->curr_voltage = new_uV; } dump_registers(dd, dd->vsel_reg, __func__); return rc; } static int ncp6335d_set_mode(struct regulator_dev *rdev, unsigned int mode) { int rc; struct ncp6335d_info *dd = rdev_get_drvdata(rdev); /* only FAST and NORMAL mode types are supported */ if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) { dev_err(dd->dev, "Mode %d not supported\n", mode); return -EINVAL; } rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND, dd->mode_bit, (mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0); if (rc) { dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc); return rc; } rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND, NCP6335D_DVS_PWM_MODE, (mode == REGULATOR_MODE_FAST) ? NCP6335D_DVS_PWM_MODE : 0); if (rc) dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc); dump_registers(dd, REG_NCP6335D_COMMAND, __func__); return rc; } static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev) { unsigned int val; int rc; struct ncp6335d_info *dd = rdev_get_drvdata(rdev); rc = regmap_read(dd->regmap, REG_NCP6335D_COMMAND, &val); if (rc) { dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc); return rc; } dump_registers(dd, REG_NCP6335D_COMMAND, __func__); if (val & dd->mode_bit) return REGULATOR_MODE_FAST; return REGULATOR_MODE_NORMAL; } static struct regulator_ops ncp6335d_ops = { .set_voltage = ncp6335d_set_voltage, .get_voltage = ncp6335d_get_voltage, .enable = ncp6335d_enable, .disable = ncp6335d_disable, .set_mode = ncp6335d_set_mode, .get_mode = ncp6335d_get_mode, }; static struct regulator_desc rdesc = { .name = "ncp6335d", .owner = THIS_MODULE, .n_voltages = 128, .ops = &ncp6335d_ops, }; static int __devinit ncp6335d_init(struct ncp6335d_info *dd, const struct ncp6335d_platform_data *pdata) { int rc; unsigned int val; switch (pdata->default_vsel) { case NCP6335D_VSEL0: dd->vsel_reg = REG_NCP6335D_PROGVSEL0; dd->mode_bit = NCP6335D_PWM_MODE0; break; case NCP6335D_VSEL1: dd->vsel_reg = REG_NCP6335D_PROGVSEL1; dd->mode_bit = NCP6335D_PWM_MODE1; break; default: dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel); return -EINVAL; } /* get the current programmed voltage */ rc = regmap_read(dd->regmap, dd->vsel_reg, &val); if (rc) { dev_err(dd->dev, "Unable to get volatge rc(%d)", rc); return rc; } dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV; /* set discharge */ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_PGOOD, NCP6335D_PGOOD_DISCHG, (pdata->discharge_enable ? NCP6335D_PGOOD_DISCHG : 0)); if (rc) { dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc); return -EINVAL; } /* set slew rate */ if (pdata->slew_rate_ns < NCP6335D_MIN_SLEW_NS || pdata->slew_rate_ns > NCP6335D_MAX_SLEW_NS) { dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns); return -EINVAL; } val = DIV_ROUND_UP(pdata->slew_rate_ns - NCP6335D_MIN_SLEW_NS, NCP6335D_MIN_SLEW_NS); val >>= 1; dd->slew_rate = val * NCP6335D_MIN_SLEW_NS; rc = regmap_update_bits(dd->regmap, REG_NCP6335D_TIMING, NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT); if (rc) dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc); /* Set Sleep mode bit */ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND, NCP6335D_SLEEP_MODE, pdata->sleep_enable ? NCP6335D_SLEEP_MODE : 0); if (rc) dev_err(dd->dev, "Unable to set sleep mode (%d)\n", rc); dump_registers(dd, REG_NCP6335D_COMMAND, __func__); dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__); dump_registers(dd, REG_NCP6335D_TIMING, __func__); dump_registers(dd, REG_NCP6335D_PGOOD, __func__); return rc; } static struct regmap_config ncp6335d_regmap_config = { .reg_bits = 8, .val_bits = 8, }; static int __devinit ncp6335d_regulator_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc; unsigned int val = 0; struct ncp6335d_info *dd; const struct ncp6335d_platform_data *pdata; pdata = client->dev.platform_data; if (!pdata) { dev_err(&client->dev, "Platform data not specified\n"); return -EINVAL; } dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL); if (!dd) { dev_err(&client->dev, "Unable to allocate memory\n"); return -ENOMEM; } dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config); if (IS_ERR(dd->regmap)) { dev_err(&client->dev, "Error allocating regmap\n"); return PTR_ERR(dd->regmap); } rc = regmap_read(dd->regmap, REG_NCP6335D_PID, &val); if (rc) { dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n", rc); return rc; } dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val); dd->init_data = pdata->init_data; dd->dev = &client->dev; i2c_set_clientdata(client, dd); rc = ncp6335d_init(dd, pdata); if (rc) { dev_err(&client->dev, "Unable to intialize the regulator\n"); return -EINVAL; } dd->regulator = regulator_register(&rdesc, &client->dev, dd->init_data, dd, NULL); if (IS_ERR(dd->regulator)) { dev_err(&client->dev, "Unable to register regulator rc(%ld)", PTR_ERR(dd->regulator)); return PTR_ERR(dd->regulator); } return 0; } static int __devexit ncp6335d_regulator_remove(struct i2c_client *client) { struct ncp6335d_info *dd = i2c_get_clientdata(client); regulator_unregister(dd->regulator); return 0; } static const struct i2c_device_id ncp6335d_id[] = { {"ncp6335d", -1}, { }, }; static struct i2c_driver ncp6335d_regulator_driver = { .driver = { .name = "ncp6335d-regulator", }, .probe = ncp6335d_regulator_probe, .remove = __devexit_p(ncp6335d_regulator_remove), .id_table = ncp6335d_id, }; static int __init ncp6335d_regulator_init(void) { return i2c_add_driver(&ncp6335d_regulator_driver); } subsys_initcall(ncp6335d_regulator_init); static void __exit ncp6335d_regulator_exit(void) { i2c_del_driver(&ncp6335d_regulator_driver); } module_exit(ncp6335d_regulator_exit); MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver"); MODULE_LICENSE("GPL v2");