mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] mfd: bq2415x charger driver
       [not found] <"cover.1298918741.git.aliaksei.katovich@nokia.com">
@ 2011-02-28 21:36 ` aliaksei.katovich
  2011-02-28 21:36   ` [PATCH 1/2] " aliaksei.katovich
  2011-02-28 21:36   ` [PATCH 2/2] mfd: bq2415x compilation enablers aliaksei.katovich
  0 siblings, 2 replies; 3+ messages in thread
From: aliaksei.katovich @ 2011-02-28 21:36 UTC (permalink / raw)
  To: sameo; +Cc: linux-kernel, Aliaksei Katovich

From: Aliaksei Katovich <aliaksei.katovich@nokia.com>

The bq2415x family of Texas Instruments charger ICs are multifunctional chips
that combine voltage/current regulation with Li-Ion battery charge controller.

Given bq2415x driver implements both regulator and i2c devices within single
module. Despite not being mfd core dependant this driver however is thought
to belong to mfd subsystem rather than regulators or i2c one.

Also I have some concern about hardcoded i2c addresses which may well limit
driver usage to some certain hardware set. Alternatively driver can be extented
to support device tree or another relevant interface to eliminate this possible
limit.

Implementation is based on BQ24153/6/8 datasheet.

The patch set applies cleanly to the MFD repository at
git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6.git

Please review.

Aliaksei Katovich (2):
  mfd: bq2415x charger driver
  mfd: bq2415x compilation enablers

 drivers/mfd/Kconfig         |    7 +
 drivers/mfd/Makefile        |    1 +
 drivers/mfd/bq2415x.c       |  780 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/bq2415x.h |   88 +++++
 4 files changed, 876 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/bq2415x.c
 create mode 100644 include/linux/mfd/bq2415x.h


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] mfd: bq2415x charger driver
  2011-02-28 21:36 ` [PATCH 0/2] mfd: bq2415x charger driver aliaksei.katovich
@ 2011-02-28 21:36   ` aliaksei.katovich
  2011-02-28 21:36   ` [PATCH 2/2] mfd: bq2415x compilation enablers aliaksei.katovich
  1 sibling, 0 replies; 3+ messages in thread
From: aliaksei.katovich @ 2011-02-28 21:36 UTC (permalink / raw)
  To: sameo; +Cc: linux-kernel, Aliaksei Katovich

From: Aliaksei Katovich <aliaksei.katovich@nokia.com>

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

Signed-off-by: Aliaksei Katovich <aliaksei.katovich@nokia.com>
---
 drivers/mfd/bq2415x.c       |  780 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/bq2415x.h |   88 +++++
 2 files changed, 868 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..4d57cb4
--- /dev/null
+++ b/drivers/mfd/bq2415x.c
@@ -0,0 +1,780 @@
+/*
+ * bq2415x.c - TI BQ24153/6 one-cell Li-Ion charger driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Aliaksei Katovich <aliaksei.katovich@nokia.com>
+ *
+ * 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>
+
+#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 */
+	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;
+};
+
+/* 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)
+{
+	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;
+}
+
+/* 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 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 (USB) */
+		.reg		= BQ2415X_CHG_CTL,
+		.val_summ	= 1,
+		.msk_summ	= 1,
+		.msk_mult	= 8,
+		.min_mask	= 0x08,
+		.max_mask	= 0x78,
+		.init.constraints = {
+			.name			= "V_ICHRG_VAC",
+			.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_ICHRG_VAC",
+			.id		= BQ2415X_V_ICHRG_VAC,
+			.n_voltages     = 15,
+			.ops		= &bq2415x_v_basic_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.owner		= THIS_MODULE,
+		},
+	},
+	[BQ2415X_V_ICHRG_USB] = { /* charge current sense (VAC) */
+		.reg		= BQ2415X_CHG_CTL,
+		.val_summ	= 1,
+		.msk_summ	= 2,
+		.msk_mult	= 8,
+		.min_mask	= 0x10,
+		.max_mask	= 0x40,
+		.init.constraints = {
+			.name			= "V_ICHRG_USB",
+			.min_uV			= 6800,
+			.max_uV			= 47600,
+			.valid_modes_mask	= REGULATOR_MODE_NORMAL,
+			.valid_ops_mask		= REGULATOR_CHANGE_VOLTAGE,
+			.always_on		= 1,
+			.boot_on		= 1,
+		},
+		.desc = {
+			.name		= "V_ICHRG_USB",
+			.id		= BQ2415X_V_ICHRG_USB,
+			.n_voltages     = 7,
+			.ops		= &bq2415x_v_basic_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);
+	}
+
+	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@nokia.com>");
+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@nokia.com>
+ *
+ * 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


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 2/2] mfd: bq2415x compilation enablers
  2011-02-28 21:36 ` [PATCH 0/2] mfd: bq2415x charger driver aliaksei.katovich
  2011-02-28 21:36   ` [PATCH 1/2] " aliaksei.katovich
@ 2011-02-28 21:36   ` aliaksei.katovich
  1 sibling, 0 replies; 3+ messages in thread
From: aliaksei.katovich @ 2011-02-28 21:36 UTC (permalink / raw)
  To: sameo; +Cc: linux-kernel, Aliaksei Katovich

From: Aliaksei Katovich <aliaksei.katovich@nokia.com>

Enable compilation of bq2415x charger driver

Signed-off-by: Aliaksei Katovich <aliaksei.katovich@nokia.com>
---
 drivers/mfd/Kconfig  |    7 +++++++
 drivers/mfd/Makefile |    1 +
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fd01836..d0ab44a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -624,6 +624,13 @@ config MFD_WL1273_CORE
 	  driver connects the radio-wl1273 V4L2 module and the wl1273
 	  audio codec.
 
+config MFD_BQ2415X
+	tristate "TI bq24153/6 one-cell Li-Ion charger IC"
+	depends on I2C && REGULATOR
+	help
+	  This driver supports TI bq24153/6 integrated switch-mode chargers
+	  attached to i2c.
+
 endif # MFD_SUPPORT
 
 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a54e2c7..a0df0db 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -83,3 +83,4 @@ obj-$(CONFIG_MFD_TPS6586X)	+= tps6586x.o
 obj-$(CONFIG_MFD_VX855)		+= vx855.o
 obj-$(CONFIG_MFD_WL1273_CORE)	+= wl1273-core.o
 obj-$(CONFIG_MFD_CS5535)	+= cs5535-mfd.o
+obj-$(CONFIG_MFD_BQ2415X)	+= bq2415x.o
-- 
1.7.0.4


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-02-28 21:37 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <"cover.1298918741.git.aliaksei.katovich@nokia.com">
2011-02-28 21:36 ` [PATCH 0/2] mfd: bq2415x charger driver aliaksei.katovich
2011-02-28 21:36   ` [PATCH 1/2] " aliaksei.katovich
2011-02-28 21:36   ` [PATCH 2/2] mfd: bq2415x compilation enablers aliaksei.katovich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®