[PATCH 1/2] mfd: bq2415x charger driver

From: aliaksei . katovich
Date: Wed Mar 09 2011 - 09:49:03 EST


From: Aliaksei Katovich <aliaksei.katovich@xxxxxxxxx>

Added driver to support TI bq24153/6 one-cell Li-Ion chargers.

Signed-off-by: Aliaksei Katovich <aliaksei.katovich@xxxxxxxxx>
---
drivers/mfd/bq2415x.c | 874 +++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/bq2415x.h | 88 +++++
2 files changed, 962 insertions(+), 0 deletions(-)
create mode 100644 drivers/mfd/bq2415x.c
create mode 100644 include/linux/mfd/bq2415x.h

diff --git a/drivers/mfd/bq2415x.c b/drivers/mfd/bq2415x.c
new file mode 100644
index 0000000..58dad1d
--- /dev/null
+++ b/drivers/mfd/bq2415x.c
@@ -0,0 +1,874 @@
+/*
+ * bq2415x.c - TI BQ24153/6 one-cell Li-Ion charger driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Aliaksei Katovich <aliaksei.katovich@xxxxxxxxx>
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * version 2 of that License.
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/bq2415x.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#define BQ2415X_RESET_MASK 0x80
+
+#define BQ2415X_IIN_LIMIT_500_MASK 0x40
+#define BQ2415X_IIN_LIMIT_800_MASK 0x80
+#define BQ2415X_IIN_LIMIT_UNLIM_MASK 0xc0
+
+static DEFINE_MUTEX(bq2415x_dev_list_mutex);
+static LIST_HEAD(bq2415x_dev_list);
+
+static struct i2c_client *bq2415x_cli;
+
+struct bq2415x_regulator {
+ struct i2c_client *cli;
+ u8 reg; /* associated register */
+ u8 val_summ; /* value summand */
+ u8 msk_summ; /* bit mask summand */
+ u8 msk_mult; /* bit mask multiplier */
+ u8 min_mask;
+ u8 max_mask; /* read bitmask */
+ bool enabled;
+ struct regulator_init_data init;
+ struct regulator_desc desc;
+};
+
+struct bq2415x_device {
+ struct i2c_client *cli;
+ struct regulator_dev *rdev[BQ2415X_MAX_REG];
+ struct list_head list;
+};
+
+/* debugfs operations */
+
+#define BQ2415X_MAX_REGS 7
+
+static int bq2415x_show(struct seq_file *s, void *unused)
+{
+ struct i2c_client *cli = s->private;
+ u8 buf[BQ2415X_MAX_REGS], i;
+
+ if (i2c_smbus_read_i2c_block_data(cli, BQ2415X_STS_CTL,
+ BQ2415X_MAX_REGS, buf) < 0)
+ return 0;
+
+ for (i = 0; i < BQ2415X_MAX_REGS; i++)
+ seq_printf(s, "%02x ", buf[i]);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int bq2415x_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bq2415x_show, inode->i_private);
+}
+
+static const struct file_operations bq2415x_ops = {
+ .open = bq2415x_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void bq2415x_debugfs_init(struct i2c_client *cli)
+{
+ struct dentry *root;
+ struct dentry *file;
+
+ root = debugfs_create_dir("bq2415x", NULL);
+ if (IS_ERR(root))
+ return;
+
+ file = debugfs_create_file("regdump", 0444, root, cli, &bq2415x_ops);
+ if (IS_ERR(file))
+ return;
+}
+
+/* i2c operations */
+
+static inline int bq2415x_i2c_read(struct i2c_client *cli, u8 reg)
+{
+ return i2c_smbus_read_byte_data(cli, reg);
+}
+
+static inline int bq2415x_i2c_write(struct i2c_client *cli, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(cli, reg, val);
+}
+
+static inline int bq2415x_enable_charger(bool flag)
+{
+ int res;
+
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_GEN_CTL);
+ if (unlikely(res < 0))
+ return res;
+
+ if (flag)
+ res &= ~(BQ2415X_CE | BQ2415X_HZ_MODE);
+ else
+ res |= (BQ2415X_CE | BQ2415X_HZ_MODE);
+
+ return bq2415x_i2c_write(bq2415x_cli, BQ2415X_GEN_CTL, res);
+}
+
+static inline int bq2415x_enable_stat(bool flag)
+{
+ int res;
+
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_STS_CTL);
+ if (unlikely(res < 0))
+ return res;
+
+ if (flag)
+ res |= BQ2415X_EN_STAT;
+ else
+ res &= ~BQ2415X_EN_STAT;
+
+ return bq2415x_i2c_write(bq2415x_cli, BQ2415X_STS_CTL, res);
+}
+
+static inline int bq2415x_enable_otg(bool flag)
+{
+ int res;
+
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_BAT_CTL);
+ if (unlikely(res < 0))
+ return res;
+
+ if (flag)
+ res |= BQ2415X_OTG_EN;
+ else
+ res &= ~BQ2415X_OTG_EN;
+
+ return bq2415x_i2c_write(bq2415x_cli, BQ2415X_STS_CTL, res);
+}
+
+static inline int bq2415x_otg_high(bool flag)
+{
+ int res;
+
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_BAT_CTL);
+ if (unlikely(res < 0))
+ return res;
+
+ if (flag)
+ res |= BQ2415X_OTG_PL;
+ else
+ res &= ~BQ2415X_OTG_PL;
+
+ return bq2415x_i2c_write(bq2415x_cli, BQ2415X_STS_CTL, res);
+}
+
+static inline int bq2415x_chg_low(bool flag)
+{
+ int res;
+
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_SPC_CTL);
+ if (unlikely(res < 0))
+ return res;
+
+ if (flag)
+ res |= BQ2415X_LOW_CHG;
+ else
+ res &= ~BQ2415X_LOW_CHG;
+
+ return bq2415x_i2c_write(bq2415x_cli, BQ2415X_SPC_CTL, res);
+}
+
+/**
+ * bq2415x_exec - execute charger specific command
+ *
+ * Returns result of command execution upon success or negative otherwise
+ */
+int bq2415x_exec(enum bq2415x_cmd cmd)
+{
+ int res;
+
+ if (!bq2415x_cli)
+ return -ENODEV;
+
+ switch (cmd) {
+ case BQ2415X_ENABLE_CHARGER:
+ res = bq2415x_enable_charger(true);
+ break;
+ case BQ2415X_DISABLE_CHARGER:
+ res = bq2415x_enable_charger(false);
+ break;
+ case BQ2415X_READ_STATUS:
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_STS_CTL);
+ break;
+ case BQ2415X_RESET_CHARGER:
+ res = bq2415x_i2c_write(bq2415x_cli, BQ2415X_CHG_CTL,
+ BQ2415X_RESET_MASK);
+ break;
+ case BQ2415X_RESET_WATCHDOG:
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_STS_CTL);
+ if (unlikely(res < 0))
+ break;
+
+ res |= BQ2415X_TMR_RST;
+ res = bq2415x_i2c_write(bq2415x_cli, BQ2415X_STS_CTL, res);
+ break;
+ case BQ2415X_ENABLE_STAT:
+ res = bq2415x_enable_stat(true);
+ break;
+ case BQ2415X_DISABLE_STAT:
+ res = bq2415x_enable_stat(false);
+ break;
+ case BQ2415X_ENABLE_OTG:
+ res = bq2415x_enable_otg(true);
+ break;
+ case BQ2415X_DISABLE_OTG:
+ res = bq2415x_enable_otg(false);
+ break;
+ case BQ2415X_OTG_HIGH:
+ res = bq2415x_otg_high(true);
+ break;
+ case BQ2415X_OTG_LOW:
+ res = bq2415x_otg_high(false);
+ break;
+ case BQ2415X_CHG_LOW:
+ res = bq2415x_chg_low(true);
+ break;
+ case BQ2415X_CHG_NORM:
+ res = bq2415x_chg_low(false);
+ break;
+ case BQ2415X_READ_DPM:
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_SPC_CTL);
+ if (res & BQ2415X_DPM_STATUS)
+ res = 1; /* DPM mode is active */
+ else
+ res = 0;
+ break;
+ case BQ2415X_READ_CD:
+ res = bq2415x_i2c_read(bq2415x_cli, BQ2415X_SPC_CTL);
+ if (res & BQ2415X_CD_STATUS)
+ res = 1; /* CD ping at HIGH level */
+ else
+ res = 0;
+ break;
+ default:
+ res = -ENOSYS;
+ }
+
+ return res;
+}
+EXPORT_SYMBOL(bq2415x_exec);
+
+static inline int bq2415x_vmap_index(unsigned val, unsigned div)
+{
+ if (val == 0)
+ return 0;
+ return (val / div) - 1;
+}
+
+static inline int bq2415x_vmap_calc(unsigned i, unsigned summ, unsigned mult)
+{
+ return (i + summ) * mult;
+}
+
+/* regulator operations */
+
+static int bq2415x_list_voltage(struct regulator_dev *rdev, unsigned i)
+{
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ if (i >= r->desc.n_voltages)
+ return 0;
+
+ return bq2415x_vmap_calc(i, r->val_summ, r->init.constraints.min_uV);
+}
+
+static int
+bq2415x_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
+{
+ int i, res;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ i = bq2415x_vmap_index(max_uV, r->init.constraints.min_uV);
+ if (i >= r->desc.n_voltages)
+ return -EINVAL;
+
+ res = bq2415x_i2c_read(r->cli, r->reg);
+ if (res < 0)
+ return res;
+
+ res |= bq2415x_vmap_calc(i, r->msk_summ, r->msk_mult);
+ return bq2415x_i2c_write(r->cli, r->reg, res);
+}
+
+static int bq2415x_get_voltage(struct regulator_dev *rdev)
+{
+ int i, ret;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ ret = bq2415x_i2c_read(r->cli, r->reg);
+ if (ret < 0)
+ return ret;
+
+ i = bq2415x_vmap_index(ret & r->max_mask, r->min_mask);
+ if (i >= r->desc.n_voltages)
+ return -EINVAL;
+
+ return bq2415x_vmap_calc(i, r->val_summ, r->init.constraints.min_uV);
+}
+
+static int
+bq2415x_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA)
+{
+ int res;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ res = bq2415x_i2c_read(r->cli, r->reg);
+ if (res < 0)
+ return res;
+
+ if (BQ2415X_IIN_LIMIT_100 <= min_uA && max_uA < BQ2415X_IIN_LIMIT_500)
+ res &= ~BQ2415X_IIN_LIMIT_UNLIM_MASK;
+ else if (BQ2415X_IIN_LIMIT_500 <= min_uA &&
+ max_uA < BQ2415X_IIN_LIMIT_800)
+ res |= BQ2415X_IIN_LIMIT_500_MASK;
+ else if (BQ2415X_IIN_LIMIT_800 <= min_uA &&
+ max_uA < BQ2415X_IIN_LIMIT_UNLIM)
+ res |= BQ2415X_IIN_LIMIT_800_MASK;
+ else if (BQ2415X_IIN_LIMIT_UNLIM <= min_uA)
+ res |= BQ2415X_IIN_LIMIT_UNLIM_MASK;
+ else
+ return -EINVAL;
+
+ return bq2415x_i2c_write(r->cli, r->reg, res);
+}
+
+static int bq2415x_get_current_limit(struct regulator_dev *rdev)
+{
+ int ret;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ ret = bq2415x_i2c_read(r->cli, r->reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= BQ2415X_IIN_LIMIT_UNLIM_MASK;
+ if (ret == BQ2415X_IIN_LIMIT_UNLIM_MASK)
+ ret = BQ2415X_IIN_LIMIT_UNLIM;
+ else if (ret == BQ2415X_IIN_LIMIT_500_MASK)
+ ret = BQ2415X_IIN_LIMIT_500;
+ else if (ret == BQ2415X_IIN_LIMIT_800_MASK)
+ ret = BQ2415X_IIN_LIMIT_800;
+ else
+ ret = BQ2415X_IIN_LIMIT_100;
+
+ return ret;
+}
+
+static int bq2415x_v_iterm_enable(struct regulator_dev *rdev)
+{
+ int val;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ val = bq2415x_i2c_read(r->cli, BQ2415X_GEN_CTL);
+ if (val < 0)
+ return val;
+
+ val |= BQ2415X_TE;
+ return bq2415x_i2c_write(r->cli, BQ2415X_GEN_CTL, val);
+}
+
+static int bq2415x_v_iterm_disable(struct regulator_dev *rdev)
+{
+ int val;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ val = bq2415x_i2c_read(r->cli, BQ2415X_GEN_CTL);
+ if (val < 0)
+ return val;
+
+ val &= ~BQ2415X_TE;
+ return bq2415x_i2c_write(r->cli, BQ2415X_GEN_CTL, val);
+}
+
+static int bq2415x_v_iterm_is_enabled(struct regulator_dev *rdev)
+{
+ int val;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ val = bq2415x_i2c_read(r->cli, BQ2415X_GEN_CTL);
+ if (val < 0)
+ return val;
+
+ return val & BQ2415X_TE;
+}
+
+static int bq2415x_v_ichrg_enable(struct regulator_dev *rdev)
+{
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ r->enabled = true;
+ return 0;
+}
+
+static int bq2415x_v_ichrg_disable(struct regulator_dev *rdev)
+{
+ int val;
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ val = bq2415x_i2c_read(r->cli, r->reg);
+ if (val < 0)
+ return val;
+
+ val &= 0x0f;
+ r->enabled = false;
+ return bq2415x_i2c_write(r->cli, r->reg, val);
+}
+
+static int bq2415x_v_ichrg_is_enabled(struct regulator_dev *rdev)
+{
+ struct bq2415x_regulator *r = rdev_get_drvdata(rdev);
+
+ return r->enabled;
+}
+
+/* regulator initialization */
+
+static struct regulator_ops bq2415x_c_basic_ops = {
+ .set_current_limit = bq2415x_set_current_limit,
+ .get_current_limit = bq2415x_get_current_limit,
+};
+
+static struct regulator_ops bq2415x_v_basic_ops = {
+ .list_voltage = bq2415x_list_voltage,
+ .set_voltage = bq2415x_set_voltage,
+ .get_voltage = bq2415x_get_voltage,
+};
+
+static struct regulator_ops bq2415x_v_iterm_ops = {
+ .list_voltage = bq2415x_list_voltage,
+ .set_voltage = bq2415x_set_voltage,
+ .get_voltage = bq2415x_get_voltage,
+ .enable = bq2415x_v_iterm_enable,
+ .disable = bq2415x_v_iterm_disable,
+ .is_enabled = bq2415x_v_iterm_is_enabled,
+};
+
+static struct regulator_ops bq2415x_v_ichrg_ops = {
+ .list_voltage = bq2415x_list_voltage,
+ .set_voltage = bq2415x_set_voltage,
+ .get_voltage = bq2415x_get_voltage,
+ .enable = bq2415x_v_ichrg_enable,
+ .disable = bq2415x_v_ichrg_disable,
+ .is_enabled = bq2415x_v_ichrg_is_enabled,
+};
+
+static struct bq2415x_regulator bq2415x_regulator[] = {
+ [BQ2415X_V_LOWV] = { /* weak battery voltage */
+ .reg = BQ2415X_GEN_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 16,
+ .min_mask = 0x10,
+ .max_mask = 0x30,
+ .init.constraints = {
+ .name = "V_LOWV",
+ .min_uV = 100000,
+ .max_uV = 300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .desc = {
+ .name = "V_LOWV",
+ .id = BQ2415X_V_LOWV,
+ .n_voltages = 3,
+ .ops = &bq2415x_v_basic_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_OREG] = { /* battery regulation */
+ .reg = BQ2415X_BAT_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 4,
+ .min_mask = 0x04,
+ .max_mask = 0xfc,
+ .init.constraints = {
+ .name = "V_OREG",
+ .min_uV = 20000,
+ .max_uV = 1260000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .desc = {
+ .name = "V_OREG",
+ .id = BQ2415X_V_OREG,
+ .n_voltages = 63,
+ .ops = &bq2415x_v_basic_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_ICHRG_VAC] = { /* charge current sense (VAC) */
+ .reg = BQ2415X_CHG_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 8,
+ .min_mask = 0x08,
+ .max_mask = 0x78,
+ .enabled = false,
+ .init.constraints = {
+ .name = "V_ICHRG_VAC",
+ .min_uV = 6800,
+ .max_uV = 102000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ .always_on = 0,
+ .boot_on = 0,
+ },
+ .desc = {
+ .name = "V_ICHRG_VAC",
+ .id = BQ2415X_V_ICHRG_VAC,
+ .n_voltages = 15,
+ .ops = &bq2415x_v_ichrg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_ICHRG_USB] = { /* charge current sense (USB) */
+ .reg = BQ2415X_CHG_CTL,
+ .val_summ = 1,
+ .msk_summ = 2,
+ .msk_mult = 8,
+ .min_mask = 0x10,
+ .max_mask = 0x40,
+ .enabled = false,
+ .init.constraints = {
+ .name = "V_ICHRG_USB",
+ .min_uV = 6800,
+ .max_uV = 47600,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ .always_on = 0,
+ .boot_on = 0,
+ },
+ .desc = {
+ .name = "V_ICHRG_USB",
+ .id = BQ2415X_V_ICHRG_USB,
+ .n_voltages = 7,
+ .ops = &bq2415x_v_ichrg_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_ITERM] = { /* termination current sense */
+ .reg = BQ2415X_CHG_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 1,
+ .min_mask = 0x01,
+ .max_mask = 0x07,
+ .init.constraints = {
+ .name = "V_ITERM",
+ .min_uV = 3400,
+ .max_uV = 23800,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ .always_on = 0,
+ .boot_on = 0,
+ },
+ .desc = {
+ .name = "V_ITERM",
+ .id = BQ2415X_V_ITERM,
+ .n_voltages = 7,
+ .ops = &bq2415x_v_iterm_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_SREG] = { /* special charger */
+ .reg = BQ2415X_SPC_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 1,
+ .min_mask = 0x01,
+ .max_mask = 0x07,
+ .init.constraints = {
+ .name = "V_SREG",
+ .min_uV = 80000,
+ .max_uV = 560000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .desc = {
+ .name = "V_SREG",
+ .id = BQ2415X_V_SREG,
+ .n_voltages = 7,
+ .ops = &bq2415x_v_basic_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_MCHRG] = { /* maximum charge current */
+ .reg = BQ2415X_SFT_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 16,
+ .min_mask = 0x10,
+ .max_mask = 0xf0,
+ .init.constraints = {
+ .name = "V_MCHRG",
+ .min_uV = 6800,
+ .max_uV = 102000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .desc = {
+ .name = "V_MCHRG",
+ .id = BQ2415X_V_MCHRG,
+ .n_voltages = 15,
+ .ops = &bq2415x_v_basic_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_V_MREG] = { /* maximum battery regulation */
+ .reg = BQ2415X_SFT_CTL,
+ .val_summ = 1,
+ .msk_summ = 1,
+ .msk_mult = 1,
+ .min_mask = 0x01,
+ .max_mask = 0x0f,
+ .init.constraints = {
+ .name = "V_MREG",
+ .min_uV = 20000,
+ .max_uV = 300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .desc = {
+ .name = "V_MREG",
+ .id = BQ2415X_V_MREG,
+ .n_voltages = 15,
+ .ops = &bq2415x_v_basic_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [BQ2415X_I_INLIMIT] = { /* input current limit */
+ .reg = BQ2415X_GEN_CTL,
+ .init.constraints = {
+ .name = "I_INLIMIT",
+ .min_uA = 100000,
+ .max_uA = 1500000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_CURRENT,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .desc = {
+ .name = "I_INLIMIT",
+ .id = BQ2415X_I_INLIMIT,
+ .n_voltages = 4,
+ .ops = &bq2415x_c_basic_ops,
+ .type = REGULATOR_CURRENT,
+ .owner = THIS_MODULE,
+ },
+ },
+};
+
+static void __init bq2415x_add_consumers(struct bq2415x_regulator *r,
+ struct regulator_init_data *init)
+{
+ int i, j, n;
+ struct regulator_consumer_supply *cons;
+
+ /* count my consumers */
+ for (i = 0, n = 0; i < init->num_consumer_supplies; i++) {
+ if (strcmp(init->consumer_supplies[i].supply,
+ r->init.constraints.name) == 0)
+ n++;
+ }
+
+ cons = kzalloc(sizeof(*cons) * n, GFP_KERNEL);
+ if (!cons)
+ return;
+
+ /* populate my consumers table */
+ for (i = 0, j = 0; i < init->num_consumer_supplies && j < n; i++) {
+ if (!init->consumer_supplies[i].supply)
+ continue;
+ if (strcmp(init->consumer_supplies[i].supply,
+ r->init.constraints.name) != 0)
+ continue;
+
+ cons[j].supply = init->consumer_supplies[i].supply;
+
+ if (init->consumer_supplies[i].dev)
+ cons[j].dev = init->consumer_supplies[i].dev;
+ if (init->consumer_supplies[i].dev_name)
+ cons[j].dev_name = init->consumer_supplies[i].dev_name;
+
+ j++;
+ }
+
+ r->init.num_consumer_supplies = n;
+ r->init.consumer_supplies = cons;
+}
+
+static int __devinit bq2415x_regulator_init(struct bq2415x_device *bq)
+{
+ int i;
+ struct device *dev = &bq->cli->dev;
+ struct bq2415x_regulator *r;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init = dev->platform_data;
+
+ for (i = 0; i < ARRAY_SIZE(bq2415x_regulator); i++) {
+ r = &bq2415x_regulator[i];
+ r->cli = bq->cli;
+
+ if (init)
+ bq2415x_add_consumers(r, init);
+
+ rdev = regulator_register(&r->desc, dev, &r->init, r);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+
+ bq->rdev[i] = rdev;
+ }
+
+ return 0;
+}
+
+/* driver initialization */
+
+static struct bq2415x_device *bq2415x_getdev(struct i2c_client *cli)
+{
+ struct bq2415x_device *pos, *dev = 0;
+
+ mutex_lock(&bq2415x_dev_list_mutex);
+ list_for_each_entry(pos, &bq2415x_dev_list, list) {
+ if (pos->cli == cli ||
+ (pos->cli->addr == cli->addr &&
+ pos->cli->adapter == cli->adapter)) {
+ dev = pos;
+ break;
+ }
+ }
+ mutex_unlock(&bq2415x_dev_list_mutex);
+ return dev;
+}
+
+static int bq2415x_remove(struct i2c_client *cli)
+{
+ int i;
+ struct bq2415x_device *dev = bq2415x_getdev(cli);
+
+ if (dev) {
+ for (i = 0; i < ARRAY_SIZE(dev->rdev); i++) {
+ if (dev->rdev[i])
+ regulator_unregister(dev->rdev[i]);
+ }
+
+ mutex_lock(&bq2415x_dev_list_mutex);
+ list_del(&dev->list);
+ mutex_unlock(&bq2415x_dev_list_mutex);
+ kfree(dev);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bq2415x_regulator); i++)
+ kfree(bq2415x_regulator[i].init.consumer_supplies);
+
+ return 0;
+}
+
+static int __devinit
+bq2415x_probe(struct i2c_client *cli, const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct bq2415x_device *dev;
+
+ bq2415x_cli = cli;
+
+ if (i2c_check_functionality(cli->adapter, I2C_FUNC_I2C) == 0) {
+ dev_dbg(&cli->dev, "i2c is not supported\n");
+ return -EIO;
+ }
+
+ dev = bq2415x_getdev(cli);
+ if (dev)
+ return -EBUSY;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->cli = cli;
+ rc = bq2415x_regulator_init(dev);
+ if (rc < 0) {
+ bq2415x_remove(cli);
+ } else {
+ mutex_lock(&bq2415x_dev_list_mutex);
+ list_add(&dev->list, &bq2415x_dev_list);
+ mutex_unlock(&bq2415x_dev_list_mutex);
+ bq2415x_debugfs_init(cli);
+ }
+
+ return rc;
+}
+
+static const struct i2c_device_id bq2415x_id[] = {
+ { "bq24156", 0 }, /* VAC charger */
+ { "bq24153", 0 }, /* USB charger */
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq2415x_id);
+
+static struct i2c_driver bq2415x_driver = {
+ .driver = {
+ .name = "bq2415x",
+ .owner = THIS_MODULE,
+ },
+ .id_table = bq2415x_id,
+ .probe = bq2415x_probe,
+ .remove = bq2415x_remove,
+};
+
+static int __init bq2415x_init(void)
+{
+ return i2c_add_driver(&bq2415x_driver);
+}
+module_init(bq2415x_init);
+
+static void __exit bq2415x_exit(void)
+{
+ i2c_del_driver(&bq2415x_driver);
+}
+module_exit(bq2415x_exit);
+
+MODULE_AUTHOR("Aliaksei Katovich <aliaksei.katovich@xxxxxxxxx>");
+MODULE_DESCRIPTION("TI BQ24153/6 one-cell Li-Ion charger driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/bq2415x.h b/include/linux/mfd/bq2415x.h
new file mode 100644
index 0000000..fb9ecb5
--- /dev/null
+++ b/include/linux/mfd/bq2415x.h
@@ -0,0 +1,88 @@
+/*
+ * bq2415x.h - header of TI BQ24153/6 one-cell Li-Ion Charger Driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Aliaksei Katovich <aliaksei.katovich@xxxxxxxxx>
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * version 2 of that License.
+ */
+
+#ifndef __BQ2415X_H_
+#define __BQ2415X_H_
+
+#include <linux/i2c.h>
+#include <linux/regulator/machine.h>
+
+#define BQ24156_ADDR 0x6a
+#define BQ24153_ADDR 0x6b
+
+#define BQ2415X_STS_CTL 0x00 /* status/control (rw) */
+#define BQ2415X_TMR_RST BIT(7)
+#define BQ2415X_EN_STAT BIT(6)
+#define BQ2415X_STAT2 BIT(5)
+#define BQ2415X_STAT1 BIT(4)
+
+#define BQ2415X_GEN_CTL 0x01 /* generic control (rw) */
+#define BQ2415X_TE BIT(3)
+#define BQ2415X_CE BIT(2)
+#define BQ2415X_HZ_MODE BIT(1)
+
+#define BQ2415X_BAT_CTL 0x02 /* control/battery voltage (rw) */
+#define BQ2415X_OTG_PL BIT(1)
+#define BQ2415X_OTG_EN BIT(0)
+
+#define BQ2415X_REV_STS 0x03 /* vender/part/revision (ro) */
+#define BQ2415X_CHG_CTL 0x04 /* battery termination/fast charge
+ * current (rw) */
+#define BQ2415X_SPC_CTL 0x05 /* special charger voltage/enable pin
+ * status (rw) */
+#define BQ2415X_LOW_CHG BIT(5)
+#define BQ2415X_DPM_STATUS BIT(4)
+#define BQ2415X_CD_STATUS BIT(3)
+
+#define BQ2415X_SFT_CTL 0x06 /* safety limit (rw, write-once after
+ * reset!)*/
+
+/* pre-defined input current limits, uA */
+#define BQ2415X_IIN_LIMIT_100 100000
+#define BQ2415X_IIN_LIMIT_500 500000
+#define BQ2415X_IIN_LIMIT_800 800000
+#define BQ2415X_IIN_LIMIT_UNLIM 1500000
+
+enum { /* regulator id */
+ BQ2415X_V_LOWV = 0,
+ BQ2415X_V_OREG,
+ BQ2415X_V_ICHRG_VAC,
+ BQ2415X_V_ICHRG_USB,
+ BQ2415X_V_ITERM,
+ BQ2415X_V_SREG,
+ BQ2415X_V_MCHRG,
+ BQ2415X_V_MREG,
+ BQ2415X_I_INLIMIT,
+ BQ2415X_MAX_REG,
+};
+
+enum bq2415x_cmd {
+ BQ2415X_ENABLE_CHARGER = 1,
+ BQ2415X_DISABLE_CHARGER,
+ BQ2415X_READ_STATUS,
+ BQ2415X_RESET_CHARGER,
+ BQ2415X_RESET_WATCHDOG,
+ BQ2415X_ENABLE_STAT,
+ BQ2415X_DISABLE_STAT,
+ BQ2415X_ENABLE_OTG,
+ BQ2415X_DISABLE_OTG,
+ BQ2415X_OTG_HIGH,
+ BQ2415X_OTG_LOW,
+ BQ2415X_CHG_LOW, /* LOW_CHG bit */
+ BQ2415X_CHG_NORM, /* LOW_CHG bit */
+ BQ2415X_READ_DPM,
+ BQ2415X_READ_CD,
+};
+
+int bq2415x_exec(enum bq2415x_cmd cmd);
+
+#endif /* __BQ2415X_H_ */
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/