From f60c4de2e315a67d6f085d2b6325825f0e3ad427 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pali=20Roh=C3=A1r?= Date: Tue, 22 May 2012 23:10:19 +0200 Subject: [PATCH] Reorganized bq2415x patches * Renamed to bq2415x_.patch * Moved Makefile/Kconfig code part from charger to kconfig patch --- .../debian/patches/bq24150-charger.patch | 1668 -------------------- .../debian/patches/bq24150-rx51.patch | 249 --- .../debian/patches/bq2415x_charger.patch | 1648 +++++++++++++++++++ .../debian/patches/bq2415x_kconfig.patch | 20 + .../debian/patches/bq2415x_rx51.patch | 249 +++ kernel-power-2.6.28/debian/patches/series | 5 +- 6 files changed, 1920 insertions(+), 1919 deletions(-) delete mode 100644 kernel-power-2.6.28/debian/patches/bq24150-charger.patch delete mode 100644 kernel-power-2.6.28/debian/patches/bq24150-rx51.patch create mode 100644 kernel-power-2.6.28/debian/patches/bq2415x_charger.patch create mode 100644 kernel-power-2.6.28/debian/patches/bq2415x_kconfig.patch create mode 100644 kernel-power-2.6.28/debian/patches/bq2415x_rx51.patch diff --git a/kernel-power-2.6.28/debian/patches/bq24150-charger.patch b/kernel-power-2.6.28/debian/patches/bq24150-charger.patch deleted file mode 100644 index 4393e84..0000000 --- a/kernel-power-2.6.28/debian/patches/bq24150-charger.patch +++ /dev/null @@ -1,1668 +0,0 @@ ---- /dev/null 2012-04-29 08:55:34.366920721 +0200 -+++ kernel-power/drivers/power/bq2415x_charger.c 2012-04-29 17:53:57.799826239 +0200 -@@ -0,0 +1,1564 @@ -+/* -+ bq2415x_charger.c - bq2415x charger driver -+ Copyright (C) 2011-2012 Pali Rohár -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ 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. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+*/ -+ -+/* -+ Datasheets: -+ http://www.ti.com/product/bq24150 -+ http://www.ti.com/product/bq24150a -+ http://www.ti.com/product/bq24152 -+ http://www.ti.com/product/bq24153 -+ http://www.ti.com/product/bq24153a -+ http://www.ti.com/product/bq24155 -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define BQ2415X_TIMER_TIMEOUT 10 -+ -+#define BQ2415X_REG_STATUS 0x00 -+#define BQ2415X_REG_CONTROL 0x01 -+#define BQ2415X_REG_VOLTAGE 0x02 -+#define BQ2415X_REG_VENDER 0x03 -+#define BQ2415X_REG_CURRENT 0x04 -+ -+/* reset state for all registers */ -+#define BQ2415X_RESET_STATUS BIT(6) -+#define BQ2415X_RESET_CONTROL (BIT(4)|BIT(5)) -+#define BQ2415X_RESET_VOLTAGE (BIT(1)|BIT(3)) -+#define BQ2415X_RESET_CURRENT (BIT(0)|BIT(3)|BIT(7)) -+ -+/* status register */ -+#define BQ2415X_BIT_TMR_RST 7 -+#define BQ2415X_BIT_OTG 7 -+#define BQ2415X_BIT_EN_STAT 6 -+#define BQ2415X_MASK_STAT (BIT(4)|BIT(5)) -+#define BQ2415X_SHIFT_STAT 4 -+#define BQ2415X_BIT_BOOST 3 -+#define BQ2415X_MASK_FAULT (BIT(0)|BIT(1)|BIT(2)) -+#define BQ2415X_SHIFT_FAULT 0 -+ -+/* control register */ -+#define BQ2415X_MASK_LIMIT (BIT(6)|BIT(7)) -+#define BQ2415X_SHIFT_LIMIT 6 -+#define BQ2415X_MASK_VLOWV (BIT(4)|BIT(5)) -+#define BQ2415X_SHIFT_VLOWV 4 -+#define BQ2415X_BIT_TE 3 -+#define BQ2415X_BIT_CE 2 -+#define BQ2415X_BIT_HZ_MODE 1 -+#define BQ2415X_BIT_OPA_MODE 0 -+ -+/* voltage register */ -+#define BQ2415X_MASK_VO (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) -+#define BQ2415X_SHIFT_VO 2 -+#define BQ2415X_BIT_OTG_PL 1 -+#define BQ2415X_BIT_OTG_EN 0 -+ -+/* vender register */ -+#define BQ2415X_MASK_VENDER (BIT(5)|BIT(6)|BIT(7)) -+#define BQ2415X_SHIFT_VENDER 5 -+#define BQ2415X_MASK_PN (BIT(3)|BIT(4)) -+#define BQ2415X_SHIFT_PN 3 -+#define BQ2415X_MASK_REVISION (BIT(0)|BIT(1)|BIT(2)) -+#define BQ2415X_SHIFT_REVISION 0 -+ -+/* current register */ -+/* RESET BIT(7) */ -+#define BQ2415X_MASK_VI_CHRG (BIT(4)|BIT(5)|BIT(6)|BIT(7)) -+#define BQ2415X_SHIFT_VI_CHRG 4 -+/* N/A BIT(3) */ -+#define BQ2415X_MASK_VI_TERM (BIT(0)|BIT(1)|BIT(2)|BIT(7)) -+#define BQ2415X_SHIFT_VI_TERM 0 -+ -+ -+enum bq2415x_command { -+ BQ2415X_TIMER_RESET, -+ BQ2415X_OTG_STATUS, -+ BQ2415X_STAT_PIN_STATUS, -+ BQ2415X_STAT_PIN_ENABLE, -+ BQ2415X_STAT_PIN_DISABLE, -+ BQ2415X_CHARGE_STATUS, -+ BQ2415X_BOOST_STATUS, -+ BQ2415X_FAULT_STATUS, -+ -+ BQ2415X_CHARGE_TERMINATION_STATUS, -+ BQ2415X_CHARGE_TERMINATION_ENABLE, -+ BQ2415X_CHARGE_TERMINATION_DISABLE, -+ BQ2415X_CHARGER_STATUS, -+ BQ2415X_CHARGER_ENABLE, -+ BQ2415X_CHARGER_DISABLE, -+ BQ2415X_HIGH_IMPEDANCE_STATUS, -+ BQ2415X_HIGH_IMPEDANCE_ENABLE, -+ BQ2415X_HIGH_IMPEDANCE_DISABLE, -+ BQ2415X_BOOST_MODE_STATUS, -+ BQ2415X_BOOST_MODE_ENABLE, -+ BQ2415X_BOOST_MODE_DISABLE, -+ -+ BQ2415X_OTG_LEVEL, -+ BQ2415X_OTG_ACTIVATE_HIGH, -+ BQ2415X_OTG_ACTIVATE_LOW, -+ BQ2415X_OTG_PIN_STATUS, -+ BQ2415X_OTG_PIN_ENABLE, -+ BQ2415X_OTG_PIN_DISABLE, -+ -+ BQ2415X_VENDER_CODE, -+ BQ2415X_PART_NUMBER, -+ BQ2415X_REVISION, -+}; -+ -+enum bq2415x_chip { -+ BQUNKNOWN, -+ BQ24150, -+ BQ24150A, -+ BQ24151, -+ BQ24151A, -+ BQ24152, -+ BQ24153, -+ BQ24153A, -+ BQ24155, -+ BQ24156, -+ BQ24156A, -+ BQ24158, -+}; -+ -+enum bq2415x_mode { -+ BQ2415X_MODE_NONE, -+ BQ2415X_MODE_HOST_CHARGER, -+ BQ2415X_MODE_DEDICATED_CHARGER, -+ BQ2415X_MODE_BOOST, -+}; -+ -+static char *bq2415x_chip_name[] = { -+ "unknown", -+ "bq24150", -+ "bq24150a", -+ "bq24151", -+ "bq24151a", -+ "bq24152", -+ "bq24153", -+ "bq24153a", -+ "bq24155", -+ "bq24156", -+ "bq24156a", -+ "bq24158", -+}; -+ -+struct bq2415x_device { -+ struct device *dev; -+ struct bq2415x_platform_data init_data; -+ struct power_supply charger; -+ struct delayed_work work; -+ enum bq2415x_mode charger_mode; /* mode reported by hook function */ -+ enum bq2415x_mode mode; /* actual setted mode */ -+ enum bq2415x_chip chip; -+ char *model; -+ char *name; -+ int autotimer; /* 1 - if driver automatically reset timer, 0 - not */ -+ int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */ -+ int id; -+}; -+ -+static DEFINE_IDR(bq2415x_id); -+ -+static DEFINE_MUTEX(bq2415x_id_mutex); -+static DEFINE_MUTEX(bq2415x_timer_mutex); -+static DEFINE_MUTEX(bq2415x_i2c_mutex); -+ -+/* i2c read functions */ -+ -+static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg) -+{ -+ struct i2c_client *client = to_i2c_client(bq->dev); -+ struct i2c_msg msg[2]; -+ u8 val; -+ int ret; -+ -+ if (!client->adapter) -+ return -ENODEV; -+ -+ msg[0].addr = client->addr; -+ msg[0].flags = 0; -+ msg[0].buf = ® -+ msg[0].len = sizeof(reg); -+ msg[1].addr = client->addr; -+ msg[1].flags = I2C_M_RD; -+ msg[1].buf = &val; -+ msg[1].len = sizeof(val); -+ -+ mutex_lock(&bq2415x_i2c_mutex); -+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); -+ mutex_unlock(&bq2415x_i2c_mutex); -+ -+ if (ret < 0) -+ return ret; -+ -+ return val; -+} -+ -+static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg, -+ u8 mask, u8 shift) -+{ -+ int ret; -+ -+ if (shift > 8) -+ return -EINVAL; -+ -+ ret = bq2415x_i2c_read(bq, reg); -+ if (ret < 0) -+ return ret; -+ else -+ return (ret & mask) >> shift; -+} -+ -+static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit) -+{ -+ if (bit > 8) -+ return -EINVAL; -+ else -+ return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit); -+} -+ -+/* i2c write functions */ -+ -+static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val) -+{ -+ struct i2c_client *client = to_i2c_client(bq->dev); -+ struct i2c_msg msg[1]; -+ u8 data[2]; -+ int ret; -+ -+ data[0] = reg; -+ data[1] = val; -+ -+ msg[0].addr = client->addr; -+ msg[0].flags = 0; -+ msg[0].buf = data; -+ msg[0].len = ARRAY_SIZE(data); -+ -+ mutex_lock(&bq2415x_i2c_mutex); -+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); -+ mutex_unlock(&bq2415x_i2c_mutex); -+ -+ /* i2c_transfer returns number of messages transferred */ -+ if (ret < 0) -+ return ret; -+ else if (ret != 1) -+ return -EIO; -+ -+ return 0; -+} -+ -+static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val, -+ u8 mask, u8 shift) -+{ -+ int ret; -+ -+ if (shift > 8) -+ return -EINVAL; -+ -+ ret = bq2415x_i2c_read(bq, reg); -+ if (ret < 0) -+ return ret; -+ -+ ret &= ~mask; -+ ret |= val << shift; -+ -+ return bq2415x_i2c_write(bq, reg, ret); -+} -+ -+static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg, -+ bool val, u8 bit) -+{ -+ if (bit > 8) -+ return -EINVAL; -+ else -+ return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit); -+} -+ -+/* global exec command function */ -+ -+static int bq2415x_exec_command(struct bq2415x_device *bq, -+ enum bq2415x_command command) -+{ -+ int ret; -+ switch (command) { -+ case BQ2415X_TIMER_RESET: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, -+ 1, BQ2415X_BIT_TMR_RST); -+ case BQ2415X_OTG_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, -+ BQ2415X_BIT_OTG); -+ case BQ2415X_STAT_PIN_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, -+ BQ2415X_BIT_EN_STAT); -+ case BQ2415X_STAT_PIN_ENABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1, -+ BQ2415X_BIT_EN_STAT); -+ case BQ2415X_STAT_PIN_DISABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0, -+ BQ2415X_BIT_EN_STAT); -+ case BQ2415X_CHARGE_STATUS: -+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS, -+ BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT); -+ case BQ2415X_BOOST_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, -+ BQ2415X_BIT_BOOST); -+ case BQ2415X_FAULT_STATUS: -+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS, -+ BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT); -+ -+ case BQ2415X_CHARGE_TERMINATION_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, -+ BQ2415X_BIT_TE); -+ case BQ2415X_CHARGE_TERMINATION_ENABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 1, BQ2415X_BIT_TE); -+ case BQ2415X_CHARGE_TERMINATION_DISABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 0, BQ2415X_BIT_TE); -+ case BQ2415X_CHARGER_STATUS: -+ ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, -+ BQ2415X_BIT_CE); -+ if (ret < 0) -+ return ret; -+ else -+ return ret > 0 ? 0 : 1; -+ case BQ2415X_CHARGER_ENABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 0, BQ2415X_BIT_CE); -+ case BQ2415X_CHARGER_DISABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 1, BQ2415X_BIT_CE); -+ case BQ2415X_HIGH_IMPEDANCE_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, -+ BQ2415X_BIT_HZ_MODE); -+ case BQ2415X_HIGH_IMPEDANCE_ENABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 1, BQ2415X_BIT_HZ_MODE); -+ case BQ2415X_HIGH_IMPEDANCE_DISABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 0, BQ2415X_BIT_HZ_MODE); -+ case BQ2415X_BOOST_MODE_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, -+ BQ2415X_BIT_OPA_MODE); -+ case BQ2415X_BOOST_MODE_ENABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 1, BQ2415X_BIT_OPA_MODE); -+ case BQ2415X_BOOST_MODE_DISABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, -+ 0, BQ2415X_BIT_OPA_MODE); -+ -+ case BQ2415X_OTG_LEVEL: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE, -+ BQ2415X_BIT_OTG_PL); -+ case BQ2415X_OTG_ACTIVATE_HIGH: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, -+ 1, BQ2415X_BIT_OTG_PL); -+ case BQ2415X_OTG_ACTIVATE_LOW: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, -+ 0, BQ2415X_BIT_OTG_PL); -+ case BQ2415X_OTG_PIN_STATUS: -+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE, -+ BQ2415X_BIT_OTG_EN); -+ case BQ2415X_OTG_PIN_ENABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, -+ 1, BQ2415X_BIT_OTG_EN); -+ case BQ2415X_OTG_PIN_DISABLE: -+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, -+ 0, BQ2415X_BIT_OTG_EN); -+ -+ case BQ2415X_VENDER_CODE: -+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, -+ BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER); -+ case BQ2415X_PART_NUMBER: -+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, -+ BQ2415X_MASK_PN, BQ2415X_SHIFT_PN); -+ case BQ2415X_REVISION: -+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, -+ BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION); -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+/* global detect chip */ -+ -+static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq) -+{ -+ struct i2c_client *client = to_i2c_client(bq->dev); -+ int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER); -+ -+ if (ret < 0) -+ return ret; -+ -+ switch (client->addr) { -+ case 0x6b: -+ switch (ret) { -+ case 0: -+ if (bq->chip == BQ24151A) -+ return bq->chip; -+ else -+ return BQ24151; -+ case 1: -+ if (bq->chip == BQ24150A || -+ bq->chip == BQ24152 || -+ bq->chip == BQ24155) -+ return bq->chip; -+ else -+ return BQ24150; -+ case 2: -+ if (bq->chip == BQ24153A) -+ return bq->chip; -+ else -+ return BQ24153; -+ default: -+ return BQUNKNOWN; -+ } -+ break; -+ -+ case 0x6a: -+ switch (ret) { -+ case 0: -+ if (bq->chip == BQ24156A) -+ return bq->chip; -+ else -+ return BQ24156; -+ case 2: -+ return BQ24158; -+ default: -+ return BQUNKNOWN; -+ } -+ break; -+ } -+ -+ return BQUNKNOWN; -+} -+ -+static int bq2415x_detect_revision(struct bq2415x_device *bq) -+{ -+ int ret = bq2415x_exec_command(bq, BQ2415X_REVISION); -+ int chip = bq2415x_detect_chip(bq); -+ if (ret < 0 || chip < 0) -+ return -1; -+ -+ switch (chip) { -+ case BQ24150: -+ case BQ24150A: -+ case BQ24151: -+ case BQ24151A: -+ case BQ24152: -+ if (ret >= 0 && ret <= 3) -+ return ret; -+ else -+ return -1; -+ -+ case BQ24153: -+ case BQ24153A: -+ case BQ24156: -+ case BQ24156A: -+ case BQ24158: -+ if (ret == 3) -+ return 0; -+ else if (ret == 1) -+ return 1; -+ else -+ return -1; -+ -+ case BQ24155: -+ if (ret == 3) -+ return 3; -+ else -+ return -1; -+ -+ case BQUNKNOWN: -+ return -1; -+ } -+ -+ return -1; -+} -+ -+static int bq2415x_get_vender_code(struct bq2415x_device *bq) -+{ -+ int ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE); -+ if (ret < 0) -+ return 0; -+ else /* convert to binary */ -+ return (ret & 0x1) + -+ ((ret >> 1) & 0x1) * 10 + -+ ((ret >> 2) & 0x1) * 100; -+} -+ -+/* global other functions */ -+ -+static void bq2415x_reset_chip(struct bq2415x_device *bq) -+{ -+ bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT); -+ bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE); -+ bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL); -+ bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS); -+} -+ -+static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA) -+{ -+ int val; -+ if (mA <= 100) -+ val = 0; -+ else if (mA <= 500) -+ val = 1; -+ else if (mA <= 800) -+ val = 2; -+ else -+ val = 3; -+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, -+ BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); -+} -+ -+static int bq2415x_get_current_limit(struct bq2415x_device *bq) -+{ -+ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, -+ BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); -+ if (ret < 0) -+ return ret; -+ else if (ret == 0) -+ return 100; -+ else if (ret == 1) -+ return 500; -+ else if (ret == 2) -+ return 800; -+ else if (ret == 3) -+ return 1800; -+ else -+ return -EINVAL; -+} -+ -+static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV) -+{ -+ int val = mV/100 + (mV%100 > 0 ? 1 : 0) - 34; -+ -+ if (val < 0) -+ val = 0; -+ else if (val > 3) -+ val = 3; -+ -+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, -+ BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); -+} -+ -+static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq) -+{ -+ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, -+ BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); -+ if (ret < 0) -+ return ret; -+ else -+ return 100 * (34 + ret); -+} -+ -+static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq, -+ int mV) -+{ -+ int val = (mV/10 + (mV%10 > 0 ? 1 : 0) - 350) / 2; -+ -+ if (val < 0) -+ val = 0; -+ else if (val > 94) /* FIXME: Max is 94 or 122 ? */ -+ return -EINVAL; -+ -+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val, -+ BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); -+} -+ -+static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq) -+{ -+ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE, -+ BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); -+ if (ret < 0) -+ return ret; -+ else -+ return 10 * (350 + 2*ret); -+} -+ -+static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA) -+{ -+ int val; -+ if (bq->init_data.resistor_sense <= 0) -+ return -ENOSYS; -+ -+ val = (mA * bq->init_data.resistor_sense - 37400); -+ val = val/6800 + (val%6800 > 0 ? 1 : 0); -+ -+ if (val < 0) -+ val = 0; -+ else if (val > 7) -+ val = 7; -+ -+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val, -+ BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG); -+} -+ -+static int bq2415x_get_charge_current(struct bq2415x_device *bq) -+{ -+ int ret; -+ if (bq->init_data.resistor_sense <= 0) -+ return -ENOSYS; -+ -+ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT, -+ BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG); -+ if (ret < 0) -+ return ret; -+ else -+ return (37400 + 6800*ret) / bq->init_data.resistor_sense; -+} -+ -+static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA) -+{ -+ int val; -+ if (bq->init_data.resistor_sense <= 0) -+ return -ENOSYS; -+ -+ val = (mA * bq->init_data.resistor_sense - 3400); -+ val = val/3400 + (val%3400 > 0 ? 1 : 0); -+ -+ if (val < 0) -+ val = 0; -+ else if (val > 7) -+ val = 7; -+ -+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val, -+ BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM); -+} -+ -+static int bq2415x_get_termination_current(struct bq2415x_device *bq) -+{ -+ int ret; -+ if (bq->init_data.resistor_sense <= 0) -+ return -ENOSYS; -+ -+ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT, -+ BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM); -+ if (ret < 0) -+ return ret; -+ else -+ return (3400 + 3400*ret) / bq->init_data.resistor_sense; -+} -+ -+#define bq2415x_set_default_value(bq, value) \ -+ do { \ -+ int ret = 0; \ -+ if (bq->init_data.value != -1) \ -+ ret = bq2415x_set_##value(bq, bq->init_data.value); \ -+ if (ret < 0) \ -+ return ret; \ -+ } while (0) -+ -+static int bq2415x_set_defaults(struct bq2415x_device *bq) -+{ -+ bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); -+ bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); -+ bq2415x_set_default_value(bq, current_limit); -+ bq2415x_set_default_value(bq, weak_battery_voltage); -+ bq2415x_set_default_value(bq, battery_regulation_voltage); -+ if (bq->init_data.resistor_sense > 0) { -+ bq2415x_set_default_value(bq, charge_current); -+ bq2415x_set_default_value(bq, termination_current); -+ bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE); -+ } -+ bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); -+ return 0; -+} -+ -+#undef bq2415x_set_default_value -+ -+/* charger mode functions */ -+ -+static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) -+{ -+ int ret = 0; -+ int charger = 0; -+ -+ if (mode == BQ2415X_MODE_NONE || -+ mode == BQ2415X_MODE_HOST_CHARGER || -+ mode == BQ2415X_MODE_DEDICATED_CHARGER) -+ charger = 1; -+ -+ if (charger) -+ ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); -+ else -+ ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); -+ -+ if (ret < 0) -+ return ret; -+ -+ switch (mode) { -+ case BQ2415X_MODE_NONE: -+ dev_info(bq->dev, "mode: N/A\n"); -+ ret = bq2415x_set_current_limit(bq, 100); -+ break; -+ case BQ2415X_MODE_HOST_CHARGER: -+ dev_info(bq->dev, "mode: Host/HUB charger\n"); -+ ret = bq2415x_set_current_limit(bq, 500); -+ break; -+ case BQ2415X_MODE_DEDICATED_CHARGER: -+ dev_info(bq->dev, "mode: Dedicated charger\n"); -+ ret = bq2415x_set_current_limit(bq, 1800); -+ break; -+ case BQ2415X_MODE_BOOST: /* Boost mode */ -+ dev_info(bq->dev, "mode: Boost\n"); -+ ret = bq2415x_set_current_limit(bq, 100); -+ break; -+ } -+ -+ if (ret < 0) -+ return ret; -+ -+ if (charger) -+ ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); -+ else -+ ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE); -+ -+ if (ret < 0) -+ return ret; -+ -+ bq->mode = mode; -+ return 0; -+ -+} -+ -+static void bq2415x_set_charger_type(int type, void *data) -+{ -+ struct bq2415x_device *bq = data; -+ -+ if (!bq) -+ return; -+ -+ switch (type) { -+ case 0: -+ bq->charger_mode = BQ2415X_MODE_NONE; -+ break; -+ case 1: -+ bq->charger_mode = BQ2415X_MODE_HOST_CHARGER; -+ break; -+ case 2: -+ bq->charger_mode = BQ2415X_MODE_DEDICATED_CHARGER; -+ break; -+ default: -+ return; -+ } -+ -+ if (bq->automode < 1) -+ return; -+ -+ /* TODO: Detect USB Host mode */ -+ -+ bq2415x_set_mode(bq, bq->charger_mode); -+ -+} -+ -+/* timer functions */ -+ -+static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state) -+{ -+ mutex_lock(&bq2415x_timer_mutex); -+ -+ if (bq->autotimer == state) { -+ mutex_unlock(&bq2415x_timer_mutex); -+ return; -+ } -+ -+ bq->autotimer = state; -+ -+ if (state) { -+ schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ); -+ bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); -+ } else { -+ cancel_delayed_work_sync(&bq->work); -+ } -+ -+ mutex_unlock(&bq2415x_timer_mutex); -+} -+ -+static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) -+{ -+ dev_err(bq->dev, "%s\n", msg); -+ if (bq->automode > 0) -+ bq->automode = 0; -+ bq2415x_set_mode(bq, BQ2415X_MODE_NONE); -+ bq2415x_set_autotimer(bq, 0); -+} -+ -+static void bq2415x_timer_work(struct work_struct *work) -+{ -+ struct bq2415x_device *bq = container_of(work, struct bq2415x_device, -+ work.work); -+ int ret, error, boost; -+ -+ if (!bq->autotimer) -+ return; -+ -+ ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); -+ if (ret < 0) { -+ bq2415x_timer_error(bq, "Reseting timer failed"); -+ return; -+ } -+ -+ boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS); -+ if (boost < 0) { -+ bq2415x_timer_error(bq, "Unknow error"); -+ return; -+ } -+ -+ error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS); -+ if (error < 0) { -+ bq2415x_timer_error(bq, "Unknow error"); -+ return; -+ } -+ -+ if (boost) { -+ switch (error) { -+ case 0: /* No error */ -+ break; -+ case 6: /* Timer expired */ -+ dev_err(bq->dev, "Timer expired\n"); -+ break; -+ case 3: /* Battery voltage too low */ -+ dev_err(bq->dev, "Battery voltage to low\n"); -+ break; -+ -+ case 1: /* Overvoltage protection (chip fried) */ -+ bq2415x_timer_error(bq, -+ "Overvolatge protection (chip fried)"); -+ return; -+ case 2: /* Overload */ -+ bq2415x_timer_error(bq, "Overload"); -+ return; -+ case 4: /* Battery overvoltage protection */ -+ bq2415x_timer_error(bq, -+ "Battery overvoltage protection"); -+ return; -+ case 5: /* Thermal shutdown (too hot) */ -+ bq2415x_timer_error(bq, -+ "Thermal shutdown (too hot)"); -+ return; -+ case 7: /* N/A */ -+ bq2415x_timer_error(bq, "Unknown error"); -+ return; -+ } -+ } else { -+ switch (error) { -+ case 0: /* No error */ -+ break; -+ case 2: /* Sleep mode */ -+ dev_err(bq->dev, "Sleep mode\n"); -+ break; -+ case 3: /* Poor input source */ -+ dev_err(bq->dev, "Poor input source\n"); -+ break; -+ case 6: /* Timer expired */ -+ dev_err(bq->dev, "Timer expired\n"); -+ break; -+ case 7: /* No battery */ -+ dev_err(bq->dev, "No battery\n"); -+ break; -+ -+ case 1: /* Overvoltage protection (chip fried) */ -+ bq2415x_timer_error(bq, -+ "Overvolatge protection (chip fried)"); -+ return; -+ case 4: /* Battery overvoltage protection */ -+ bq2415x_timer_error(bq, -+ "Battery overvoltage protection"); -+ return; -+ case 5: /* Thermal shutdown (too hot) */ -+ bq2415x_timer_error(bq, -+ "Thermal shutdown (too hot)"); -+ return; -+ } -+ } -+ -+ schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ); -+} -+ -+/* power supply */ -+ -+static enum power_supply_property bq2415x_power_supply_props[] = { -+ /* TODO: more power supply properties */ -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_MODEL_NAME, -+}; -+ -+static int bq2415x_power_supply_get_property(struct power_supply *psy, -+ enum power_supply_property psp, union power_supply_propval *val) -+{ -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ int ret; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS); -+ if (ret < 0) -+ return ret; -+ else if (ret == 0) /* Ready */ -+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; -+ else if (ret == 1) /* Charge in progress */ -+ val->intval = POWER_SUPPLY_STATUS_CHARGING; -+ else if (ret == 2) /* Charge done */ -+ val->intval = POWER_SUPPLY_STATUS_FULL; -+ else -+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; -+ break; -+ case POWER_SUPPLY_PROP_MODEL_NAME: -+ val->strval = bq->model; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static int bq2415x_power_supply_init(struct bq2415x_device *bq) -+{ -+ int ret; -+ int chip; -+ char revstr[8]; -+ -+ bq->charger.name = bq->name; -+ bq->charger.type = POWER_SUPPLY_TYPE_USB; -+ bq->charger.properties = bq2415x_power_supply_props; -+ bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props); -+ bq->charger.get_property = bq2415x_power_supply_get_property; -+ -+ ret = bq2415x_detect_chip(bq); -+ if (ret < 0) -+ chip = BQUNKNOWN; -+ else -+ chip = ret; -+ -+ ret = bq2415x_detect_revision(bq); -+ if (ret < 0) -+ strcpy(revstr, "unknown"); -+ else -+ sprintf(revstr, "1.%d", ret); -+ -+ bq->model = kasprintf(GFP_KERNEL, -+ "chip %s, revision %s, vender code %.3d", -+ bq2415x_chip_name[chip], revstr, -+ bq2415x_get_vender_code(bq)); -+ if (!bq->model) { -+ dev_err(bq->dev, "failed to allocate model name\n"); -+ return -ENOMEM; -+ } -+ -+ ret = power_supply_register(bq->dev, &bq->charger); -+ if (ret) { -+ kfree(bq->model); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void bq2415x_power_supply_exit(struct bq2415x_device *bq) -+{ -+ bq->autotimer = 0; -+ if (bq->automode > 0) -+ bq->automode = 0; -+ cancel_delayed_work_sync(&bq->work); -+ power_supply_unregister(&bq->charger); -+ kfree(bq->model); -+} -+ -+/* sysfs files */ -+ -+static ssize_t bq2415x_sysfs_show_status(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ enum bq2415x_command command; -+ int ret; -+ -+ if (strcmp(attr->attr.name, "otg_status") == 0) -+ command = BQ2415X_OTG_STATUS; -+ else if (strcmp(attr->attr.name, "charge_status") == 0) -+ command = BQ2415X_CHARGE_STATUS; -+ else if (strcmp(attr->attr.name, "boost_status") == 0) -+ command = BQ2415X_BOOST_STATUS; -+ else if (strcmp(attr->attr.name, "fault_status") == 0) -+ command = BQ2415X_FAULT_STATUS; -+ else -+ return -EINVAL; -+ -+ ret = bq2415x_exec_command(bq, command); -+ if (ret < 0) -+ return ret; -+ else -+ return sprintf(buf, "%d\n", ret); -+} -+ -+static ssize_t bq2415x_sysfs_set_timer(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ int ret = 0; -+ -+ if (strncmp(buf, "auto", 4) == 0) -+ bq2415x_set_autotimer(bq, 1); -+ else if (strncmp(buf, "off", 3) == 0) -+ bq2415x_set_autotimer(bq, 0); -+ else -+ ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); -+ -+ if (ret < 0) -+ return ret; -+ else -+ return count; -+} -+ -+static ssize_t bq2415x_sysfs_show_timer(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ -+ if (bq->autotimer) -+ return sprintf(buf, "auto\n"); -+ else -+ return sprintf(buf, "off\n"); -+} -+ -+static ssize_t bq2415x_sysfs_set_mode(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ enum bq2415x_mode mode; -+ int ret = 0; -+ -+ if (strncmp(buf, "auto", 4) == 0) { -+ if (bq->automode < 0) -+ return -ENOSYS; -+ bq->automode = 1; -+ mode = bq->charger_mode; -+ } else if (strncmp(buf, "none", 4) == 0) { -+ if (bq->automode > 0) -+ bq->automode = 0; -+ mode = BQ2415X_MODE_NONE; -+ } else if (strncmp(buf, "host", 4) == 0) { -+ if (bq->automode > 0) -+ bq->automode = 0; -+ mode = BQ2415X_MODE_HOST_CHARGER; -+ } else if (strncmp(buf, "dedicated", 9) == 0) { -+ if (bq->automode > 0) -+ bq->automode = 0; -+ mode = BQ2415X_MODE_DEDICATED_CHARGER; -+ } else if (strncmp(buf, "boost", 5) == 0) { -+ if (bq->automode > 0) -+ bq->automode = 0; -+ mode = BQ2415X_MODE_BOOST; -+ } else if (strncmp(buf, "reset", 5) == 0) { -+ bq2415x_reset_chip(bq); -+ bq2415x_set_defaults(bq); -+ if (bq->automode > 0) -+ bq->automode = 1; -+ return count; -+ } else -+ return -EINVAL; -+ -+ ret = bq2415x_set_mode(bq, mode); -+ if (ret < 0) -+ return ret; -+ else -+ return count; -+} -+ -+static ssize_t bq2415x_sysfs_show_mode(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ ssize_t ret = 0; -+ -+ if (bq->automode > 0) -+ ret += sprintf(buf+ret, "auto ("); -+ -+ switch (bq->mode) { -+ case BQ2415X_MODE_NONE: -+ ret += sprintf(buf+ret, "none"); -+ break; -+ case BQ2415X_MODE_HOST_CHARGER: -+ ret += sprintf(buf+ret, "host"); -+ break; -+ case BQ2415X_MODE_DEDICATED_CHARGER: -+ ret += sprintf(buf+ret, "dedicated"); -+ break; -+ case BQ2415X_MODE_BOOST: -+ ret += sprintf(buf+ret, "boost"); -+ break; -+ } -+ -+ if (bq->automode > 0) -+ ret += sprintf(buf+ret, ")"); -+ -+ ret += sprintf(buf+ret, "\n"); -+ return ret; -+} -+ -+static ssize_t bq2415x_sysfs_set_registers(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ ssize_t ret = 0; -+ char *end; -+ int reg; -+ int val; -+ -+ reg = simple_strtol(buf, &end, 16); -+ if (reg < 0 || reg > 4) -+ return -EINVAL; -+ -+ val = simple_strtol(end+1, NULL, 16); -+ if (val < 0 || val > 255) -+ return -EINVAL; -+ -+ ret = bq2415x_i2c_write(bq, reg, val); -+ if (ret < 0) -+ return ret; -+ else -+ return count; -+} -+ -+static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq, -+ u8 reg, char *buf) -+{ -+ int ret = bq2415x_i2c_read(bq, reg); -+ if (ret < 0) -+ return sprintf(buf, "%#.2x=error %d\n", reg, ret); -+ else -+ return sprintf(buf, "%#.2x=%#.2x\n", reg, ret); -+} -+ -+static ssize_t bq2415x_sysfs_show_registers(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ ssize_t ret = 0; -+ -+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret); -+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret); -+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret); -+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret); -+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret); -+ return ret; -+} -+ -+/* Current & Volatage settings */ -+ -+static ssize_t bq2415x_sysfs_set_limit(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ long val; -+ int ret; -+ -+ if (strict_strtol(buf, 10, &val) < 0) -+ return -EINVAL; -+ -+ if (strcmp(attr->attr.name, "current_limit") == 0) -+ ret = bq2415x_set_current_limit(bq, val); -+ else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0) -+ ret = bq2415x_set_weak_battery_voltage(bq, val); -+ else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0) -+ ret = bq2415x_set_battery_regulation_voltage(bq, val); -+ else if (strcmp(attr->attr.name, "charge_current") == 0) -+ ret = bq2415x_set_charge_current(bq, val); -+ else if (strcmp(attr->attr.name, "termination_current") == 0) -+ ret = bq2415x_set_termination_current(bq, val); -+ else -+ return -EINVAL; -+ -+ if (ret < 0) -+ return ret; -+ else -+ return count; -+} -+ -+static ssize_t bq2415x_sysfs_show_limit(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ int ret; -+ -+ if (strcmp(attr->attr.name, "current_limit") == 0) -+ ret = bq2415x_get_current_limit(bq); -+ else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0) -+ ret = bq2415x_get_weak_battery_voltage(bq); -+ else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0) -+ ret = bq2415x_get_battery_regulation_voltage(bq); -+ else if (strcmp(attr->attr.name, "charge_current") == 0) -+ ret = bq2415x_get_charge_current(bq); -+ else if (strcmp(attr->attr.name, "termination_current") == 0) -+ ret = bq2415x_get_termination_current(bq); -+ else -+ return -EINVAL; -+ -+ if (ret < 0) -+ return ret; -+ else -+ return sprintf(buf, "%d\n", ret); -+} -+ -+static ssize_t bq2415x_sysfs_set_enable(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ enum bq2415x_command command; -+ long val; -+ int ret; -+ -+ if (strict_strtol(buf, 10, &val) < 0) -+ return -EINVAL; -+ -+ if (strcmp(attr->attr.name, "charge_termination_enable") == 0) -+ command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE : -+ BQ2415X_CHARGE_TERMINATION_DISABLE; -+ else if (strcmp(attr->attr.name, "high_impedance_enable") == 0) -+ command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE : -+ BQ2415X_HIGH_IMPEDANCE_DISABLE; -+ else if (strcmp(attr->attr.name, "otg_pin_enable") == 0) -+ command = val ? BQ2415X_OTG_PIN_ENABLE : -+ BQ2415X_OTG_PIN_DISABLE; -+ else if (strcmp(attr->attr.name, "stat_pin_enable") == 0) -+ command = val ? BQ2415X_STAT_PIN_ENABLE : -+ BQ2415X_STAT_PIN_DISABLE; -+ else -+ return -EINVAL; -+ -+ ret = bq2415x_exec_command(bq, command); -+ if (ret < 0) -+ return ret; -+ else -+ return count; -+} -+ -+static ssize_t bq2415x_sysfs_show_enable(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, -+ charger); -+ enum bq2415x_command command; -+ int ret; -+ -+ if (strcmp(attr->attr.name, "charge_termination_enable") == 0) -+ command = BQ2415X_CHARGE_TERMINATION_STATUS; -+ else if (strcmp(attr->attr.name, "high_impedance_enable") == 0) -+ command = BQ2415X_HIGH_IMPEDANCE_STATUS; -+ else if (strcmp(attr->attr.name, "otg_pin_enable") == 0) -+ command = BQ2415X_OTG_PIN_STATUS; -+ else if (strcmp(attr->attr.name, "stat_pin_enable") == 0) -+ command = BQ2415X_STAT_PIN_STATUS; -+ else -+ return -EINVAL; -+ -+ ret = bq2415x_exec_command(bq, command); -+ if (ret < 0) -+ return ret; -+ else -+ return sprintf(buf, "%d\n", ret); -+} -+ -+static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); -+static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); -+static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); -+static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); -+static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); -+ -+static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); -+static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); -+static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); -+static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); -+ -+static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode); -+static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer); -+ -+static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO, -+ bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers); -+ -+static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); -+static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); -+static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); -+static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); -+ -+static struct attribute *bq2415x_sysfs_attributes[] = { -+ &dev_attr_current_limit.attr, -+ &dev_attr_weak_battery_voltage.attr, -+ &dev_attr_battery_regulation_voltage.attr, -+ &dev_attr_charge_current.attr, -+ &dev_attr_termination_current.attr, -+ -+ &dev_attr_charge_termination_enable.attr, -+ &dev_attr_high_impedance_enable.attr, -+ &dev_attr_otg_pin_enable.attr, -+ &dev_attr_stat_pin_enable.attr, -+ -+ &dev_attr_mode.attr, -+ &dev_attr_timer.attr, -+ -+ &dev_attr_registers.attr, -+ -+ &dev_attr_otg_status.attr, -+ &dev_attr_charge_status.attr, -+ &dev_attr_boost_status.attr, -+ &dev_attr_fault_status.attr, -+ NULL, -+}; -+ -+static const struct attribute_group bq2415x_sysfs_attr_group = { -+ .attrs = bq2415x_sysfs_attributes, -+}; -+ -+static int bq2415x_sysfs_init(struct bq2415x_device *bq) -+{ -+ return sysfs_create_group(&bq->charger.dev->kobj, -+ &bq2415x_sysfs_attr_group); -+} -+ -+static void bq2415x_sysfs_exit(struct bq2415x_device *bq) -+{ -+ sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group); -+} -+ -+/* bq2415x register */ -+ -+static int bq2415x_probe(struct i2c_client *client, -+ const struct i2c_device_id *id) -+{ -+ int ret; -+ int num; -+ char *name; -+ struct bq2415x_device *bq; -+ -+ if (!client->dev.platform_data) { -+ dev_err(&client->dev, "platform data not set\n"); -+ return -ENODEV; -+ } -+ -+ /* Get new ID for the new device */ -+ ret = idr_pre_get(&bq2415x_id, GFP_KERNEL); -+ if (ret == 0) -+ return -ENOMEM; -+ -+ mutex_lock(&bq2415x_id_mutex); -+ ret = idr_get_new(&bq2415x_id, client, &num); -+ mutex_unlock(&bq2415x_id_mutex); -+ -+ if (ret < 0) -+ return ret; -+ -+ name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); -+ if (!name) { -+ dev_err(&client->dev, "failed to allocate device name\n"); -+ ret = -ENOMEM; -+ goto error_1; -+ } -+ -+ bq = kzalloc(sizeof(*bq), GFP_KERNEL); -+ if (!bq) { -+ dev_err(&client->dev, "failed to allocate device data\n"); -+ ret = -ENOMEM; -+ goto error_2; -+ } -+ -+ i2c_set_clientdata(client, bq); -+ -+ bq->id = num; -+ bq->dev = &client->dev; -+ bq->chip = id->driver_data; -+ bq->name = name; -+ bq->mode = BQ2415X_MODE_NONE; -+ bq->charger_mode = BQ2415X_MODE_NONE; -+ bq->autotimer = 0; -+ bq->automode = 0; -+ -+ memcpy(&bq->init_data, client->dev.platform_data, -+ sizeof(bq->init_data)); -+ -+ bq2415x_reset_chip(bq); -+ -+ ret = bq2415x_power_supply_init(bq); -+ if (ret) { -+ dev_err(bq->dev, "failed to register power supply: %d\n", ret); -+ goto error_3; -+ } -+ -+ ret = bq2415x_sysfs_init(bq); -+ if (ret) { -+ dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); -+ goto error_4; -+ } -+ -+ ret = bq2415x_set_defaults(bq); -+ if (ret) { -+ dev_err(bq->dev, "failed to set default values: %d\n", ret); -+ goto error_5; -+ } -+ -+ if (bq->init_data.set_charger_type_hook) { -+ if (bq->init_data.set_charger_type_hook( -+ bq2415x_set_charger_type, bq)) { -+ bq->automode = 1; -+ dev_info(bq->dev, "automode enabled\n"); -+ } else { -+ bq->automode = -1; -+ dev_info(bq->dev, "automode not supported\n"); -+ } -+ } else { -+ bq->automode = -1; -+ dev_info(bq->dev, "automode not supported\n"); -+ } -+ -+ INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work); -+ bq2415x_set_autotimer(bq, 1); -+ -+ dev_info(bq->dev, "driver registred\n"); -+ return 0; -+ -+error_5: -+ bq2415x_sysfs_exit(bq); -+error_4: -+ bq2415x_power_supply_exit(bq); -+error_3: -+ kfree(bq); -+error_2: -+ kfree(name); -+error_1: -+ mutex_lock(&bq2415x_id_mutex); -+ idr_remove(&bq2415x_id, num); -+ mutex_unlock(&bq2415x_id_mutex); -+ -+ return ret; -+} -+ -+/* bq2415x unregister */ -+ -+static int bq2415x_remove(struct i2c_client *client) -+{ -+ struct bq2415x_device *bq = i2c_get_clientdata(client); -+ -+ if (bq->init_data.set_charger_type_hook) -+ bq->init_data.set_charger_type_hook(NULL, NULL); -+ -+ bq2415x_sysfs_exit(bq); -+ bq2415x_power_supply_exit(bq); -+ -+ bq2415x_reset_chip(bq); -+ -+ mutex_lock(&bq2415x_id_mutex); -+ idr_remove(&bq2415x_id, bq->id); -+ mutex_unlock(&bq2415x_id_mutex); -+ -+ dev_info(bq->dev, "driver unregistred\n"); -+ -+ kfree(bq->name); -+ kfree(bq); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id bq2415x_i2c_id_table[] = { -+ { "bq2415x", BQUNKNOWN }, -+ { "bq24150", BQ24150 }, -+ { "bq24150a", BQ24150A }, -+ { "bq24151", BQ24151 }, -+ { "bq24151a", BQ24151A }, -+ { "bq24152", BQ24152 }, -+ { "bq24153", BQ24153 }, -+ { "bq24153a", BQ24153A }, -+ { "bq24155", BQ24155 }, -+ { "bq24156", BQ24156 }, -+ { "bq24156a", BQ24156A }, -+ { "bq24158", BQ24158 }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table); -+ -+static struct i2c_driver bq2415x_driver = { -+ .driver = { -+ .name = "bq2415x-charger", -+ }, -+ .probe = bq2415x_probe, -+ .remove = bq2415x_remove, -+ .id_table = bq2415x_i2c_id_table, -+}; -+ -+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("Pali Rohár "); -+MODULE_DESCRIPTION("bq2415x charger driver"); -+MODULE_LICENSE("GPL"); ---- /dev/null 2012-04-29 08:55:34.366920721 +0200 -+++ kernel-power/include/linux/power/bq2415x_charger.h 2012-01-27 15:47:45.689585447 +0100 -@@ -0,0 +1,78 @@ -+/* -+ bq2415x_charger.h - bq2415x charger driver -+ Copyright (C) 2011-2012 Pali Rohár -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ 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. -+ -+ You should have received a copy of the GNU General Public License along -+ with this program; if not, write to the Free Software Foundation, Inc., -+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -+*/ -+ -+#ifndef BQ2415X_CHARGER_H -+#define BQ2415X_CHARGER_H -+ -+/* -+ This is platform data for bq2415x chip. It contains default board volatages -+ and currents which can be also later configured via sysfs. If value is -1 -+ then default chip value (specified in datasheet) will be used. -+ -+ Value resistor_sense is needed for for configuring charge and termination -+ current. It it is less or equal to zero, configuring charge and termination -+ current will not be possible. -+ -+ Function set_charger_type_hook is needed for automode (setting correct -+ current limit when charger is connected/disconnected). When is NULL, -+ automode function is disabled. When is not NULL, it must have this prototype: -+ -+ int (*set_charger_type_hook)(void (*hook)(int type, void *data), void *data) -+ -+ and bq2415x driver will call it as: -+ -+ platform_data->set_charger_type_hook(bq_hook_function, bq_private_data); -+ -+ Board/platform function set_charger_type_hook return non zero when hook -+ function was successfull registred. Platform code should call that hook -+ function (which get from pointer, with data) every time when charger was -+ connected/disconnected. bq driver then set correct current limit. -+ -+ Hook function has this prototype: -+ -+ void hook(int type, void *data); -+ -+ type is: -+ 0 - for unknown or none charger (max current limit is 100mA) -+ 1 - for usb host/hub charger (max current limit is 500mA) -+ 2 - for dedicated charger (unlimited) -+ -+ data is pointer to bq_private_data (which get from set_charger_type_hook) -+ -+ When bq driver is being unloaded, it call function: -+ -+ platform_data->set_charger_type_hook(NULL, NULL); -+ -+ After thet board/platform code must not call bq hook function! It is -+ possible that pointer to hook function will not be valid. -+ -+*/ -+ -+struct bq2415x_platform_data { -+ int current_limit; /* mA */ -+ int weak_battery_voltage; /* mV */ -+ int battery_regulation_voltage; /* mV */ -+ int charge_current; /* mA */ -+ int termination_current; /* mA */ -+ int resistor_sense; /* m ohm */ -+ int (*set_charger_type_hook)(void (*hook)(int type, void *data), -+ void *data); -+}; -+ -+#endif ---- kernel-power/drivers/power/Makefile.orig 2012-04-29 18:03:10.751810073 +0200 -+++ kernel-power/drivers/power/Makefile 2012-04-29 18:03:52.207808860 +0200 -@@ -25,3 +25,4 @@ obj-$(CONFIG_TWL4030_BCI_BATTERY) += twl - obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o - obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o - obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o -+obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o ---- kernel-power/drivers/power/Kconfig.orig 2012-04-29 18:01:48.819812466 +0200 -+++ kernel-power/drivers/power/Kconfig 2012-04-29 18:03:06.043810209 +0200 -@@ -75,4 +75,10 @@ config BATTERY_BQ27x00 - help - Say Y here to enable support for batteries with BQ27200(I2C) chip. - -+config CHARGER_BQ2415X -+ tristate "BQ2415X charger driver" -+ depends on I2C -+ help -+ Say Y here to enable support for chargers with BQ2415X(I2C) chip. -+ - endif # POWER_SUPPLY diff --git a/kernel-power-2.6.28/debian/patches/bq24150-rx51.patch b/kernel-power-2.6.28/debian/patches/bq24150-rx51.patch deleted file mode 100644 index e32624b..0000000 --- a/kernel-power-2.6.28/debian/patches/bq24150-rx51.patch +++ /dev/null @@ -1,249 +0,0 @@ ---- kernel-power/drivers/usb/otg/twl4030-usb.c.orig 2012-04-29 17:15:00.195894587 +0200 -+++ kernel-power/drivers/usb/otg/twl4030-usb.c 2012-04-29 17:18:06.815889128 +0200 -@@ -37,6 +37,8 @@ - #include - #include - -+#include -+#include - - /* Register defines */ - -@@ -265,6 +265,9 @@ struct twl4030_usb { - u8 linkstat; - u8 asleep; - bool irq_enabled; -+ -+ struct delayed_work work; -+ int work_inited; - }; - - /* internal define on top of container_of */ -@@ -365,6 +367,14 @@ static enum linkstat twl4030_usb_linksta - dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", - status, status, linkstat); - -+ if (machine_is_nokia_rx51() && rx51_with_charger_detection()) { -+ rx51_set_charger(linkstat == USB_LINK_VBUS); -+ if (twl->work_inited && linkstat == USB_LINK_VBUS) { -+ printk("rx51 - schedule delayed work in 2 seconds - rx51_detect_wallcharger\n"); -+ schedule_delayed_work(&twl->work, 2 * HZ); /* 2 seconds should be enought */ -+ } -+ } -+ - /* REVISIT this assumes host and peripheral controllers - * are registered, and that both are active... - */ -@@ -771,6 +773,11 @@ static int __init twl4030_usb_probe(stru - */ - twl4030_usb_irq(twl->irq, twl); - -+ if (machine_is_nokia_rx51()) { -+ INIT_DELAYED_WORK(&twl->work, rx51_detect_wallcharger); -+ twl->work_inited = 1; -+ } -+ - dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); - return 0; - } ---- kernel-power/drivers/usb/musb/omap2430.c.orig 2012-04-29 16:34:10.123966221 +0200 -+++ kernel-power/drivers/usb/musb/omap2430.c 2012-04-29 16:50:48.611937027 +0200 -@@ -38,6 +38,8 @@ - #include - #include - -+#include -+ - #include - - #include "musb_core.h" -@@ -230,6 +232,7 @@ int musb_platform_set_mode(struct musb * - - if (machine_is_nokia_rx51()) { - u8 testmode; -+ rx51_enable_charger_detection(0); - - musb_platform_resume(musb); - -@@ -255,6 +259,7 @@ int musb_platform_set_mode(struct musb * - - musb_writeb(musb->mregs, MUSB_TESTMODE, 0); - musb_platform_suspend(musb); -+ rx51_enable_charger_detection(1); - } - - otg_set_peripheral(musb->xceiv, &musb->g); -@@ -434,6 +438,9 @@ void musb_save_ctx_and_suspend(struct us - - musb->is_charger = 0; - -+ if (machine_is_nokia_rx51() && rx51_with_charger_detection()) -+ rx51_set_wallcharger(0); -+ - /* clear constraints */ - if (musb->board && musb->board->set_pm_limits) - musb->board->set_pm_limits(musb->controller, 0); ---- kernel-power/drivers/usb/musb/musb_core.c.orig 2012-04-29 16:57:27.407925369 +0200 -+++ kernel-power/drivers/usb/musb/musb_core.c 2012-04-29 17:13:39.083896956 +0200 -@@ -105,6 +105,8 @@ - #include - #endif - -+#include -+ - #include "musb_core.h" - - -@@ -223,6 +224,10 @@ static int musb_charger_detect(struct mu - u8 vdat = 0; - u8 r; - -+ printk("musb_charger_detect (enabled = %d)\n", rx51_with_charger_detection()); -+ if (machine_is_nokia_rx51() && !rx51_with_charger_detection()) -+ return 0; -+ - msleep(5); - - /* Using ulpi with musb is quite tricky. The following code -@@ -308,6 +315,8 @@ static int musb_charger_detect(struct mu - /* Regulators off */ - otg_set_suspend(musb->xceiv, 1); - musb->is_charger = 1; -+ if (machine_is_nokia_rx51() && rx51_with_charger_detection()) -+ rx51_set_wallcharger(1); - } else { - /* enable interrupts */ - musb_writeb(musb->mregs, MUSB_INTRUSBE, ctx.intrusbe); -@@ -330,6 +330,14 @@ static int musb_charger_detect(struct mu - return vdat; - } - -+void rx51_detect_wallcharger(void *work) -+{ -+ printk("rx51_detect_wallcharger (the_musb = %p)\n", the_musb); -+ if (the_musb) -+ musb_charger_detect(the_musb); -+} -+EXPORT_SYMBOL(rx51_detect_wallcharger); -+ - /*-------------------------------------------------------------------------*/ - - static inline struct musb *dev_to_musb(struct device *dev) ---- kernel-power/arch/arm/plat-omap/include/mach/board-rx51.h.orig 2012-04-29 16:39:03.927957628 +0200 -+++ kernel-power/arch/arm/plat-omap/include/mach/board-rx51.h 2012-04-29 17:14:50.923894858 +0200 -@@ -39,6 +39,12 @@ extern void rx51_usb_init(void); - static inline void rx51_usb_init(void) { } - #endif - -+extern void rx51_set_charger(int connected); -+extern void rx51_set_wallcharger(int connected); -+extern void rx51_enable_charger_detection(int enable); -+extern void rx51_detect_wallcharger(void *work); -+extern int rx51_with_charger_detection(void); -+ - extern void omap_bt_init(struct omap_bluetooth_config *bt_config); - - struct omap_sdrc_params *rx51_get_sdram_timings(void); ---- kernel-power/arch/arm/mach-omap2/board-rx51-peripherals.c.orig 2012-04-29 17:18:56.995887664 +0200 -+++ kernel-power/arch/arm/mach-omap2/board-rx51-peripherals.c 2012-04-29 17:46:57.487838528 +0200 -@@ -38,6 +38,8 @@ - #include - - #include "../../../drivers/input/lirc/lirc_rx51.h" -+#include -+#include - - #define RX51_DEBUG_BASE 0x08000000 /* debug board */ - #define RX51_ETHR_START RX51_DEBUG_BASE -@@ -563,6 +565,80 @@ static struct i2c_board_info __initdata - }, - }; - -+static int rx51_charger_type; -+static int rx51_charger_connected; -+static int rx51_wallcharger_connected; -+static int rx51_charger_detection = 1; -+ -+static void *rx51_charger_hook_data; -+static void (*rx51_charger_hook)(int type, void *data); -+ -+static int rx51_charger_set_hook(void (*hook)(int type, void *data), void *data) -+{ -+ rx51_charger_hook = hook; -+ rx51_charger_hook_data = data; -+ return 1; -+} -+ -+static void rx51_update_charger_type(void) -+{ -+ int type; -+ if (rx51_charger_connected && rx51_wallcharger_connected) -+ type = 2; /* wallcharger */ -+ else if (rx51_charger_connected) -+ type = 1; /* usb charger */ -+ else -+ type = 0; /* no charger */ -+ -+ if (rx51_charger_type == type) -+ return; -+ -+ printk("rx51_update_charger_type (type = %d)\n", type); -+ rx51_charger_type = type; -+ -+ if (rx51_charger_hook) -+ rx51_charger_hook(rx51_charger_type, rx51_charger_hook_data); -+} -+ -+void rx51_set_charger(int connected) -+{ -+ printk("rx51_set_charger (connected = %d)\n", connected); -+ rx51_charger_connected = connected; -+ rx51_update_charger_type(); -+} -+EXPORT_SYMBOL(rx51_set_charger); -+ -+void rx51_set_wallcharger(int connected) -+{ -+ printk("rx51_set_wallcharger (connected = %d)\n", connected); -+ rx51_wallcharger_connected = connected; -+ rx51_update_charger_type(); -+} -+EXPORT_SYMBOL(rx51_set_wallcharger); -+ -+void rx51_enable_charger_detection(int enable) -+{ -+ printk("rx51_enable_charger_detection (enable = %d)\n", enable); -+ rx51_charger_detection = enable; -+} -+EXPORT_SYMBOL(rx51_enable_charger_detection); -+ -+int rx51_with_charger_detection(void) -+{ -+ return rx51_charger_detection; -+} -+EXPORT_SYMBOL(rx51_with_charger_detection); -+ -+static struct bq2415x_platform_data rx51_bq24150_platform_data = { -+ .current_limit = 100, /* mA */ -+ .weak_battery_voltage = 3400, /* mV */ -+ .battery_regulation_voltage = 4200, /* mV */ -+ .charge_current = 950, /*1200*/ /* mA */ -+ .termination_current = 150, /*400*/ /* mA */ -+ .resistor_sense = 68, /* m ohm */ -+ .set_charger_type_hook = &rx51_charger_set_hook, -+}; -+ - static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_2[] = { - { - I2C_BOARD_INFO("lp5523", 0x32), -@@ -575,6 +636,10 @@ static struct i2c_board_info __initdata - { - I2C_BOARD_INFO("bq27200", 0x55), - }, -+ { -+ I2C_BOARD_INFO("bq24150", 0x6b), -+ .platform_data = &rx51_bq24150_platform_data, -+ }, - }; - - static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_3[] = { diff --git a/kernel-power-2.6.28/debian/patches/bq2415x_charger.patch b/kernel-power-2.6.28/debian/patches/bq2415x_charger.patch new file mode 100644 index 0000000..4d5c51f --- /dev/null +++ b/kernel-power-2.6.28/debian/patches/bq2415x_charger.patch @@ -0,0 +1,1648 @@ +--- /dev/null 2012-04-29 08:55:34.366920721 +0200 ++++ kernel-power/drivers/power/bq2415x_charger.c 2012-04-29 17:53:57.799826239 +0200 +@@ -0,0 +1,1564 @@ ++/* ++ bq2415x_charger.c - bq2415x charger driver ++ Copyright (C) 2011-2012 Pali Rohár ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++*/ ++ ++/* ++ Datasheets: ++ http://www.ti.com/product/bq24150 ++ http://www.ti.com/product/bq24150a ++ http://www.ti.com/product/bq24152 ++ http://www.ti.com/product/bq24153 ++ http://www.ti.com/product/bq24153a ++ http://www.ti.com/product/bq24155 ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define BQ2415X_TIMER_TIMEOUT 10 ++ ++#define BQ2415X_REG_STATUS 0x00 ++#define BQ2415X_REG_CONTROL 0x01 ++#define BQ2415X_REG_VOLTAGE 0x02 ++#define BQ2415X_REG_VENDER 0x03 ++#define BQ2415X_REG_CURRENT 0x04 ++ ++/* reset state for all registers */ ++#define BQ2415X_RESET_STATUS BIT(6) ++#define BQ2415X_RESET_CONTROL (BIT(4)|BIT(5)) ++#define BQ2415X_RESET_VOLTAGE (BIT(1)|BIT(3)) ++#define BQ2415X_RESET_CURRENT (BIT(0)|BIT(3)|BIT(7)) ++ ++/* status register */ ++#define BQ2415X_BIT_TMR_RST 7 ++#define BQ2415X_BIT_OTG 7 ++#define BQ2415X_BIT_EN_STAT 6 ++#define BQ2415X_MASK_STAT (BIT(4)|BIT(5)) ++#define BQ2415X_SHIFT_STAT 4 ++#define BQ2415X_BIT_BOOST 3 ++#define BQ2415X_MASK_FAULT (BIT(0)|BIT(1)|BIT(2)) ++#define BQ2415X_SHIFT_FAULT 0 ++ ++/* control register */ ++#define BQ2415X_MASK_LIMIT (BIT(6)|BIT(7)) ++#define BQ2415X_SHIFT_LIMIT 6 ++#define BQ2415X_MASK_VLOWV (BIT(4)|BIT(5)) ++#define BQ2415X_SHIFT_VLOWV 4 ++#define BQ2415X_BIT_TE 3 ++#define BQ2415X_BIT_CE 2 ++#define BQ2415X_BIT_HZ_MODE 1 ++#define BQ2415X_BIT_OPA_MODE 0 ++ ++/* voltage register */ ++#define BQ2415X_MASK_VO (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) ++#define BQ2415X_SHIFT_VO 2 ++#define BQ2415X_BIT_OTG_PL 1 ++#define BQ2415X_BIT_OTG_EN 0 ++ ++/* vender register */ ++#define BQ2415X_MASK_VENDER (BIT(5)|BIT(6)|BIT(7)) ++#define BQ2415X_SHIFT_VENDER 5 ++#define BQ2415X_MASK_PN (BIT(3)|BIT(4)) ++#define BQ2415X_SHIFT_PN 3 ++#define BQ2415X_MASK_REVISION (BIT(0)|BIT(1)|BIT(2)) ++#define BQ2415X_SHIFT_REVISION 0 ++ ++/* current register */ ++/* RESET BIT(7) */ ++#define BQ2415X_MASK_VI_CHRG (BIT(4)|BIT(5)|BIT(6)|BIT(7)) ++#define BQ2415X_SHIFT_VI_CHRG 4 ++/* N/A BIT(3) */ ++#define BQ2415X_MASK_VI_TERM (BIT(0)|BIT(1)|BIT(2)|BIT(7)) ++#define BQ2415X_SHIFT_VI_TERM 0 ++ ++ ++enum bq2415x_command { ++ BQ2415X_TIMER_RESET, ++ BQ2415X_OTG_STATUS, ++ BQ2415X_STAT_PIN_STATUS, ++ BQ2415X_STAT_PIN_ENABLE, ++ BQ2415X_STAT_PIN_DISABLE, ++ BQ2415X_CHARGE_STATUS, ++ BQ2415X_BOOST_STATUS, ++ BQ2415X_FAULT_STATUS, ++ ++ BQ2415X_CHARGE_TERMINATION_STATUS, ++ BQ2415X_CHARGE_TERMINATION_ENABLE, ++ BQ2415X_CHARGE_TERMINATION_DISABLE, ++ BQ2415X_CHARGER_STATUS, ++ BQ2415X_CHARGER_ENABLE, ++ BQ2415X_CHARGER_DISABLE, ++ BQ2415X_HIGH_IMPEDANCE_STATUS, ++ BQ2415X_HIGH_IMPEDANCE_ENABLE, ++ BQ2415X_HIGH_IMPEDANCE_DISABLE, ++ BQ2415X_BOOST_MODE_STATUS, ++ BQ2415X_BOOST_MODE_ENABLE, ++ BQ2415X_BOOST_MODE_DISABLE, ++ ++ BQ2415X_OTG_LEVEL, ++ BQ2415X_OTG_ACTIVATE_HIGH, ++ BQ2415X_OTG_ACTIVATE_LOW, ++ BQ2415X_OTG_PIN_STATUS, ++ BQ2415X_OTG_PIN_ENABLE, ++ BQ2415X_OTG_PIN_DISABLE, ++ ++ BQ2415X_VENDER_CODE, ++ BQ2415X_PART_NUMBER, ++ BQ2415X_REVISION, ++}; ++ ++enum bq2415x_chip { ++ BQUNKNOWN, ++ BQ24150, ++ BQ24150A, ++ BQ24151, ++ BQ24151A, ++ BQ24152, ++ BQ24153, ++ BQ24153A, ++ BQ24155, ++ BQ24156, ++ BQ24156A, ++ BQ24158, ++}; ++ ++enum bq2415x_mode { ++ BQ2415X_MODE_NONE, ++ BQ2415X_MODE_HOST_CHARGER, ++ BQ2415X_MODE_DEDICATED_CHARGER, ++ BQ2415X_MODE_BOOST, ++}; ++ ++static char *bq2415x_chip_name[] = { ++ "unknown", ++ "bq24150", ++ "bq24150a", ++ "bq24151", ++ "bq24151a", ++ "bq24152", ++ "bq24153", ++ "bq24153a", ++ "bq24155", ++ "bq24156", ++ "bq24156a", ++ "bq24158", ++}; ++ ++struct bq2415x_device { ++ struct device *dev; ++ struct bq2415x_platform_data init_data; ++ struct power_supply charger; ++ struct delayed_work work; ++ enum bq2415x_mode charger_mode; /* mode reported by hook function */ ++ enum bq2415x_mode mode; /* actual setted mode */ ++ enum bq2415x_chip chip; ++ char *model; ++ char *name; ++ int autotimer; /* 1 - if driver automatically reset timer, 0 - not */ ++ int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */ ++ int id; ++}; ++ ++static DEFINE_IDR(bq2415x_id); ++ ++static DEFINE_MUTEX(bq2415x_id_mutex); ++static DEFINE_MUTEX(bq2415x_timer_mutex); ++static DEFINE_MUTEX(bq2415x_i2c_mutex); ++ ++/* i2c read functions */ ++ ++static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg) ++{ ++ struct i2c_client *client = to_i2c_client(bq->dev); ++ struct i2c_msg msg[2]; ++ u8 val; ++ int ret; ++ ++ if (!client->adapter) ++ return -ENODEV; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].buf = ® ++ msg[0].len = sizeof(reg); ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].buf = &val; ++ msg[1].len = sizeof(val); ++ ++ mutex_lock(&bq2415x_i2c_mutex); ++ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); ++ mutex_unlock(&bq2415x_i2c_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ return val; ++} ++ ++static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg, ++ u8 mask, u8 shift) ++{ ++ int ret; ++ ++ if (shift > 8) ++ return -EINVAL; ++ ++ ret = bq2415x_i2c_read(bq, reg); ++ if (ret < 0) ++ return ret; ++ else ++ return (ret & mask) >> shift; ++} ++ ++static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit) ++{ ++ if (bit > 8) ++ return -EINVAL; ++ else ++ return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit); ++} ++ ++/* i2c write functions */ ++ ++static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val) ++{ ++ struct i2c_client *client = to_i2c_client(bq->dev); ++ struct i2c_msg msg[1]; ++ u8 data[2]; ++ int ret; ++ ++ data[0] = reg; ++ data[1] = val; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].buf = data; ++ msg[0].len = ARRAY_SIZE(data); ++ ++ mutex_lock(&bq2415x_i2c_mutex); ++ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); ++ mutex_unlock(&bq2415x_i2c_mutex); ++ ++ /* i2c_transfer returns number of messages transferred */ ++ if (ret < 0) ++ return ret; ++ else if (ret != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val, ++ u8 mask, u8 shift) ++{ ++ int ret; ++ ++ if (shift > 8) ++ return -EINVAL; ++ ++ ret = bq2415x_i2c_read(bq, reg); ++ if (ret < 0) ++ return ret; ++ ++ ret &= ~mask; ++ ret |= val << shift; ++ ++ return bq2415x_i2c_write(bq, reg, ret); ++} ++ ++static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg, ++ bool val, u8 bit) ++{ ++ if (bit > 8) ++ return -EINVAL; ++ else ++ return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit); ++} ++ ++/* global exec command function */ ++ ++static int bq2415x_exec_command(struct bq2415x_device *bq, ++ enum bq2415x_command command) ++{ ++ int ret; ++ switch (command) { ++ case BQ2415X_TIMER_RESET: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, ++ 1, BQ2415X_BIT_TMR_RST); ++ case BQ2415X_OTG_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, ++ BQ2415X_BIT_OTG); ++ case BQ2415X_STAT_PIN_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, ++ BQ2415X_BIT_EN_STAT); ++ case BQ2415X_STAT_PIN_ENABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1, ++ BQ2415X_BIT_EN_STAT); ++ case BQ2415X_STAT_PIN_DISABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0, ++ BQ2415X_BIT_EN_STAT); ++ case BQ2415X_CHARGE_STATUS: ++ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS, ++ BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT); ++ case BQ2415X_BOOST_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS, ++ BQ2415X_BIT_BOOST); ++ case BQ2415X_FAULT_STATUS: ++ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS, ++ BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT); ++ ++ case BQ2415X_CHARGE_TERMINATION_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, ++ BQ2415X_BIT_TE); ++ case BQ2415X_CHARGE_TERMINATION_ENABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 1, BQ2415X_BIT_TE); ++ case BQ2415X_CHARGE_TERMINATION_DISABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 0, BQ2415X_BIT_TE); ++ case BQ2415X_CHARGER_STATUS: ++ ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, ++ BQ2415X_BIT_CE); ++ if (ret < 0) ++ return ret; ++ else ++ return ret > 0 ? 0 : 1; ++ case BQ2415X_CHARGER_ENABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 0, BQ2415X_BIT_CE); ++ case BQ2415X_CHARGER_DISABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 1, BQ2415X_BIT_CE); ++ case BQ2415X_HIGH_IMPEDANCE_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, ++ BQ2415X_BIT_HZ_MODE); ++ case BQ2415X_HIGH_IMPEDANCE_ENABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 1, BQ2415X_BIT_HZ_MODE); ++ case BQ2415X_HIGH_IMPEDANCE_DISABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 0, BQ2415X_BIT_HZ_MODE); ++ case BQ2415X_BOOST_MODE_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL, ++ BQ2415X_BIT_OPA_MODE); ++ case BQ2415X_BOOST_MODE_ENABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 1, BQ2415X_BIT_OPA_MODE); ++ case BQ2415X_BOOST_MODE_DISABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, ++ 0, BQ2415X_BIT_OPA_MODE); ++ ++ case BQ2415X_OTG_LEVEL: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE, ++ BQ2415X_BIT_OTG_PL); ++ case BQ2415X_OTG_ACTIVATE_HIGH: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, ++ 1, BQ2415X_BIT_OTG_PL); ++ case BQ2415X_OTG_ACTIVATE_LOW: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, ++ 0, BQ2415X_BIT_OTG_PL); ++ case BQ2415X_OTG_PIN_STATUS: ++ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE, ++ BQ2415X_BIT_OTG_EN); ++ case BQ2415X_OTG_PIN_ENABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, ++ 1, BQ2415X_BIT_OTG_EN); ++ case BQ2415X_OTG_PIN_DISABLE: ++ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE, ++ 0, BQ2415X_BIT_OTG_EN); ++ ++ case BQ2415X_VENDER_CODE: ++ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, ++ BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER); ++ case BQ2415X_PART_NUMBER: ++ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, ++ BQ2415X_MASK_PN, BQ2415X_SHIFT_PN); ++ case BQ2415X_REVISION: ++ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER, ++ BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++/* global detect chip */ ++ ++static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq) ++{ ++ struct i2c_client *client = to_i2c_client(bq->dev); ++ int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER); ++ ++ if (ret < 0) ++ return ret; ++ ++ switch (client->addr) { ++ case 0x6b: ++ switch (ret) { ++ case 0: ++ if (bq->chip == BQ24151A) ++ return bq->chip; ++ else ++ return BQ24151; ++ case 1: ++ if (bq->chip == BQ24150A || ++ bq->chip == BQ24152 || ++ bq->chip == BQ24155) ++ return bq->chip; ++ else ++ return BQ24150; ++ case 2: ++ if (bq->chip == BQ24153A) ++ return bq->chip; ++ else ++ return BQ24153; ++ default: ++ return BQUNKNOWN; ++ } ++ break; ++ ++ case 0x6a: ++ switch (ret) { ++ case 0: ++ if (bq->chip == BQ24156A) ++ return bq->chip; ++ else ++ return BQ24156; ++ case 2: ++ return BQ24158; ++ default: ++ return BQUNKNOWN; ++ } ++ break; ++ } ++ ++ return BQUNKNOWN; ++} ++ ++static int bq2415x_detect_revision(struct bq2415x_device *bq) ++{ ++ int ret = bq2415x_exec_command(bq, BQ2415X_REVISION); ++ int chip = bq2415x_detect_chip(bq); ++ if (ret < 0 || chip < 0) ++ return -1; ++ ++ switch (chip) { ++ case BQ24150: ++ case BQ24150A: ++ case BQ24151: ++ case BQ24151A: ++ case BQ24152: ++ if (ret >= 0 && ret <= 3) ++ return ret; ++ else ++ return -1; ++ ++ case BQ24153: ++ case BQ24153A: ++ case BQ24156: ++ case BQ24156A: ++ case BQ24158: ++ if (ret == 3) ++ return 0; ++ else if (ret == 1) ++ return 1; ++ else ++ return -1; ++ ++ case BQ24155: ++ if (ret == 3) ++ return 3; ++ else ++ return -1; ++ ++ case BQUNKNOWN: ++ return -1; ++ } ++ ++ return -1; ++} ++ ++static int bq2415x_get_vender_code(struct bq2415x_device *bq) ++{ ++ int ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE); ++ if (ret < 0) ++ return 0; ++ else /* convert to binary */ ++ return (ret & 0x1) + ++ ((ret >> 1) & 0x1) * 10 + ++ ((ret >> 2) & 0x1) * 100; ++} ++ ++/* global other functions */ ++ ++static void bq2415x_reset_chip(struct bq2415x_device *bq) ++{ ++ bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT); ++ bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE); ++ bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL); ++ bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS); ++} ++ ++static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA) ++{ ++ int val; ++ if (mA <= 100) ++ val = 0; ++ else if (mA <= 500) ++ val = 1; ++ else if (mA <= 800) ++ val = 2; ++ else ++ val = 3; ++ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, ++ BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); ++} ++ ++static int bq2415x_get_current_limit(struct bq2415x_device *bq) ++{ ++ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, ++ BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT); ++ if (ret < 0) ++ return ret; ++ else if (ret == 0) ++ return 100; ++ else if (ret == 1) ++ return 500; ++ else if (ret == 2) ++ return 800; ++ else if (ret == 3) ++ return 1800; ++ else ++ return -EINVAL; ++} ++ ++static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV) ++{ ++ int val = mV/100 + (mV%100 > 0 ? 1 : 0) - 34; ++ ++ if (val < 0) ++ val = 0; ++ else if (val > 3) ++ val = 3; ++ ++ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val, ++ BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); ++} ++ ++static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq) ++{ ++ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL, ++ BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV); ++ if (ret < 0) ++ return ret; ++ else ++ return 100 * (34 + ret); ++} ++ ++static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq, ++ int mV) ++{ ++ int val = (mV/10 + (mV%10 > 0 ? 1 : 0) - 350) / 2; ++ ++ if (val < 0) ++ val = 0; ++ else if (val > 94) /* FIXME: Max is 94 or 122 ? */ ++ return -EINVAL; ++ ++ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val, ++ BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); ++} ++ ++static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq) ++{ ++ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE, ++ BQ2415X_MASK_VO, BQ2415X_SHIFT_VO); ++ if (ret < 0) ++ return ret; ++ else ++ return 10 * (350 + 2*ret); ++} ++ ++static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA) ++{ ++ int val; ++ if (bq->init_data.resistor_sense <= 0) ++ return -ENOSYS; ++ ++ val = (mA * bq->init_data.resistor_sense - 37400); ++ val = val/6800 + (val%6800 > 0 ? 1 : 0); ++ ++ if (val < 0) ++ val = 0; ++ else if (val > 7) ++ val = 7; ++ ++ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val, ++ BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG); ++} ++ ++static int bq2415x_get_charge_current(struct bq2415x_device *bq) ++{ ++ int ret; ++ if (bq->init_data.resistor_sense <= 0) ++ return -ENOSYS; ++ ++ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT, ++ BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG); ++ if (ret < 0) ++ return ret; ++ else ++ return (37400 + 6800*ret) / bq->init_data.resistor_sense; ++} ++ ++static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA) ++{ ++ int val; ++ if (bq->init_data.resistor_sense <= 0) ++ return -ENOSYS; ++ ++ val = (mA * bq->init_data.resistor_sense - 3400); ++ val = val/3400 + (val%3400 > 0 ? 1 : 0); ++ ++ if (val < 0) ++ val = 0; ++ else if (val > 7) ++ val = 7; ++ ++ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val, ++ BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM); ++} ++ ++static int bq2415x_get_termination_current(struct bq2415x_device *bq) ++{ ++ int ret; ++ if (bq->init_data.resistor_sense <= 0) ++ return -ENOSYS; ++ ++ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT, ++ BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM); ++ if (ret < 0) ++ return ret; ++ else ++ return (3400 + 3400*ret) / bq->init_data.resistor_sense; ++} ++ ++#define bq2415x_set_default_value(bq, value) \ ++ do { \ ++ int ret = 0; \ ++ if (bq->init_data.value != -1) \ ++ ret = bq2415x_set_##value(bq, bq->init_data.value); \ ++ if (ret < 0) \ ++ return ret; \ ++ } while (0) ++ ++static int bq2415x_set_defaults(struct bq2415x_device *bq) ++{ ++ bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); ++ bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); ++ bq2415x_set_default_value(bq, current_limit); ++ bq2415x_set_default_value(bq, weak_battery_voltage); ++ bq2415x_set_default_value(bq, battery_regulation_voltage); ++ if (bq->init_data.resistor_sense > 0) { ++ bq2415x_set_default_value(bq, charge_current); ++ bq2415x_set_default_value(bq, termination_current); ++ bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE); ++ } ++ bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); ++ return 0; ++} ++ ++#undef bq2415x_set_default_value ++ ++/* charger mode functions */ ++ ++static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) ++{ ++ int ret = 0; ++ int charger = 0; ++ ++ if (mode == BQ2415X_MODE_NONE || ++ mode == BQ2415X_MODE_HOST_CHARGER || ++ mode == BQ2415X_MODE_DEDICATED_CHARGER) ++ charger = 1; ++ ++ if (charger) ++ ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE); ++ else ++ ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); ++ ++ if (ret < 0) ++ return ret; ++ ++ switch (mode) { ++ case BQ2415X_MODE_NONE: ++ dev_info(bq->dev, "mode: N/A\n"); ++ ret = bq2415x_set_current_limit(bq, 100); ++ break; ++ case BQ2415X_MODE_HOST_CHARGER: ++ dev_info(bq->dev, "mode: Host/HUB charger\n"); ++ ret = bq2415x_set_current_limit(bq, 500); ++ break; ++ case BQ2415X_MODE_DEDICATED_CHARGER: ++ dev_info(bq->dev, "mode: Dedicated charger\n"); ++ ret = bq2415x_set_current_limit(bq, 1800); ++ break; ++ case BQ2415X_MODE_BOOST: /* Boost mode */ ++ dev_info(bq->dev, "mode: Boost\n"); ++ ret = bq2415x_set_current_limit(bq, 100); ++ break; ++ } ++ ++ if (ret < 0) ++ return ret; ++ ++ if (charger) ++ ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE); ++ else ++ ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE); ++ ++ if (ret < 0) ++ return ret; ++ ++ bq->mode = mode; ++ return 0; ++ ++} ++ ++static void bq2415x_set_charger_type(int type, void *data) ++{ ++ struct bq2415x_device *bq = data; ++ ++ if (!bq) ++ return; ++ ++ switch (type) { ++ case 0: ++ bq->charger_mode = BQ2415X_MODE_NONE; ++ break; ++ case 1: ++ bq->charger_mode = BQ2415X_MODE_HOST_CHARGER; ++ break; ++ case 2: ++ bq->charger_mode = BQ2415X_MODE_DEDICATED_CHARGER; ++ break; ++ default: ++ return; ++ } ++ ++ if (bq->automode < 1) ++ return; ++ ++ /* TODO: Detect USB Host mode */ ++ ++ bq2415x_set_mode(bq, bq->charger_mode); ++ ++} ++ ++/* timer functions */ ++ ++static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state) ++{ ++ mutex_lock(&bq2415x_timer_mutex); ++ ++ if (bq->autotimer == state) { ++ mutex_unlock(&bq2415x_timer_mutex); ++ return; ++ } ++ ++ bq->autotimer = state; ++ ++ if (state) { ++ schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ); ++ bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); ++ } else { ++ cancel_delayed_work_sync(&bq->work); ++ } ++ ++ mutex_unlock(&bq2415x_timer_mutex); ++} ++ ++static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) ++{ ++ dev_err(bq->dev, "%s\n", msg); ++ if (bq->automode > 0) ++ bq->automode = 0; ++ bq2415x_set_mode(bq, BQ2415X_MODE_NONE); ++ bq2415x_set_autotimer(bq, 0); ++} ++ ++static void bq2415x_timer_work(struct work_struct *work) ++{ ++ struct bq2415x_device *bq = container_of(work, struct bq2415x_device, ++ work.work); ++ int ret, error, boost; ++ ++ if (!bq->autotimer) ++ return; ++ ++ ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); ++ if (ret < 0) { ++ bq2415x_timer_error(bq, "Reseting timer failed"); ++ return; ++ } ++ ++ boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS); ++ if (boost < 0) { ++ bq2415x_timer_error(bq, "Unknow error"); ++ return; ++ } ++ ++ error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS); ++ if (error < 0) { ++ bq2415x_timer_error(bq, "Unknow error"); ++ return; ++ } ++ ++ if (boost) { ++ switch (error) { ++ case 0: /* No error */ ++ break; ++ case 6: /* Timer expired */ ++ dev_err(bq->dev, "Timer expired\n"); ++ break; ++ case 3: /* Battery voltage too low */ ++ dev_err(bq->dev, "Battery voltage to low\n"); ++ break; ++ ++ case 1: /* Overvoltage protection (chip fried) */ ++ bq2415x_timer_error(bq, ++ "Overvolatge protection (chip fried)"); ++ return; ++ case 2: /* Overload */ ++ bq2415x_timer_error(bq, "Overload"); ++ return; ++ case 4: /* Battery overvoltage protection */ ++ bq2415x_timer_error(bq, ++ "Battery overvoltage protection"); ++ return; ++ case 5: /* Thermal shutdown (too hot) */ ++ bq2415x_timer_error(bq, ++ "Thermal shutdown (too hot)"); ++ return; ++ case 7: /* N/A */ ++ bq2415x_timer_error(bq, "Unknown error"); ++ return; ++ } ++ } else { ++ switch (error) { ++ case 0: /* No error */ ++ break; ++ case 2: /* Sleep mode */ ++ dev_err(bq->dev, "Sleep mode\n"); ++ break; ++ case 3: /* Poor input source */ ++ dev_err(bq->dev, "Poor input source\n"); ++ break; ++ case 6: /* Timer expired */ ++ dev_err(bq->dev, "Timer expired\n"); ++ break; ++ case 7: /* No battery */ ++ dev_err(bq->dev, "No battery\n"); ++ break; ++ ++ case 1: /* Overvoltage protection (chip fried) */ ++ bq2415x_timer_error(bq, ++ "Overvolatge protection (chip fried)"); ++ return; ++ case 4: /* Battery overvoltage protection */ ++ bq2415x_timer_error(bq, ++ "Battery overvoltage protection"); ++ return; ++ case 5: /* Thermal shutdown (too hot) */ ++ bq2415x_timer_error(bq, ++ "Thermal shutdown (too hot)"); ++ return; ++ } ++ } ++ ++ schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ); ++} ++ ++/* power supply */ ++ ++static enum power_supply_property bq2415x_power_supply_props[] = { ++ /* TODO: more power supply properties */ ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_MODEL_NAME, ++}; ++ ++static int bq2415x_power_supply_get_property(struct power_supply *psy, ++ enum power_supply_property psp, union power_supply_propval *val) ++{ ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ int ret; ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS); ++ if (ret < 0) ++ return ret; ++ else if (ret == 0) /* Ready */ ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ else if (ret == 1) /* Charge in progress */ ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ else if (ret == 2) /* Charge done */ ++ val->intval = POWER_SUPPLY_STATUS_FULL; ++ else ++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; ++ break; ++ case POWER_SUPPLY_PROP_MODEL_NAME: ++ val->strval = bq->model; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int bq2415x_power_supply_init(struct bq2415x_device *bq) ++{ ++ int ret; ++ int chip; ++ char revstr[8]; ++ ++ bq->charger.name = bq->name; ++ bq->charger.type = POWER_SUPPLY_TYPE_USB; ++ bq->charger.properties = bq2415x_power_supply_props; ++ bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props); ++ bq->charger.get_property = bq2415x_power_supply_get_property; ++ ++ ret = bq2415x_detect_chip(bq); ++ if (ret < 0) ++ chip = BQUNKNOWN; ++ else ++ chip = ret; ++ ++ ret = bq2415x_detect_revision(bq); ++ if (ret < 0) ++ strcpy(revstr, "unknown"); ++ else ++ sprintf(revstr, "1.%d", ret); ++ ++ bq->model = kasprintf(GFP_KERNEL, ++ "chip %s, revision %s, vender code %.3d", ++ bq2415x_chip_name[chip], revstr, ++ bq2415x_get_vender_code(bq)); ++ if (!bq->model) { ++ dev_err(bq->dev, "failed to allocate model name\n"); ++ return -ENOMEM; ++ } ++ ++ ret = power_supply_register(bq->dev, &bq->charger); ++ if (ret) { ++ kfree(bq->model); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void bq2415x_power_supply_exit(struct bq2415x_device *bq) ++{ ++ bq->autotimer = 0; ++ if (bq->automode > 0) ++ bq->automode = 0; ++ cancel_delayed_work_sync(&bq->work); ++ power_supply_unregister(&bq->charger); ++ kfree(bq->model); ++} ++ ++/* sysfs files */ ++ ++static ssize_t bq2415x_sysfs_show_status(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ enum bq2415x_command command; ++ int ret; ++ ++ if (strcmp(attr->attr.name, "otg_status") == 0) ++ command = BQ2415X_OTG_STATUS; ++ else if (strcmp(attr->attr.name, "charge_status") == 0) ++ command = BQ2415X_CHARGE_STATUS; ++ else if (strcmp(attr->attr.name, "boost_status") == 0) ++ command = BQ2415X_BOOST_STATUS; ++ else if (strcmp(attr->attr.name, "fault_status") == 0) ++ command = BQ2415X_FAULT_STATUS; ++ else ++ return -EINVAL; ++ ++ ret = bq2415x_exec_command(bq, command); ++ if (ret < 0) ++ return ret; ++ else ++ return sprintf(buf, "%d\n", ret); ++} ++ ++static ssize_t bq2415x_sysfs_set_timer(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ int ret = 0; ++ ++ if (strncmp(buf, "auto", 4) == 0) ++ bq2415x_set_autotimer(bq, 1); ++ else if (strncmp(buf, "off", 3) == 0) ++ bq2415x_set_autotimer(bq, 0); ++ else ++ ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET); ++ ++ if (ret < 0) ++ return ret; ++ else ++ return count; ++} ++ ++static ssize_t bq2415x_sysfs_show_timer(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ ++ if (bq->autotimer) ++ return sprintf(buf, "auto\n"); ++ else ++ return sprintf(buf, "off\n"); ++} ++ ++static ssize_t bq2415x_sysfs_set_mode(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ enum bq2415x_mode mode; ++ int ret = 0; ++ ++ if (strncmp(buf, "auto", 4) == 0) { ++ if (bq->automode < 0) ++ return -ENOSYS; ++ bq->automode = 1; ++ mode = bq->charger_mode; ++ } else if (strncmp(buf, "none", 4) == 0) { ++ if (bq->automode > 0) ++ bq->automode = 0; ++ mode = BQ2415X_MODE_NONE; ++ } else if (strncmp(buf, "host", 4) == 0) { ++ if (bq->automode > 0) ++ bq->automode = 0; ++ mode = BQ2415X_MODE_HOST_CHARGER; ++ } else if (strncmp(buf, "dedicated", 9) == 0) { ++ if (bq->automode > 0) ++ bq->automode = 0; ++ mode = BQ2415X_MODE_DEDICATED_CHARGER; ++ } else if (strncmp(buf, "boost", 5) == 0) { ++ if (bq->automode > 0) ++ bq->automode = 0; ++ mode = BQ2415X_MODE_BOOST; ++ } else if (strncmp(buf, "reset", 5) == 0) { ++ bq2415x_reset_chip(bq); ++ bq2415x_set_defaults(bq); ++ if (bq->automode > 0) ++ bq->automode = 1; ++ return count; ++ } else ++ return -EINVAL; ++ ++ ret = bq2415x_set_mode(bq, mode); ++ if (ret < 0) ++ return ret; ++ else ++ return count; ++} ++ ++static ssize_t bq2415x_sysfs_show_mode(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ ssize_t ret = 0; ++ ++ if (bq->automode > 0) ++ ret += sprintf(buf+ret, "auto ("); ++ ++ switch (bq->mode) { ++ case BQ2415X_MODE_NONE: ++ ret += sprintf(buf+ret, "none"); ++ break; ++ case BQ2415X_MODE_HOST_CHARGER: ++ ret += sprintf(buf+ret, "host"); ++ break; ++ case BQ2415X_MODE_DEDICATED_CHARGER: ++ ret += sprintf(buf+ret, "dedicated"); ++ break; ++ case BQ2415X_MODE_BOOST: ++ ret += sprintf(buf+ret, "boost"); ++ break; ++ } ++ ++ if (bq->automode > 0) ++ ret += sprintf(buf+ret, ")"); ++ ++ ret += sprintf(buf+ret, "\n"); ++ return ret; ++} ++ ++static ssize_t bq2415x_sysfs_set_registers(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ ssize_t ret = 0; ++ char *end; ++ int reg; ++ int val; ++ ++ reg = simple_strtol(buf, &end, 16); ++ if (reg < 0 || reg > 4) ++ return -EINVAL; ++ ++ val = simple_strtol(end+1, NULL, 16); ++ if (val < 0 || val > 255) ++ return -EINVAL; ++ ++ ret = bq2415x_i2c_write(bq, reg, val); ++ if (ret < 0) ++ return ret; ++ else ++ return count; ++} ++ ++static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq, ++ u8 reg, char *buf) ++{ ++ int ret = bq2415x_i2c_read(bq, reg); ++ if (ret < 0) ++ return sprintf(buf, "%#.2x=error %d\n", reg, ret); ++ else ++ return sprintf(buf, "%#.2x=%#.2x\n", reg, ret); ++} ++ ++static ssize_t bq2415x_sysfs_show_registers(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ ssize_t ret = 0; ++ ++ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret); ++ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret); ++ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret); ++ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret); ++ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret); ++ return ret; ++} ++ ++/* Current & Volatage settings */ ++ ++static ssize_t bq2415x_sysfs_set_limit(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ long val; ++ int ret; ++ ++ if (strict_strtol(buf, 10, &val) < 0) ++ return -EINVAL; ++ ++ if (strcmp(attr->attr.name, "current_limit") == 0) ++ ret = bq2415x_set_current_limit(bq, val); ++ else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0) ++ ret = bq2415x_set_weak_battery_voltage(bq, val); ++ else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0) ++ ret = bq2415x_set_battery_regulation_voltage(bq, val); ++ else if (strcmp(attr->attr.name, "charge_current") == 0) ++ ret = bq2415x_set_charge_current(bq, val); ++ else if (strcmp(attr->attr.name, "termination_current") == 0) ++ ret = bq2415x_set_termination_current(bq, val); ++ else ++ return -EINVAL; ++ ++ if (ret < 0) ++ return ret; ++ else ++ return count; ++} ++ ++static ssize_t bq2415x_sysfs_show_limit(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ int ret; ++ ++ if (strcmp(attr->attr.name, "current_limit") == 0) ++ ret = bq2415x_get_current_limit(bq); ++ else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0) ++ ret = bq2415x_get_weak_battery_voltage(bq); ++ else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0) ++ ret = bq2415x_get_battery_regulation_voltage(bq); ++ else if (strcmp(attr->attr.name, "charge_current") == 0) ++ ret = bq2415x_get_charge_current(bq); ++ else if (strcmp(attr->attr.name, "termination_current") == 0) ++ ret = bq2415x_get_termination_current(bq); ++ else ++ return -EINVAL; ++ ++ if (ret < 0) ++ return ret; ++ else ++ return sprintf(buf, "%d\n", ret); ++} ++ ++static ssize_t bq2415x_sysfs_set_enable(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ enum bq2415x_command command; ++ long val; ++ int ret; ++ ++ if (strict_strtol(buf, 10, &val) < 0) ++ return -EINVAL; ++ ++ if (strcmp(attr->attr.name, "charge_termination_enable") == 0) ++ command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE : ++ BQ2415X_CHARGE_TERMINATION_DISABLE; ++ else if (strcmp(attr->attr.name, "high_impedance_enable") == 0) ++ command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE : ++ BQ2415X_HIGH_IMPEDANCE_DISABLE; ++ else if (strcmp(attr->attr.name, "otg_pin_enable") == 0) ++ command = val ? BQ2415X_OTG_PIN_ENABLE : ++ BQ2415X_OTG_PIN_DISABLE; ++ else if (strcmp(attr->attr.name, "stat_pin_enable") == 0) ++ command = val ? BQ2415X_STAT_PIN_ENABLE : ++ BQ2415X_STAT_PIN_DISABLE; ++ else ++ return -EINVAL; ++ ++ ret = bq2415x_exec_command(bq, command); ++ if (ret < 0) ++ return ret; ++ else ++ return count; ++} ++ ++static ssize_t bq2415x_sysfs_show_enable(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct power_supply *psy = dev_get_drvdata(dev); ++ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, ++ charger); ++ enum bq2415x_command command; ++ int ret; ++ ++ if (strcmp(attr->attr.name, "charge_termination_enable") == 0) ++ command = BQ2415X_CHARGE_TERMINATION_STATUS; ++ else if (strcmp(attr->attr.name, "high_impedance_enable") == 0) ++ command = BQ2415X_HIGH_IMPEDANCE_STATUS; ++ else if (strcmp(attr->attr.name, "otg_pin_enable") == 0) ++ command = BQ2415X_OTG_PIN_STATUS; ++ else if (strcmp(attr->attr.name, "stat_pin_enable") == 0) ++ command = BQ2415X_STAT_PIN_STATUS; ++ else ++ return -EINVAL; ++ ++ ret = bq2415x_exec_command(bq, command); ++ if (ret < 0) ++ return ret; ++ else ++ return sprintf(buf, "%d\n", ret); ++} ++ ++static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); ++static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); ++static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); ++static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); ++static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit); ++ ++static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); ++static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); ++static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); ++static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable); ++ ++static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode); ++static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer); ++ ++static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO, ++ bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers); ++ ++static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); ++static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); ++static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); ++static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL); ++ ++static struct attribute *bq2415x_sysfs_attributes[] = { ++ &dev_attr_current_limit.attr, ++ &dev_attr_weak_battery_voltage.attr, ++ &dev_attr_battery_regulation_voltage.attr, ++ &dev_attr_charge_current.attr, ++ &dev_attr_termination_current.attr, ++ ++ &dev_attr_charge_termination_enable.attr, ++ &dev_attr_high_impedance_enable.attr, ++ &dev_attr_otg_pin_enable.attr, ++ &dev_attr_stat_pin_enable.attr, ++ ++ &dev_attr_mode.attr, ++ &dev_attr_timer.attr, ++ ++ &dev_attr_registers.attr, ++ ++ &dev_attr_otg_status.attr, ++ &dev_attr_charge_status.attr, ++ &dev_attr_boost_status.attr, ++ &dev_attr_fault_status.attr, ++ NULL, ++}; ++ ++static const struct attribute_group bq2415x_sysfs_attr_group = { ++ .attrs = bq2415x_sysfs_attributes, ++}; ++ ++static int bq2415x_sysfs_init(struct bq2415x_device *bq) ++{ ++ return sysfs_create_group(&bq->charger.dev->kobj, ++ &bq2415x_sysfs_attr_group); ++} ++ ++static void bq2415x_sysfs_exit(struct bq2415x_device *bq) ++{ ++ sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group); ++} ++ ++/* bq2415x register */ ++ ++static int bq2415x_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int ret; ++ int num; ++ char *name; ++ struct bq2415x_device *bq; ++ ++ if (!client->dev.platform_data) { ++ dev_err(&client->dev, "platform data not set\n"); ++ return -ENODEV; ++ } ++ ++ /* Get new ID for the new device */ ++ ret = idr_pre_get(&bq2415x_id, GFP_KERNEL); ++ if (ret == 0) ++ return -ENOMEM; ++ ++ mutex_lock(&bq2415x_id_mutex); ++ ret = idr_get_new(&bq2415x_id, client, &num); ++ mutex_unlock(&bq2415x_id_mutex); ++ ++ if (ret < 0) ++ return ret; ++ ++ name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); ++ if (!name) { ++ dev_err(&client->dev, "failed to allocate device name\n"); ++ ret = -ENOMEM; ++ goto error_1; ++ } ++ ++ bq = kzalloc(sizeof(*bq), GFP_KERNEL); ++ if (!bq) { ++ dev_err(&client->dev, "failed to allocate device data\n"); ++ ret = -ENOMEM; ++ goto error_2; ++ } ++ ++ i2c_set_clientdata(client, bq); ++ ++ bq->id = num; ++ bq->dev = &client->dev; ++ bq->chip = id->driver_data; ++ bq->name = name; ++ bq->mode = BQ2415X_MODE_NONE; ++ bq->charger_mode = BQ2415X_MODE_NONE; ++ bq->autotimer = 0; ++ bq->automode = 0; ++ ++ memcpy(&bq->init_data, client->dev.platform_data, ++ sizeof(bq->init_data)); ++ ++ bq2415x_reset_chip(bq); ++ ++ ret = bq2415x_power_supply_init(bq); ++ if (ret) { ++ dev_err(bq->dev, "failed to register power supply: %d\n", ret); ++ goto error_3; ++ } ++ ++ ret = bq2415x_sysfs_init(bq); ++ if (ret) { ++ dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); ++ goto error_4; ++ } ++ ++ ret = bq2415x_set_defaults(bq); ++ if (ret) { ++ dev_err(bq->dev, "failed to set default values: %d\n", ret); ++ goto error_5; ++ } ++ ++ if (bq->init_data.set_charger_type_hook) { ++ if (bq->init_data.set_charger_type_hook( ++ bq2415x_set_charger_type, bq)) { ++ bq->automode = 1; ++ dev_info(bq->dev, "automode enabled\n"); ++ } else { ++ bq->automode = -1; ++ dev_info(bq->dev, "automode not supported\n"); ++ } ++ } else { ++ bq->automode = -1; ++ dev_info(bq->dev, "automode not supported\n"); ++ } ++ ++ INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work); ++ bq2415x_set_autotimer(bq, 1); ++ ++ dev_info(bq->dev, "driver registred\n"); ++ return 0; ++ ++error_5: ++ bq2415x_sysfs_exit(bq); ++error_4: ++ bq2415x_power_supply_exit(bq); ++error_3: ++ kfree(bq); ++error_2: ++ kfree(name); ++error_1: ++ mutex_lock(&bq2415x_id_mutex); ++ idr_remove(&bq2415x_id, num); ++ mutex_unlock(&bq2415x_id_mutex); ++ ++ return ret; ++} ++ ++/* bq2415x unregister */ ++ ++static int bq2415x_remove(struct i2c_client *client) ++{ ++ struct bq2415x_device *bq = i2c_get_clientdata(client); ++ ++ if (bq->init_data.set_charger_type_hook) ++ bq->init_data.set_charger_type_hook(NULL, NULL); ++ ++ bq2415x_sysfs_exit(bq); ++ bq2415x_power_supply_exit(bq); ++ ++ bq2415x_reset_chip(bq); ++ ++ mutex_lock(&bq2415x_id_mutex); ++ idr_remove(&bq2415x_id, bq->id); ++ mutex_unlock(&bq2415x_id_mutex); ++ ++ dev_info(bq->dev, "driver unregistred\n"); ++ ++ kfree(bq->name); ++ kfree(bq); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id bq2415x_i2c_id_table[] = { ++ { "bq2415x", BQUNKNOWN }, ++ { "bq24150", BQ24150 }, ++ { "bq24150a", BQ24150A }, ++ { "bq24151", BQ24151 }, ++ { "bq24151a", BQ24151A }, ++ { "bq24152", BQ24152 }, ++ { "bq24153", BQ24153 }, ++ { "bq24153a", BQ24153A }, ++ { "bq24155", BQ24155 }, ++ { "bq24156", BQ24156 }, ++ { "bq24156a", BQ24156A }, ++ { "bq24158", BQ24158 }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table); ++ ++static struct i2c_driver bq2415x_driver = { ++ .driver = { ++ .name = "bq2415x-charger", ++ }, ++ .probe = bq2415x_probe, ++ .remove = bq2415x_remove, ++ .id_table = bq2415x_i2c_id_table, ++}; ++ ++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("Pali Rohár "); ++MODULE_DESCRIPTION("bq2415x charger driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null 2012-04-29 08:55:34.366920721 +0200 ++++ kernel-power/include/linux/power/bq2415x_charger.h 2012-01-27 15:47:45.689585447 +0100 +@@ -0,0 +1,78 @@ ++/* ++ bq2415x_charger.h - bq2415x charger driver ++ Copyright (C) 2011-2012 Pali Rohár ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++*/ ++ ++#ifndef BQ2415X_CHARGER_H ++#define BQ2415X_CHARGER_H ++ ++/* ++ This is platform data for bq2415x chip. It contains default board volatages ++ and currents which can be also later configured via sysfs. If value is -1 ++ then default chip value (specified in datasheet) will be used. ++ ++ Value resistor_sense is needed for for configuring charge and termination ++ current. It it is less or equal to zero, configuring charge and termination ++ current will not be possible. ++ ++ Function set_charger_type_hook is needed for automode (setting correct ++ current limit when charger is connected/disconnected). When is NULL, ++ automode function is disabled. When is not NULL, it must have this prototype: ++ ++ int (*set_charger_type_hook)(void (*hook)(int type, void *data), void *data) ++ ++ and bq2415x driver will call it as: ++ ++ platform_data->set_charger_type_hook(bq_hook_function, bq_private_data); ++ ++ Board/platform function set_charger_type_hook return non zero when hook ++ function was successfull registred. Platform code should call that hook ++ function (which get from pointer, with data) every time when charger was ++ connected/disconnected. bq driver then set correct current limit. ++ ++ Hook function has this prototype: ++ ++ void hook(int type, void *data); ++ ++ type is: ++ 0 - for unknown or none charger (max current limit is 100mA) ++ 1 - for usb host/hub charger (max current limit is 500mA) ++ 2 - for dedicated charger (unlimited) ++ ++ data is pointer to bq_private_data (which get from set_charger_type_hook) ++ ++ When bq driver is being unloaded, it call function: ++ ++ platform_data->set_charger_type_hook(NULL, NULL); ++ ++ After thet board/platform code must not call bq hook function! It is ++ possible that pointer to hook function will not be valid. ++ ++*/ ++ ++struct bq2415x_platform_data { ++ int current_limit; /* mA */ ++ int weak_battery_voltage; /* mV */ ++ int battery_regulation_voltage; /* mV */ ++ int charge_current; /* mA */ ++ int termination_current; /* mA */ ++ int resistor_sense; /* m ohm */ ++ int (*set_charger_type_hook)(void (*hook)(int type, void *data), ++ void *data); ++}; ++ ++#endif diff --git a/kernel-power-2.6.28/debian/patches/bq2415x_kconfig.patch b/kernel-power-2.6.28/debian/patches/bq2415x_kconfig.patch new file mode 100644 index 0000000..32a44d2 --- /dev/null +++ b/kernel-power-2.6.28/debian/patches/bq2415x_kconfig.patch @@ -0,0 +1,20 @@ +--- kernel-power/drivers/power/Makefile 2012-04-29 18:03:10.751810073 +0200 ++++ kernel-power/drivers/power/Makefile 2012-04-29 18:03:52.207808860 +0200 +@@ -25,3 +25,4 @@ obj-$(CONFIG_TWL4030_BCI_BATTERY) += twl + obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o + obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o + obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o ++obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o +--- kernel-power/drivers/power/Kconfig 2012-04-29 18:01:48.819812466 +0200 ++++ kernel-power/drivers/power/Kconfig 2012-04-29 18:03:06.043810209 +0200 +@@ -75,4 +75,10 @@ config BATTERY_BQ27x00 + help + Say Y here to enable support for batteries with BQ27200(I2C) chip. + ++config CHARGER_BQ2415X ++ tristate "BQ2415X charger driver" ++ depends on I2C ++ help ++ Say Y here to enable support for chargers with BQ2415X(I2C) chip. ++ + endif # POWER_SUPPLY diff --git a/kernel-power-2.6.28/debian/patches/bq2415x_rx51.patch b/kernel-power-2.6.28/debian/patches/bq2415x_rx51.patch new file mode 100644 index 0000000..e32624b --- /dev/null +++ b/kernel-power-2.6.28/debian/patches/bq2415x_rx51.patch @@ -0,0 +1,249 @@ +--- kernel-power/drivers/usb/otg/twl4030-usb.c.orig 2012-04-29 17:15:00.195894587 +0200 ++++ kernel-power/drivers/usb/otg/twl4030-usb.c 2012-04-29 17:18:06.815889128 +0200 +@@ -37,6 +37,8 @@ + #include + #include + ++#include ++#include + + /* Register defines */ + +@@ -265,6 +265,9 @@ struct twl4030_usb { + u8 linkstat; + u8 asleep; + bool irq_enabled; ++ ++ struct delayed_work work; ++ int work_inited; + }; + + /* internal define on top of container_of */ +@@ -365,6 +367,14 @@ static enum linkstat twl4030_usb_linksta + dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", + status, status, linkstat); + ++ if (machine_is_nokia_rx51() && rx51_with_charger_detection()) { ++ rx51_set_charger(linkstat == USB_LINK_VBUS); ++ if (twl->work_inited && linkstat == USB_LINK_VBUS) { ++ printk("rx51 - schedule delayed work in 2 seconds - rx51_detect_wallcharger\n"); ++ schedule_delayed_work(&twl->work, 2 * HZ); /* 2 seconds should be enought */ ++ } ++ } ++ + /* REVISIT this assumes host and peripheral controllers + * are registered, and that both are active... + */ +@@ -771,6 +773,11 @@ static int __init twl4030_usb_probe(stru + */ + twl4030_usb_irq(twl->irq, twl); + ++ if (machine_is_nokia_rx51()) { ++ INIT_DELAYED_WORK(&twl->work, rx51_detect_wallcharger); ++ twl->work_inited = 1; ++ } ++ + dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); + return 0; + } +--- kernel-power/drivers/usb/musb/omap2430.c.orig 2012-04-29 16:34:10.123966221 +0200 ++++ kernel-power/drivers/usb/musb/omap2430.c 2012-04-29 16:50:48.611937027 +0200 +@@ -38,6 +38,8 @@ + #include + #include + ++#include ++ + #include + + #include "musb_core.h" +@@ -230,6 +232,7 @@ int musb_platform_set_mode(struct musb * + + if (machine_is_nokia_rx51()) { + u8 testmode; ++ rx51_enable_charger_detection(0); + + musb_platform_resume(musb); + +@@ -255,6 +259,7 @@ int musb_platform_set_mode(struct musb * + + musb_writeb(musb->mregs, MUSB_TESTMODE, 0); + musb_platform_suspend(musb); ++ rx51_enable_charger_detection(1); + } + + otg_set_peripheral(musb->xceiv, &musb->g); +@@ -434,6 +438,9 @@ void musb_save_ctx_and_suspend(struct us + + musb->is_charger = 0; + ++ if (machine_is_nokia_rx51() && rx51_with_charger_detection()) ++ rx51_set_wallcharger(0); ++ + /* clear constraints */ + if (musb->board && musb->board->set_pm_limits) + musb->board->set_pm_limits(musb->controller, 0); +--- kernel-power/drivers/usb/musb/musb_core.c.orig 2012-04-29 16:57:27.407925369 +0200 ++++ kernel-power/drivers/usb/musb/musb_core.c 2012-04-29 17:13:39.083896956 +0200 +@@ -105,6 +105,8 @@ + #include + #endif + ++#include ++ + #include "musb_core.h" + + +@@ -223,6 +224,10 @@ static int musb_charger_detect(struct mu + u8 vdat = 0; + u8 r; + ++ printk("musb_charger_detect (enabled = %d)\n", rx51_with_charger_detection()); ++ if (machine_is_nokia_rx51() && !rx51_with_charger_detection()) ++ return 0; ++ + msleep(5); + + /* Using ulpi with musb is quite tricky. The following code +@@ -308,6 +315,8 @@ static int musb_charger_detect(struct mu + /* Regulators off */ + otg_set_suspend(musb->xceiv, 1); + musb->is_charger = 1; ++ if (machine_is_nokia_rx51() && rx51_with_charger_detection()) ++ rx51_set_wallcharger(1); + } else { + /* enable interrupts */ + musb_writeb(musb->mregs, MUSB_INTRUSBE, ctx.intrusbe); +@@ -330,6 +330,14 @@ static int musb_charger_detect(struct mu + return vdat; + } + ++void rx51_detect_wallcharger(void *work) ++{ ++ printk("rx51_detect_wallcharger (the_musb = %p)\n", the_musb); ++ if (the_musb) ++ musb_charger_detect(the_musb); ++} ++EXPORT_SYMBOL(rx51_detect_wallcharger); ++ + /*-------------------------------------------------------------------------*/ + + static inline struct musb *dev_to_musb(struct device *dev) +--- kernel-power/arch/arm/plat-omap/include/mach/board-rx51.h.orig 2012-04-29 16:39:03.927957628 +0200 ++++ kernel-power/arch/arm/plat-omap/include/mach/board-rx51.h 2012-04-29 17:14:50.923894858 +0200 +@@ -39,6 +39,12 @@ extern void rx51_usb_init(void); + static inline void rx51_usb_init(void) { } + #endif + ++extern void rx51_set_charger(int connected); ++extern void rx51_set_wallcharger(int connected); ++extern void rx51_enable_charger_detection(int enable); ++extern void rx51_detect_wallcharger(void *work); ++extern int rx51_with_charger_detection(void); ++ + extern void omap_bt_init(struct omap_bluetooth_config *bt_config); + + struct omap_sdrc_params *rx51_get_sdram_timings(void); +--- kernel-power/arch/arm/mach-omap2/board-rx51-peripherals.c.orig 2012-04-29 17:18:56.995887664 +0200 ++++ kernel-power/arch/arm/mach-omap2/board-rx51-peripherals.c 2012-04-29 17:46:57.487838528 +0200 +@@ -38,6 +38,8 @@ + #include + + #include "../../../drivers/input/lirc/lirc_rx51.h" ++#include ++#include + + #define RX51_DEBUG_BASE 0x08000000 /* debug board */ + #define RX51_ETHR_START RX51_DEBUG_BASE +@@ -563,6 +565,80 @@ static struct i2c_board_info __initdata + }, + }; + ++static int rx51_charger_type; ++static int rx51_charger_connected; ++static int rx51_wallcharger_connected; ++static int rx51_charger_detection = 1; ++ ++static void *rx51_charger_hook_data; ++static void (*rx51_charger_hook)(int type, void *data); ++ ++static int rx51_charger_set_hook(void (*hook)(int type, void *data), void *data) ++{ ++ rx51_charger_hook = hook; ++ rx51_charger_hook_data = data; ++ return 1; ++} ++ ++static void rx51_update_charger_type(void) ++{ ++ int type; ++ if (rx51_charger_connected && rx51_wallcharger_connected) ++ type = 2; /* wallcharger */ ++ else if (rx51_charger_connected) ++ type = 1; /* usb charger */ ++ else ++ type = 0; /* no charger */ ++ ++ if (rx51_charger_type == type) ++ return; ++ ++ printk("rx51_update_charger_type (type = %d)\n", type); ++ rx51_charger_type = type; ++ ++ if (rx51_charger_hook) ++ rx51_charger_hook(rx51_charger_type, rx51_charger_hook_data); ++} ++ ++void rx51_set_charger(int connected) ++{ ++ printk("rx51_set_charger (connected = %d)\n", connected); ++ rx51_charger_connected = connected; ++ rx51_update_charger_type(); ++} ++EXPORT_SYMBOL(rx51_set_charger); ++ ++void rx51_set_wallcharger(int connected) ++{ ++ printk("rx51_set_wallcharger (connected = %d)\n", connected); ++ rx51_wallcharger_connected = connected; ++ rx51_update_charger_type(); ++} ++EXPORT_SYMBOL(rx51_set_wallcharger); ++ ++void rx51_enable_charger_detection(int enable) ++{ ++ printk("rx51_enable_charger_detection (enable = %d)\n", enable); ++ rx51_charger_detection = enable; ++} ++EXPORT_SYMBOL(rx51_enable_charger_detection); ++ ++int rx51_with_charger_detection(void) ++{ ++ return rx51_charger_detection; ++} ++EXPORT_SYMBOL(rx51_with_charger_detection); ++ ++static struct bq2415x_platform_data rx51_bq24150_platform_data = { ++ .current_limit = 100, /* mA */ ++ .weak_battery_voltage = 3400, /* mV */ ++ .battery_regulation_voltage = 4200, /* mV */ ++ .charge_current = 950, /*1200*/ /* mA */ ++ .termination_current = 150, /*400*/ /* mA */ ++ .resistor_sense = 68, /* m ohm */ ++ .set_charger_type_hook = &rx51_charger_set_hook, ++}; ++ + static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_2[] = { + { + I2C_BOARD_INFO("lp5523", 0x32), +@@ -575,6 +636,10 @@ static struct i2c_board_info __initdata + { + I2C_BOARD_INFO("bq27200", 0x55), + }, ++ { ++ I2C_BOARD_INFO("bq24150", 0x6b), ++ .platform_data = &rx51_bq24150_platform_data, ++ }, + }; + + static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_3[] = { diff --git a/kernel-power-2.6.28/debian/patches/series b/kernel-power-2.6.28/debian/patches/series index eef1b96..b42bdf4 100644 --- a/kernel-power-2.6.28/debian/patches/series +++ b/kernel-power-2.6.28/debian/patches/series @@ -70,5 +70,6 @@ support-non-page-aligned-buffers-in-iommu_vmap.diff videobuf-dma-sg-support-non-pagable-user-memory.diff bluetooth-fix-potential-bad-memory-access-with-sysfs-files.diff USB-fix-kernel-oops-with-g_ether-and-windows.diff -bq24150-charger.patch -bq24150-rx51.patch +bq2415x_charger.patch +bq2415x_kconfig.patch +bq2415x_rx51.patch -- 1.7.9.5