M7350/kernel/drivers/regulator/onsemi-ncp6335d.c
2024-09-09 08:52:07 +00:00

394 lines
10 KiB
C

/* 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 <linux/module.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regulator/driver.h>
#include <linux/regmap.h>
#include <linux/regulator/onsemi-ncp6335d.h>
/* 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");