drivers: regulator: Add snapshot of OnSemi NCP6335D regulator

This is snapshot of the OnSemi NCP6335D driver as of msm-3.10
'commit 156ba1726643 ("regulator: onsemi-ncp6335d: Add i2c retry logic")'.

Change-Id: I4cf0acd272fcf498462d4397385cd62f144eadf8
Signed-off-by: Kiran Gunda <kgunda@codeaurora.org>
This commit is contained in:
Anirudh Ghayal 2017-05-02 16:15:27 +05:30 committed by Kiran Gunda
parent f1a10f1598
commit 660dbf1cf4
5 changed files with 880 additions and 0 deletions

View file

@ -0,0 +1,72 @@
ON Semiconductor NCP6335d regulator
NCP6335d is able to deliver up to 5.0 A, with programmable output voltage from
0.6 V to 1.87 V in 10mV steps, with synchronous rectification and automatic PWM/
PFM transitions, enable pins and power good/fail signaling.
The NCP6335d interface is via I2C bus.
Required Properties:
- compatible: Must be "onnn,ncp6335d-regulator".
- reg: The device 8-bit I2C address.
- regulator-min-microvolt: Minimum voltage in microvolts supported by this
regulator.
- regulator-max-microvolt: Maximum voltage in microvolts supported by this
regulator.
- onnn,min-setpoint: Minimum setpoint voltage in microvolts supported
by this regulator.
- onnn,step-size: The step size of the regulator, in uV.
- onnn,min-slew-ns: Minimum time in ns needed to change voltage by
one step size. This value corresponds to DVS
mode bit of 00b in command register.
- onnn,max-slew-ns: Maximum time in ns needed to change voltage by
one step size. This value corresponds to DVS
mode bit of 11b in command register.
- onnn,vsel: Working vsel register. Supported value are 0
or 1.
- onnn,slew-ns: Time in ns needed to change voltage by one step
size. Supported value are 333, 666, 1333, 2666.
Optional Properties:
- onnn,discharge-enable: Present: discharge enabled.
Not Present: discharge disabled.
- onnn,restore-reg: Present: Restore vsel register from backup register.
Not Present: No restore.
- onnn,vsel-gpio: Present: GPIO connects to the VSEL pin and set the
VSEL pin according to device tree flag.
Not Present: No GPIO is connected to vsel pin.
- pinctrl-names: The state name of the VSEL pin configuration.
Only support: "default"
- pinctrl-0: The phandles of the pin configuration node in
pinctrl for VSEL pin.
For details of pinctrl properties, please refer to:
"Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
- onnn,sleep-enable: Present: Forced in sleep mode when EN and VSEL
pins are low.
Not Present: Low quiescent current mode when EN and VSEL
pins are low.
- onnn,mode: A string which specifies the initial mode to use for the regulator.
Supported values are "pwm" and "auto". PWM mode is more
robust, but draws more current than auto mode. If this propery
is not specified, then the regulator will be in the hardware default mode.
Example:
i2c_0 {
ncp6335d-regulator@1c {
compatible = "onnn,ncp6335d-regulator";
reg = <0x1c>;
onnn,vsel = <0>;
onnn,slew-rate-ns = <2666>;
onnn,discharge-enable;
onnn,step-size = <10000>;
onnn,min-slew-ns = <333>;
onnn,max-slew-ns = <2666>;
pintrl-names = "default";
pinctrl-0 = <&ext_buck_vsel_default>;
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1350000>;
onnn,min-setpoint = <600000>;
onnn,vsel-gpio = <&msmgpio 2 1>;
};
};

View file

@ -472,6 +472,15 @@ config REGULATOR_MT6397
This driver supports the control of different power rails of device
through regulator interface.
config REGULATOR_ONSEMI_NCP6335D
tristate "OnSemi NCP6335D regulator support"
depends on I2C
help
This driver supports the OnSemi NCP6335D switching voltage regulator
(buck converter). The regulator is controlled using an I2C interface
and supports a programmable voltage range from 0.6V to 1.4V in steps
of 6.25mV.
config REGULATOR_PALMAS
tristate "TI Palmas PMIC Regulators"
depends on MFD_PALMAS

View file

@ -65,6 +65,7 @@ obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o

View file

@ -0,0 +1,763 @@
/* Copyright (c) 2012-2014, 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/gpio.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/debugfs.h>
#include <linux/regulator/onsemi-ncp6335d.h>
#include <linux/string.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_VOLTAGE_STEPS 128
#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 vsel_backup_reg;
unsigned int mode_bit;
int curr_voltage;
int slew_rate;
unsigned int step_size;
unsigned int min_voltage;
unsigned int min_slew_ns;
unsigned int max_slew_ns;
unsigned int peek_poke_address;
struct dentry *debug_root;
};
static int delay_array[] = {10, 20, 30, 40, 50};
static int ncp6335x_read(struct ncp6335d_info *dd, unsigned int reg,
unsigned int *val)
{
int i = 0, rc = 0;
rc = regmap_read(dd->regmap, reg, val);
for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) {
pr_debug("Failed reading reg=%u - retry(%d)\n", reg, i);
msleep(delay_array[i]);
rc = regmap_read(dd->regmap, reg, val);
}
if (rc)
pr_err("Failed reading reg=%u rc=%d\n", reg, rc);
return rc;
}
static int ncp6335x_write(struct ncp6335d_info *dd, unsigned int reg,
unsigned int val)
{
int i = 0, rc = 0;
rc = regmap_write(dd->regmap, reg, val);
for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) {
pr_debug("Failed writing reg=%u - retry(%d)\n", reg, i);
msleep(delay_array[i]);
rc = regmap_write(dd->regmap, reg, val);
}
if (rc)
pr_err("Failed writing reg=%u rc=%d\n", reg, rc);
return rc;
}
static int ncp6335x_update_bits(struct ncp6335d_info *dd, unsigned int reg,
unsigned int mask, unsigned int val)
{
int i = 0, rc = 0;
rc = regmap_update_bits(dd->regmap, reg, mask, val);
for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) {
pr_debug("Failed updating reg=%u- retry(%d)\n", reg, i);
msleep(delay_array[i]);
rc = regmap_update_bits(dd->regmap, reg, mask, val);
}
if (rc)
pr_err("Failed updating reg=%u rc=%d\n", reg, rc);
return rc;
}
static void dump_registers(struct ncp6335d_info *dd,
unsigned int reg, const char *func)
{
unsigned int val = 0;
ncp6335x_read(dd, 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) / dd->step_size;
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 = ncp6335x_update_bits(dd, 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 = ncp6335x_update_bits(dd, 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 = ncp6335x_read(dd, 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) * dd->step_size) +
dd->min_voltage;
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 - dd->min_voltage, dd->step_size);
new_uV = (set_val * dd->step_size) + dd->min_voltage;
if (new_uV > max_uV) {
dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
min_uV, max_uV);
return -EINVAL;
}
rc = ncp6335x_update_bits(dd, 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_list_voltage(struct regulator_dev *rdev,
unsigned selector)
{
struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
if (selector >= NCP6335D_VOLTAGE_STEPS)
return 0;
return selector * dd->step_size + dd->min_voltage;
}
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 = ncp6335x_update_bits(dd, 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 = ncp6335x_update_bits(dd, 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 = ncp6335x_read(dd, 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,
.list_voltage = ncp6335d_list_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 = NCP6335D_VOLTAGE_STEPS,
.ops = &ncp6335d_ops,
};
static int ncp6335d_restore_working_reg(struct device_node *node,
struct ncp6335d_info *dd)
{
int ret;
unsigned int val;
/* Restore register from back up register */
ret = ncp6335x_read(dd, dd->vsel_backup_reg, &val);
if (ret < 0) {
dev_err(dd->dev, "Failed to get backup data from reg %d, ret = %d\n",
dd->vsel_backup_reg, ret);
return ret;
}
ret = ncp6335x_update_bits(dd, dd->vsel_reg,
NCP6335D_VOUT_SEL_MASK, val);
if (ret < 0) {
dev_err(dd->dev, "Failed to update working reg %d, ret = %d\n",
dd->vsel_reg, ret);
return ret;
}
return ret;
}
static int ncp6335d_parse_gpio(struct device_node *node,
struct ncp6335d_info *dd)
{
int ret = 0, gpio;
enum of_gpio_flags flags;
if (!of_find_property(node, "onnn,vsel-gpio", NULL))
return ret;
/* Get GPIO connected to vsel and set its output */
gpio = of_get_named_gpio_flags(node,
"onnn,vsel-gpio", 0, &flags);
if (!gpio_is_valid(gpio)) {
if (gpio != -EPROBE_DEFER)
dev_err(dd->dev, "Could not get vsel, ret = %d\n",
gpio);
return gpio;
}
ret = devm_gpio_request(dd->dev, gpio, "ncp6335d_vsel");
if (ret) {
dev_err(dd->dev, "Failed to obtain gpio %d ret = %d\n",
gpio, ret);
return ret;
}
ret = gpio_direction_output(gpio, flags & OF_GPIO_ACTIVE_LOW ? 0 : 1);
if (ret) {
dev_err(dd->dev, "Failed to set GPIO %d to: %s, ret = %d",
gpio, flags & OF_GPIO_ACTIVE_LOW ?
"GPIO_LOW" : "GPIO_HIGH", ret);
return ret;
}
return ret;
}
static int ncp6335d_init(struct i2c_client *client, 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->vsel_backup_reg = REG_NCP6335D_PROGVSEL1;
dd->mode_bit = NCP6335D_PWM_MODE0;
break;
case NCP6335D_VSEL1:
dd->vsel_reg = REG_NCP6335D_PROGVSEL1;
dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL0;
dd->mode_bit = NCP6335D_PWM_MODE1;
break;
default:
dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel);
return -EINVAL;
}
if (of_property_read_bool(client->dev.of_node, "onnn,restore-reg")) {
rc = ncp6335d_restore_working_reg(client->dev.of_node, dd);
if (rc)
return rc;
}
rc = ncp6335d_parse_gpio(client->dev.of_node, dd);
if (rc)
return rc;
/* get the current programmed voltage */
rc = ncp6335x_read(dd, 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) *
dd->step_size) + dd->min_voltage;
/* set discharge */
rc = ncp6335x_update_bits(dd, 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 < dd->min_slew_ns ||
pdata->slew_rate_ns > dd->max_slew_ns) {
dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns);
return -EINVAL;
}
dd->slew_rate = pdata->slew_rate_ns;
val = DIV_ROUND_UP(pdata->slew_rate_ns, dd->min_slew_ns);
val = ilog2(val);
rc = ncp6335x_update_bits(dd, 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 = ncp6335x_update_bits(dd, 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 ncp6335d_parse_dt(struct i2c_client *client,
struct ncp6335d_info *dd)
{
int rc;
rc = of_property_read_u32(client->dev.of_node,
"onnn,step-size", &dd->step_size);
if (rc < 0) {
dev_err(&client->dev, "step size missing: rc = %d.\n", rc);
return rc;
}
rc = of_property_read_u32(client->dev.of_node,
"onnn,min-slew-ns", &dd->min_slew_ns);
if (rc < 0) {
dev_err(&client->dev, "min slew us missing: rc = %d.\n", rc);
return rc;
}
rc = of_property_read_u32(client->dev.of_node,
"onnn,max-slew-ns", &dd->max_slew_ns);
if (rc < 0) {
dev_err(&client->dev, "max slew us missing: rc = %d.\n", rc);
return rc;
}
rc = of_property_read_u32(client->dev.of_node,
"onnn,min-setpoint", &dd->min_voltage);
if (rc < 0) {
dev_err(&client->dev, "min set point missing: rc = %d.\n", rc);
return rc;
}
return rc;
}
static struct ncp6335d_platform_data *
ncp6335d_get_of_platform_data(struct i2c_client *client)
{
struct ncp6335d_platform_data *pdata = NULL;
struct regulator_init_data *init_data;
const char *mode_name;
int rc;
init_data = of_get_regulator_init_data(&client->dev,
client->dev.of_node);
if (!init_data) {
dev_err(&client->dev, "regulator init data is missing\n");
return pdata;
}
pdata = devm_kzalloc(&client->dev,
sizeof(struct ncp6335d_platform_data), GFP_KERNEL);
if (!pdata) {
dev_err(&client->dev, "ncp6335d_platform_data allocation failed.\n");
return pdata;
}
rc = of_property_read_u32(client->dev.of_node,
"onnn,vsel", &pdata->default_vsel);
if (rc < 0) {
dev_err(&client->dev, "onnn,vsel property missing: rc = %d.\n",
rc);
return NULL;
}
rc = of_property_read_u32(client->dev.of_node,
"onnn,slew-ns", &pdata->slew_rate_ns);
if (rc < 0) {
dev_err(&client->dev, "onnn,slew-ns property missing: rc = %d.\n",
rc);
return NULL;
}
pdata->discharge_enable = of_property_read_bool(client->dev.of_node,
"onnn,discharge-enable");
pdata->sleep_enable = of_property_read_bool(client->dev.of_node,
"onnn,sleep-enable");
pdata->init_data = init_data;
init_data->constraints.input_uV = init_data->constraints.max_uV;
init_data->constraints.valid_ops_mask =
REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS |
REGULATOR_CHANGE_MODE;
init_data->constraints.valid_modes_mask =
REGULATOR_MODE_NORMAL |
REGULATOR_MODE_FAST;
rc = of_property_read_string(client->dev.of_node, "onnn,mode",
&mode_name);
if (!rc) {
if (strcmp("pwm", mode_name) == 0) {
init_data->constraints.initial_mode =
REGULATOR_MODE_FAST;
} else if (strcmp("auto", mode_name) == 0) {
init_data->constraints.initial_mode =
REGULATOR_MODE_NORMAL;
} else {
dev_err(&client->dev, "onnn,mode, unknown regulator mode: %s\n",
mode_name);
return NULL;
}
}
return pdata;
}
static int get_reg(void *data, u64 *val)
{
struct ncp6335d_info *dd = data;
int rc;
unsigned int temp = 0;
rc = ncp6335x_read(dd, dd->peek_poke_address, &temp);
if (rc < 0)
dev_err(dd->dev, "Couldn't read reg %x rc = %d\n",
dd->peek_poke_address, rc);
else
*val = temp;
return rc;
}
static int set_reg(void *data, u64 val)
{
struct ncp6335d_info *dd = data;
int rc;
unsigned int temp = 0;
temp = (unsigned int) val;
rc = ncp6335x_write(dd, dd->peek_poke_address, temp);
if (rc < 0)
dev_err(dd->dev, "Couldn't write 0x%02x to 0x%02x rc= %d\n",
dd->peek_poke_address, temp, rc);
return rc;
}
DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
static int 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;
struct regulator_config config = { };
if (client->dev.of_node)
pdata = ncp6335d_get_of_platform_data(client);
else
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;
}
if (client->dev.of_node) {
rc = ncp6335d_parse_dt(client, dd);
if (rc)
return rc;
} else {
dd->step_size = NCP6335D_STEP_VOLTAGE_UV;
dd->min_voltage = NCP6335D_MIN_VOLTAGE_UV;
dd->min_slew_ns = NCP6335D_MIN_SLEW_NS;
dd->max_slew_ns = NCP6335D_MAX_SLEW_NS;
}
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 = ncp6335x_read(dd, 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(client, dd, pdata);
if (rc) {
dev_err(&client->dev, "Unable to intialize the regulator\n");
return -EINVAL;
}
config.dev = &client->dev;
config.init_data = dd->init_data;
config.regmap = dd->regmap;
config.driver_data = dd;
config.of_node = client->dev.of_node;
dd->regulator = regulator_register(&rdesc, &config);
if (IS_ERR(dd->regulator)) {
dev_err(&client->dev, "Unable to register regulator rc(%ld)",
PTR_ERR(dd->regulator));
return PTR_ERR(dd->regulator);
}
dd->debug_root = debugfs_create_dir("ncp6335x", NULL);
if (!dd->debug_root)
dev_err(&client->dev, "Couldn't create debug dir\n");
if (dd->debug_root) {
struct dentry *ent;
ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO,
dd->debug_root,
&(dd->peek_poke_address));
if (!ent)
dev_err(&client->dev, "Couldn't create address debug file rc = %d\n",
rc);
ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO,
dd->debug_root, dd,
&poke_poke_debug_ops);
if (!ent)
dev_err(&client->dev, "Couldn't create data debug file rc = %d\n",
rc);
}
return 0;
}
static int ncp6335d_regulator_remove(struct i2c_client *client)
{
struct ncp6335d_info *dd = i2c_get_clientdata(client);
regulator_unregister(dd->regulator);
debugfs_remove_recursive(dd->debug_root);
return 0;
}
static struct of_device_id ncp6335d_match_table[] = {
{ .compatible = "onnn,ncp6335d-regulator", },
{},
};
MODULE_DEVICE_TABLE(of, ncp6335d_match_table);
static const struct i2c_device_id ncp6335d_id[] = {
{"ncp6335d", -1},
{ },
};
static struct i2c_driver ncp6335d_regulator_driver = {
.driver = {
.name = "ncp6335d-regulator",
.owner = THIS_MODULE,
.of_match_table = ncp6335d_match_table,
},
.probe = ncp6335d_regulator_probe,
.remove = ncp6335d_regulator_remove,
.id_table = ncp6335d_id,
};
/**
* ncp6335d_regulator_init() - initialized ncp6335d regulator driver
* This function registers the ncp6335d regulator platform driver.
*
* Returns 0 on success or errno on failure.
*/
int __init ncp6335d_regulator_init(void)
{
static bool initialized;
if (initialized)
return 0;
else
initialized = true;
return i2c_add_driver(&ncp6335d_regulator_driver);
}
EXPORT_SYMBOL(ncp6335d_regulator_init);
arch_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");

View file

@ -0,0 +1,35 @@
/* Copyright (c) 2012-2014, 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.
*/
#ifndef __NCP6335D_H__
#define __NCP6335D_H__
enum {
NCP6335D_VSEL0,
NCP6335D_VSEL1,
};
struct ncp6335d_platform_data {
struct regulator_init_data *init_data;
int default_vsel;
int slew_rate_ns;
int discharge_enable;
bool sleep_enable;
};
#ifdef CONFIG_REGULATOR_ONSEMI_NCP6335D
int __init ncp6335d_regulator_init(void);
#else
static inline int __init ncp6335d_regulator_init(void) { return 0; }
#endif
#endif