From 41d2760dd94b20972bcddf12cc07b3f48b6ef503 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pali=20Roh=C3=A1r?= Date: Sun, 29 Apr 2012 18:14:14 +0200 Subject: [PATCH] Added bq2415x driver --- .../debian/patches/bq24150-charger.patch | 1668 ++++++++++++++++++++ .../debian/patches/bq24150-rx51.patch | 249 +++ .../debian/patches/rx51_defconfig.diff | 3 +- kernel-power-2.6.28/debian/patches/series | 2 + 4 files changed, 1921 insertions(+), 1 deletion(-) create mode 100644 kernel-power-2.6.28/debian/patches/bq24150-charger.patch create mode 100644 kernel-power-2.6.28/debian/patches/bq24150-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 new file mode 100644 index 0000000..4393e84 --- /dev/null +++ b/kernel-power-2.6.28/debian/patches/bq24150-charger.patch @@ -0,0 +1,1668 @@ +--- /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 new file mode 100644 index 0000000..e32624b --- /dev/null +++ b/kernel-power-2.6.28/debian/patches/bq24150-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/rx51_defconfig.diff b/kernel-power-2.6.28/debian/patches/rx51_defconfig.diff index 2ae1990..ae64d0c 100644 --- a/kernel-power-2.6.28/debian/patches/rx51_defconfig.diff +++ b/kernel-power-2.6.28/debian/patches/rx51_defconfig.diff @@ -528,7 +528,7 @@ CONFIG_DEVKMEM=y # CONFIG_SERIAL_NONSTANDARD is not set -@@ -998,7 +1196,12 @@ +@@ -998,7 +1196,13 @@ # CONFIG_GPIO_MAX7301 is not set # CONFIG_GPIO_MCP23S08 is not set # CONFIG_W1 is not set @@ -539,6 +539,7 @@ +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_TWL4030_BCI_BATTERY is not set +CONFIG_BATTERY_BQ27x00=m ++CONFIG_CHARGER_BQ2415X=m CONFIG_HWMON=y # CONFIG_HWMON_VID is not set # CONFIG_SENSORS_AD7414 is not set diff --git a/kernel-power-2.6.28/debian/patches/series b/kernel-power-2.6.28/debian/patches/series index b2fec74..eef1b96 100644 --- a/kernel-power-2.6.28/debian/patches/series +++ b/kernel-power-2.6.28/debian/patches/series @@ -70,3 +70,5 @@ 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 -- 1.7.9.5