lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:	Mon, 28 Feb 2011 23:36:14 +0200
From:	aliaksei.katovich@...il.com
To:	sameo@...ux.intel.com
Cc:	linux-kernel@...r.kernel.org,
	Aliaksei Katovich <aliaksei.katovich@...ia.com>
Subject: [PATCH 1/2] mfd: bq2415x charger driver

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

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

Signed-off-by: Aliaksei Katovich <aliaksei.katovich@...ia.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@...ia.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@...ia.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@...ia.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

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ