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:
parent
f1a10f1598
commit
660dbf1cf4
5 changed files with 880 additions and 0 deletions
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
763
drivers/regulator/onsemi-ncp6335d.c
Normal file
763
drivers/regulator/onsemi-ncp6335d.c
Normal 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");
|
35
include/linux/regulator/onsemi-ncp6335d.h
Normal file
35
include/linux/regulator/onsemi-ncp6335d.h
Normal 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
|
Loading…
Add table
Reference in a new issue