From: Michael Büsch Date: Sun, 6 Feb 2011 12:44:12 +0000 (+0000) Subject: n810: Add more battery management code X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=09655bf0e46e0b889e8c7b007a5b426c9bc2d0e2;p=openwrt%2Fstaging%2Fblocktrron.git n810: Add more battery management code SVN-Revision: 25380 --- diff --git a/target/linux/omap24xx/patches-2.6.37/510-retu-tahvo-user-debugging.patch b/target/linux/omap24xx/patches-2.6.37/510-retu-tahvo-user-debugging.patch index 3a988fb993..481c278d9d 100644 --- a/target/linux/omap24xx/patches-2.6.37/510-retu-tahvo-user-debugging.patch +++ b/target/linux/omap24xx/patches-2.6.37/510-retu-tahvo-user-debugging.patch @@ -4,10 +4,10 @@ drivers/cbus/tahvo-user.c | 75 +++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 2 deletions(-) -Index: linux-2.6.37-rc1/drivers/cbus/Kconfig +Index: linux-2.6.37/drivers/cbus/Kconfig =================================================================== ---- linux-2.6.37-rc1.orig/drivers/cbus/Kconfig 2010-11-05 17:04:49.001997921 +0100 -+++ linux-2.6.37-rc1/drivers/cbus/Kconfig 2010-11-05 17:04:52.017998785 +0100 +--- linux-2.6.37.orig/drivers/cbus/Kconfig 2011-02-05 20:01:30.636705379 +0100 ++++ linux-2.6.37/drivers/cbus/Kconfig 2011-02-05 20:56:11.593025426 +0100 @@ -28,6 +28,10 @@ If you want support for Tahvo's user space read/write etc. functions, you should say Y here. @@ -30,10 +30,10 @@ Index: linux-2.6.37-rc1/drivers/cbus/Kconfig config CBUS_RETU_POWERBUTTON depends on CBUS_RETU bool "Support for Retu power button" -Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c +Index: linux-2.6.37/drivers/cbus/retu-user.c =================================================================== ---- linux-2.6.37-rc1.orig/drivers/cbus/retu-user.c 2010-11-05 17:04:49.002997987 +0100 -+++ linux-2.6.37-rc1/drivers/cbus/retu-user.c 2010-11-05 17:04:52.017998785 +0100 +--- linux-2.6.37.orig/drivers/cbus/retu-user.c 2011-02-05 20:01:30.637705440 +0100 ++++ linux-2.6.37/drivers/cbus/retu-user.c 2011-02-05 20:56:42.584938988 +0100 @@ -46,6 +46,12 @@ #define PFX "retu-user: " @@ -47,7 +47,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c /* Bitmap for marking the interrupt sources as having the handlers */ static u32 retu_irq_bits; -@@ -105,6 +111,94 @@ +@@ -105,6 +111,93 @@ 3 }; @@ -88,7 +88,6 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c + [RETU_REG_STATUS] = "Status register", + [RETU_REG_WATCHDOG] = "Watchdog register", + [RETU_REG_AUDTXR] = "Audio Codec Tx register", -+ [0x14] = "Charger detect?", + }; + const char *name; + @@ -142,7 +141,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c /* * The handler for all RETU interrupts. * -@@ -157,6 +251,8 @@ +@@ -157,6 +250,8 @@ /* Mark that this interrupt has a handler */ retu_irq_bits |= 1 << id; @@ -151,7 +150,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c return 0; } -@@ -216,6 +312,10 @@ +@@ -216,6 +311,10 @@ /* Generate new value */ tmp = (tmp & ~MASK(field)) | (value & MASK(field)); @@ -162,7 +161,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c /* Write data to RETU */ retu_write_reg(reg, tmp); spin_unlock_irqrestore(&retu_lock, flags); -@@ -244,6 +344,9 @@ +@@ -244,6 +343,9 @@ /* Read the register */ value = retu_read_reg(reg) & mask; @@ -172,7 +171,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c /* Right justify value */ while (!(mask & 1)) { value = value >> 1; -@@ -273,7 +376,7 @@ +@@ -273,7 +375,7 @@ static long retu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct retu_tahvo_write_parms par; @@ -181,7 +180,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c switch (cmd) { case URT_IOCT_IRQ_SUBSCR: -@@ -290,7 +393,15 @@ +@@ -290,7 +392,15 @@ printk(KERN_ERR "copy_to_user failed: %d\n", ret); break; case RETU_IOCH_ADC_READ: @@ -198,7 +197,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c default: return -ENOIOCTLCMD; } -@@ -332,6 +443,8 @@ +@@ -332,6 +442,8 @@ list_move(&irq->node, &retu_irqs_reserve); spin_unlock_irqrestore(&retu_irqs_lock, flags); @@ -207,10 +206,10 @@ Index: linux-2.6.37-rc1/drivers/cbus/retu-user.c ret = copy_to_user(buf + i * sizeof(irq_id), &irq_id, sizeof(irq_id)); if (ret) -Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c +Index: linux-2.6.37/drivers/cbus/tahvo-user.c =================================================================== ---- linux-2.6.37-rc1.orig/drivers/cbus/tahvo-user.c 2010-11-05 17:04:49.003998052 +0100 -+++ linux-2.6.37-rc1/drivers/cbus/tahvo-user.c 2010-11-05 17:04:52.018998824 +0100 +--- linux-2.6.37.orig/drivers/cbus/tahvo-user.c 2011-02-05 20:01:30.638705501 +0100 ++++ linux-2.6.37/drivers/cbus/tahvo-user.c 2011-02-05 20:57:03.817249794 +0100 @@ -46,6 +46,12 @@ #define PFX "tahvo-user: " @@ -224,7 +223,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c /* Bitmap for marking the interrupt sources as having the handlers */ static u32 tahvo_irq_bits; -@@ -87,6 +93,64 @@ +@@ -87,6 +93,60 @@ 1 }; @@ -255,10 +254,6 @@ Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c + [TAHVO_REG_IMR] = "Interrupt mask", + [TAHVO_REG_LEDPWMR] = "LED PWM", + [TAHVO_REG_USBR] = "USB control", -+ [0x04] = "Charge current control?", -+ [0x08] = "Charge ctl 1?", -+ [0x0C] = "Charge ctl 2?", -+ [0x0D] = "Battery current ADC?", + }; + const char *name; + @@ -289,7 +284,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c /* * The handler for all TAHVO interrupts. * -@@ -142,6 +206,8 @@ +@@ -142,6 +202,8 @@ /* Mark that this interrupt has a handler */ tahvo_irq_bits |= 1 << id; @@ -298,7 +293,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c return 0; } -@@ -200,6 +266,10 @@ +@@ -200,6 +262,10 @@ } /* Generate a new value */ tmp = (tmp & ~MASK(field)) | (value & MASK(field)); @@ -309,7 +304,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c /* Write data to TAHVO */ tahvo_write_reg(reg, tmp); spin_unlock_irqrestore(&tahvo_lock, flags); -@@ -228,6 +298,9 @@ +@@ -228,6 +294,9 @@ /* Read the register */ value = tahvo_read_reg(reg) & mask; @@ -319,7 +314,7 @@ Index: linux-2.6.37-rc1/drivers/cbus/tahvo-user.c /* Right justify value */ while (!(mask & 1)) { value = value >> 1; -@@ -314,6 +387,8 @@ +@@ -314,6 +383,8 @@ list_move(&irq->node, &tahvo_irqs_reserve); spin_unlock_irqrestore(&tahvo_irqs_lock, flags); diff --git a/target/linux/omap24xx/patches-2.6.37/900-n810-battery-management.patch b/target/linux/omap24xx/patches-2.6.37/900-n810-battery-management.patch index 3f6262a357..2f2061629a 100644 --- a/target/linux/omap24xx/patches-2.6.37/900-n810-battery-management.patch +++ b/target/linux/omap24xx/patches-2.6.37/900-n810-battery-management.patch @@ -12,8 +12,8 @@ Index: linux-2.6.37/drivers/cbus/Kconfig =================================================================== ---- linux-2.6.37.orig/drivers/cbus/Kconfig 2011-01-28 22:33:39.703215389 +0100 -+++ linux-2.6.37/drivers/cbus/Kconfig 2011-01-28 23:41:57.094298060 +0100 +--- linux-2.6.37.orig/drivers/cbus/Kconfig 2011-02-06 00:24:48.502005279 +0100 ++++ linux-2.6.37/drivers/cbus/Kconfig 2011-02-06 00:24:48.550008091 +0100 @@ -94,4 +94,12 @@ to Retu/Vilma. Detection state and events are exposed through sysfs. @@ -29,8 +29,8 @@ Index: linux-2.6.37/drivers/cbus/Kconfig endmenu Index: linux-2.6.37/drivers/cbus/Makefile =================================================================== ---- linux-2.6.37.orig/drivers/cbus/Makefile 2011-01-28 22:33:39.694216931 +0100 -+++ linux-2.6.37/drivers/cbus/Makefile 2011-01-28 22:33:39.754206648 +0100 +--- linux-2.6.37.orig/drivers/cbus/Makefile 2011-02-06 00:24:48.493004751 +0100 ++++ linux-2.6.37/drivers/cbus/Makefile 2011-02-06 00:24:48.550008091 +0100 @@ -12,3 +12,6 @@ obj-$(CONFIG_CBUS_TAHVO_USER) += tahvo-user.o obj-$(CONFIG_CBUS_RETU_USER) += retu-user.o @@ -41,8 +41,8 @@ Index: linux-2.6.37/drivers/cbus/Makefile Index: linux-2.6.37/drivers/cbus/n810bm_main.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.37/drivers/cbus/n810bm_main.c 2011-01-28 23:41:23.510064352 +0100 -@@ -0,0 +1,562 @@ ++++ linux-2.6.37/drivers/cbus/n810bm_main.c 2011-02-06 13:36:49.581078785 +0100 +@@ -0,0 +1,959 @@ +/* + * Nokia n810 battery management + * @@ -63,6 +63,8 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + * GNU General Public License for more details. + */ + ++#define DEBUG ++ +#include +#include +#include @@ -70,13 +72,17 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c +#include +#include +#include ++#include + +#include "retu.h" +#include "tahvo.h" +#include "lipocharge.h" + + -+#define N810BM_CHECK_INTERVAL (HZ * 5) ++#define N810BM_PMM_BLOCK_FILENAME "n810-cal-bme-pmm.fw" ++#define N810BM_PMM_BLOCK_SIZE 0x600 ++ ++#define N810BM_CHECK_INTERVAL (HZ * 2) +#define N810BM_MIN_VOLTAGE_THRES 3300 /* Absolute minimum voltage threshold */ + + @@ -121,22 +127,37 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + + +enum n810bm_capacity { -+ N810BM_CAP_UNKNOWN = 0, ++ N810BM_CAP_UNKNOWN = -1, ++ N810BM_CAP_NONE = 0, + N810BM_CAP_1500MAH = 1500, /* 1500 mAh battery */ +}; + +struct n810bm { ++ bool battery_present; /* A battery is inserted */ ++ bool charger_present; /* The charger is connected */ ++ enum n810bm_capacity capacity; /* The capacity of the inserted battery (if any) */ ++ ++ bool charger_enabled; /* Want to charge? */ //TODO ++ struct lipocharge charger; /* Charger subsystem */ ++ + struct platform_device *pdev; ++ const struct firmware *pmm_block; /* CAL PMM block */ + -+ enum n810bm_capacity capacity; + struct timer_list check_timer; + -+ struct lipocharge *charger; -+ ++ bool initialized; /* The hardware was initialized */ + spinlock_t lock; +}; + + ++static inline struct n810bm * device_to_n810bm(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct n810bm *bm = platform_get_drvdata(pdev); ++ ++ return bm; ++} ++ +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET; +static void n810bm_emergency(struct n810bm *bm, const char *message) +{ @@ -146,7 +167,63 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + panic("n810bm: Failed to halt machine in emergency state\n"); +} + -+#if 0 ++static u16 tahvo_read(struct n810bm *bm, unsigned int reg) ++{ ++ int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tahvo_lock, flags); ++ ret = tahvo_read_reg(reg); ++ spin_unlock_irqrestore(&tahvo_lock, flags); ++ if (ret < 0 || ret > 0xFFFF) ++ n810bm_emergency(bm, "tahvo_read"); ++ ++ return ret; ++} ++ ++static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set) ++{ ++ int ret; ++ unsigned long flags; ++ u16 value; ++ ++ spin_lock_irqsave(&tahvo_lock, flags); ++ if (~mask) { ++ ret = tahvo_read_reg(reg); ++ if (ret < 0 || ret > 0xFFFF) ++ goto fatal_unlock; ++ value = ret; ++ } else ++ value = 0; ++ value &= ~mask; ++ value |= set; ++ ret = tahvo_write_reg(reg, value); ++ if (ret) ++ goto fatal_unlock; ++ spin_unlock_irqrestore(&tahvo_lock, flags); ++ ++ return; ++ ++fatal_unlock: ++ spin_unlock_irqrestore(&tahvo_lock, flags); ++ n810bm_emergency(bm, "tahvo_maskset"); ++} ++ ++static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value) ++{ ++ tahvo_maskset(bm, reg, 0xFFFF, value); ++} ++ ++static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask) ++{ ++ tahvo_maskset(bm, reg, mask, mask); ++} ++ ++static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask) ++{ ++ tahvo_maskset(bm, reg, mask, 0); ++} ++ +static u16 retu_read(struct n810bm *bm, unsigned int reg) +{ + int ret; @@ -160,7 +237,6 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + + return ret; +} -+#endif + +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set) +{ @@ -192,7 +268,7 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value) +{ -+ return retu_maskset(bm, reg, 0xFFFF, value); ++ retu_maskset(bm, reg, 0xFFFF, value); +} + +static int retu_adc_average(struct n810bm *bm, unsigned int chan, @@ -214,6 +290,77 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + return value; +} + ++/* Set the current measure timer that triggers on Tahvo IRQ 7 */ ++static void n810bm_set_current_measure_timer(struct n810bm *bm, ++ u16 millisec_interval) ++{ ++ u16 value = millisec_interval; ++ ++ if (value <= 0xF905) { ++ value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32; ++ value /= 16; ++ } else ++ value = 0xFF; ++ ++ tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF); ++ ++ tahvo_set(bm, TAHVO_REG_CHGCTL, ++ TAHVO_REG_CHGCTL_CURTIMRST); ++ tahvo_clear(bm, TAHVO_REG_CHGCTL, ++ TAHVO_REG_CHGCTL_CURTIMRST); ++} ++ ++static void n810bm_enable_current_measure(struct n810bm *bm, bool slow, bool irq) ++{ ++ u16 millisec_interval; ++ ++ if (slow) ++ millisec_interval = 1000; ++ else ++ millisec_interval = 250; ++ ++ /* Enable the current measurement circuitry */ ++ tahvo_set(bm, TAHVO_REG_CHGCTL, ++ TAHVO_REG_CHGCTL_CURMEAS); ++ ++ /* Setup the measurement timer */ ++ n810bm_set_current_measure_timer(bm, millisec_interval); ++ if (irq) ++ tahvo_enable_irq(TAHVO_INT_BATCURR); ++ ++ //TODO also do a software timer for safety. ++} ++ ++static void n810bm_disable_current_measure(struct n810bm *bm) ++{ ++ /* Disable the measurement timer */ ++ n810bm_set_current_measure_timer(bm, 0); ++ tahvo_disable_irq(TAHVO_INT_BATCURR); ++ ++ /* Disable the current measurement circuitry */ ++ tahvo_clear(bm, TAHVO_REG_CHGCTL, ++ TAHVO_REG_CHGCTL_CURMEAS); ++} ++ ++/* Measure the actual battery current. Returns a signed value in mA. ++ * Does only work, if current measurement was enabled. */ ++static int n810bm_measure_batt_current(struct n810bm *bm) ++{ ++ u16 retval; ++ int adc = 0, ma, i; ++ ++ for (i = 0; i < 3; i++) { ++ retval = tahvo_read(bm, TAHVO_REG_BATCURR); ++ adc += (s16)retval; /* Value is signed */ ++ } ++ adc /= 3; ++ ++ //TODO convert to mA ++ ma = adc; ++ ++ return ma; ++} ++ +static int adc_sanity_check(struct n810bm *bm, unsigned int channel) +{ + int value; @@ -224,9 +371,9 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + channel); + return -EIO; + } -+ dev_info(&bm->pdev->dev, -+ "GND ADC channel %u sanity check got value: %d", -+ channel, value); ++ dev_dbg(&bm->pdev->dev, ++ "GND ADC channel %u sanity check got value: %d", ++ channel, value); + if (value > 5) { + n810bm_emergency(bm, "GND ADC sanity check failed"); + return -EIO; @@ -345,19 +492,67 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + return percent; +} + ++/* Periodic check */ +static void n810bm_check_timer(unsigned long data) +{ + struct n810bm *bm = (struct n810bm *)data; + unsigned long flags; ++ u16 status; ++ bool battery_was_present, charger_was_present; ++ bool force_charge = 0; + int mv; + + spin_lock_irqsave(&bm->lock, flags); + -+ mv = n810bm_measure_batt_voltage(bm); -+ if (mv < 0) -+ n810bm_emergency(bm, "check timer: Failed to measure"); -+ if (mv < N810BM_MIN_VOLTAGE_THRES) -+ n810bm_emergency(bm, "check timer: Minimum voltage threshold reached"); ++ status = retu_read(bm, RETU_REG_STATUS); ++ battery_was_present = bm->battery_present; ++ charger_was_present = bm->charger_present; ++ bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL); ++ bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG); ++ ++ if (bm->battery_present != battery_was_present) { ++ /* Battery state changed */ ++ if (bm->battery_present) { ++ bm->capacity = n810bm_read_batt_capacity(bm); ++ if (bm->capacity == N810BM_CAP_UNKNOWN) { ++ dev_err(&bm->pdev->dev, "Unknown battery detected"); ++ } else { ++ dev_info(&bm->pdev->dev, "Detected %u mAh battery", ++ (unsigned int)bm->capacity); ++ } ++ } else { ++ bm->capacity = N810BM_CAP_NONE; ++ dev_info(&bm->pdev->dev, "The main battery was removed"); ++ //TODO what do if charging? ++ } ++ } ++ ++ if (bm->charger_present != charger_was_present) { ++ /* Charger state changed */ ++ dev_info(&bm->pdev->dev, "The charger was %s", ++ bm->charger_present ? "plugged in" : "removed"); ++ } ++ ++ if (bm->battery_present && !lipocharge_is_charging(&bm->charger)) { ++ mv = n810bm_measure_batt_voltage(bm); ++ if (mv < 0) ++ n810bm_emergency(bm, "check timer: Failed to measure"); ++ if (!bm->charger_present) { ++ if (mv < N810BM_MIN_VOLTAGE_THRES) { ++ n810bm_emergency(bm, "check timer: " ++ "Minimum voltage threshold reached"); ++ } ++ force_charge = 1; ++ } ++ } ++ ++ if (bm->charger_present && bm->battery_present) { ++ if (bm->charger_enabled || force_charge) { ++ if (!lipocharge_is_charging(&bm->charger)) { ++ //TODO start charging, if battery is below some threshold ++ } ++ } ++ } + + mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL)); + spin_unlock_irqrestore(&bm->lock, flags); @@ -367,28 +562,88 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + +static void n810bm_adc_irq_handler(unsigned long data) +{ -+// struct n810bm *bm = (struct n810bm *)data; ++ struct n810bm *bm = (struct n810bm *)data; + + retu_ack_irq(RETU_INT_ADCS); + //TODO -+printk("n810bm: ADC timer triggered\n"); ++dev_dbg(&bm->pdev->dev, "ADC interrupt triggered\n"); ++} ++ ++static void n810bm_tahvo_current_measure_irq_handler(unsigned long data) ++{ ++ struct n810bm *bm = (struct n810bm *)data; ++ ++ tahvo_ack_irq(TAHVO_INT_BATCURR); ++ //TODO ++dev_dbg(&bm->pdev->dev, "Tahvo measure IRQ triggered\n"); +} + ++#define DEFINE_SHOW_INT_FUNC(name, member) \ ++ static ssize_t n810bm_##name##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++ { \ ++ struct n810bm *bm = device_to_n810bm(dev); \ ++ ssize_t count; \ ++ \ ++ spin_lock_irq(&bm->lock); \ ++ count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member)); \ ++ spin_unlock_irq(&bm->lock); \ ++ \ ++ return count; \ ++ } ++ ++#define DEFINE_STORE_INT_FUNC(name, member) \ ++ static ssize_t n810bm_##name##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++ { \ ++ struct n810bm *bm = device_to_n810bm(dev); \ ++ long val; \ ++ int err; \ ++ \ ++ spin_lock_irq(&bm->lock); \ ++ err = strict_strtol(buf, 0, &val); \ ++ if (!err) \ ++ bm->member = (typeof(bm->member))val; \ ++ spin_unlock_irq(&bm->lock); \ ++ \ ++ return err ? err : count; \ ++ } ++ ++#define DEFINE_ATTR_SHOW_INT(name, member) \ ++ DEFINE_SHOW_INT_FUNC(name, member) \ ++ static DEVICE_ATTR(name, 0444, n810bm_##name##_show, NULL); ++ ++#define DEFINE_ATTR_SHOW_STORE_INT(name, member) \ ++ DEFINE_SHOW_INT_FUNC(name, member) \ ++ DEFINE_STORE_INT_FUNC(name, member) \ ++ static DEVICE_ATTR(name, 0644, n810bm_##name##_show, \ ++ n810bm_##name##_store); ++ ++DEFINE_ATTR_SHOW_INT(batt_present, battery_present); ++DEFINE_ATTR_SHOW_INT(charger_present, charger_present); ++DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled); ++ +static ssize_t n810bm_attr_charge_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct n810bm *bm = platform_get_drvdata(pdev); ++ struct n810bm *bm = device_to_n810bm(dev); + int err = -ENODEV; + ssize_t count = 0; + int millivolt; + + spin_lock_irq(&bm->lock); -+ millivolt = n810bm_measure_batt_voltage(bm); -+ if (millivolt >= 0) { -+ count = snprintf(buf, PAGE_SIZE, "%u\n", -+ n810bm_mvolt2percent(millivolt)); ++ if (bm->battery_present) { ++ millivolt = n810bm_measure_batt_voltage(bm); ++ if (millivolt >= 0) { ++ count = snprintf(buf, PAGE_SIZE, "%u\n", ++ n810bm_mvolt2percent(millivolt)); ++ err = 0; ++ } ++ } else { ++ count = snprintf(buf, PAGE_SIZE, "no battery\n"); + err = 0; + } + spin_unlock_irq(&bm->lock); @@ -401,13 +656,15 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + struct device_attribute *attr, + char *buf) +{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct n810bm *bm = platform_get_drvdata(pdev); ++ struct n810bm *bm = device_to_n810bm(dev); + ssize_t count; + + spin_lock_irq(&bm->lock); -+ count = snprintf(buf, PAGE_SIZE, "%u\n", -+ (unsigned int)bm->capacity); ++ if (bm->battery_present) { ++ count = snprintf(buf, PAGE_SIZE, "%d\n", ++ (int)bm->capacity); ++ } else ++ count = snprintf(buf, PAGE_SIZE, "no battery\n"); + spin_unlock_irq(&bm->lock); + + return count; @@ -418,8 +675,7 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + struct device_attribute *attr, + char *buf) +{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct n810bm *bm = platform_get_drvdata(pdev); ++ struct n810bm *bm = device_to_n810bm(dev); + ssize_t count = 0; + int k, err = -ENODEV; + @@ -435,33 +691,36 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c +} +static DEVICE_ATTR(batt_temp, 0444, n810bm_attr_battemp_show, NULL); + -+static ssize_t n810bm_attr_charger_voltage(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) ++static ssize_t n810bm_attr_charger_voltage_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) +{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct n810bm *bm = platform_get_drvdata(pdev); ++ struct n810bm *bm = device_to_n810bm(dev); + ssize_t count = 0; + int mv, err = -ENODEV; + + spin_lock_irq(&bm->lock); -+ mv = n810bm_measure_charger_voltage(bm); -+ if (mv >= 0) { -+ count = snprintf(buf, PAGE_SIZE, "%d\n", mv); ++ if (bm->charger_present) { ++ mv = n810bm_measure_charger_voltage(bm); ++ if (mv >= 0) { ++ count = snprintf(buf, PAGE_SIZE, "%d\n", mv); ++ err = 0; ++ } ++ } else { ++ count = snprintf(buf, PAGE_SIZE, "no charger\n"); + err = 0; + } + spin_unlock_irq(&bm->lock); + + return err ? err : count; +} -+static DEVICE_ATTR(charger_voltage, 0444, n810bm_attr_charger_voltage, NULL); ++static DEVICE_ATTR(charger_voltage, 0444, n810bm_attr_charger_voltage_show, NULL); + -+static ssize_t n810bm_attr_backup_batt_voltage(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) ++static ssize_t n810bm_attr_backup_batt_voltage_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) +{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct n810bm *bm = platform_get_drvdata(pdev); ++ struct n810bm *bm = device_to_n810bm(dev); + ssize_t count = 0; + int mv, err = -ENODEV; + @@ -475,11 +734,101 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + + return err ? err : count; +} -+static DEVICE_ATTR(backup_batt_voltage, 0444, n810bm_attr_backup_batt_voltage, NULL); ++static DEVICE_ATTR(backup_batt_voltage, 0444, n810bm_attr_backup_batt_voltage_show, NULL); ++ ++static ssize_t n810bm_attr_batt_current_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct n810bm *bm = device_to_n810bm(dev); ++ ssize_t count = 0; ++ int ma; ++ ++ spin_lock_irq(&bm->lock); ++ if (bm->battery_present) { ++ ma = n810bm_measure_batt_current(bm);//FIXME ++ count = snprintf(buf, PAGE_SIZE, "%d\n", ma); ++ } else ++ count = snprintf(buf, PAGE_SIZE, "no battery\n"); ++ spin_unlock_irq(&bm->lock); ++ ++ return count; ++} ++static DEVICE_ATTR(batt_current, 0444, n810bm_attr_batt_current_show, NULL); ++ ++//TODO remove this ++static ssize_t n810bm_attr_charger_status_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct n810bm *bm = device_to_n810bm(dev); ++ ssize_t count = 0; ++ unsigned int stat; ++ ++ spin_lock_irq(&bm->lock); ++ stat = retu_read(bm, RETU_REG_STATUS); ++ count = snprintf(buf, PAGE_SIZE, "0x%X\n", stat); ++ spin_unlock_irq(&bm->lock); ++ ++ return count; ++} ++static DEVICE_ATTR(charger_status, 0444, n810bm_attr_charger_status_show, NULL); ++ ++//TODO remove this ++static ssize_t n810bm_attr_charge_current_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct n810bm *bm = device_to_n810bm(dev); ++ ssize_t count = 0; ++ unsigned int val; ++ ++ spin_lock_irq(&bm->lock); ++ val = tahvo_read(bm, TAHVO_REG_CHGCURR); ++ count = snprintf(buf, PAGE_SIZE, "0x%X\n", val); ++ spin_unlock_irq(&bm->lock); ++ ++ return count; ++} ++ ++static ssize_t n810bm_attr_charge_current_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct n810bm *bm = device_to_n810bm(dev); ++ unsigned long val; ++ int err; ++ ++ spin_lock_irq(&bm->lock); ++ err = strict_strtoul(buf, 0, &val); ++ if (!err && val <= 0xFF) ++ tahvo_write(bm, TAHVO_REG_CHGCURR, val); ++ spin_unlock_irq(&bm->lock); ++ ++ return err ? err : count; ++} ++static DEVICE_ATTR(charge_current, 0644, ++ n810bm_attr_charge_current_show, ++ n810bm_attr_charge_current_store); ++ ++static const struct device_attribute *n810bm_attrs[] = { ++ &dev_attr_batt_present, ++ &dev_attr_batt_charge, ++ &dev_attr_batt_current, ++ &dev_attr_batt_capacity, ++ &dev_attr_batt_temp, ++ &dev_attr_backup_batt_voltage, ++ &dev_attr_charger_present, ++ &dev_attr_charger_voltage, ++ &dev_attr_charger_status, ++ &dev_attr_charger_enable, ++ &dev_attr_charge_current, ++}; + +static void n810bm_hw_exit(struct n810bm *bm) +{ + retu_write(bm, RETU_REG_ADCSCR, 0); ++ //TODO +} + +static int n810bm_hw_init(struct n810bm *bm) @@ -488,22 +837,103 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + + err = n810bm_check_adc_sanity(bm); + if (err) ++ return err; ++ ++ return 0; ++} ++ ++static int n810bm_device_init(struct n810bm *bm) ++{ ++ int attr_index; ++ int err; ++ ++ err = n810bm_hw_init(bm); ++ if (err) + goto error; -+ bm->capacity = n810bm_read_batt_capacity(bm); -+ if (bm->capacity == N810BM_CAP_UNKNOWN) { -+ dev_err(&bm->pdev->dev, "Unknown battery detected"); -+ err = -ENODEV; -+ goto error; ++ for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) { ++ err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]); ++ if (err) ++ goto err_unwind_attrs; + } -+ dev_info(&bm->pdev->dev, "Detected %u mAh battery\n", -+ (unsigned int)bm->capacity); ++ err = retu_request_irq(RETU_INT_ADCS, ++ n810bm_adc_irq_handler, ++ (unsigned long)bm, "n810bm"); ++ if (err) ++ goto err_unwind_attrs; ++ err = tahvo_request_irq(TAHVO_INT_BATCURR, ++ n810bm_tahvo_current_measure_irq_handler, ++ (unsigned long)bm, "n810bm"); ++ if (err) ++ goto err_free_retu_irq; ++ ++ lipocharge_init(&bm->charger); ++ mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL)); ++ ++ bm->initialized = 1; ++ dev_info(&bm->pdev->dev, "Battery management initialized"); + + return 0; + ++err_free_retu_irq: ++ retu_free_irq(RETU_INT_ADCS); ++err_unwind_attrs: ++ for (attr_index--; attr_index >= 0; attr_index--) ++ device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]); ++/*err_exit:*/ ++ n810bm_hw_exit(bm); +error: + return err; +} + ++static void n810bm_device_exit(struct n810bm *bm) ++{ ++ int i; ++ ++ if (!bm->initialized) ++ return; ++ ++ lipocharge_exit(&bm->charger); ++ tahvo_free_irq(TAHVO_INT_BATCURR); ++ retu_free_irq(RETU_INT_ADCS); ++ del_timer_sync(&bm->check_timer); ++ for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++) ++ device_remove_file(&bm->pdev->dev, n810bm_attrs[i]); ++ n810bm_hw_exit(bm); ++ release_firmware(bm->pmm_block); ++ ++ bm->initialized = 0; ++} ++ ++static void n810bm_pmm_block_found(const struct firmware *fw, void *context) ++{ ++ struct n810bm *bm = context; ++ int err; ++ ++ if (!fw) { ++ dev_err(&bm->pdev->dev, ++ "CAL PMM block image file not found"); ++ goto error; ++ } ++ if (fw->size != N810BM_PMM_BLOCK_SIZE || ++ memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) { ++ dev_err(&bm->pdev->dev, ++ "CAL PMM block image file has an invalid format"); ++ goto error; ++ } ++ ++ bm->pmm_block = fw; ++ err = n810bm_device_init(bm); ++ if (err) { ++ dev_err(&bm->pdev->dev, ++ "Failed to initialized battery management (%d)", err); ++ goto error; ++ } ++ ++ return; ++error: ++ release_firmware(fw); ++} ++ +static int __devinit n810bm_probe(struct platform_device *pdev) +{ + struct n810bm *bm; @@ -517,50 +947,23 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c + spin_lock_init(&bm->lock); + setup_timer(&bm->check_timer, n810bm_check_timer, (unsigned long)bm); + -+ err = n810bm_hw_init(bm); -+ if (err) ++ dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file " ++ N810BM_PMM_BLOCK_FILENAME); ++ err = request_firmware_nowait(THIS_MODULE, 1, ++ N810BM_PMM_BLOCK_FILENAME, ++ &bm->pdev->dev, GFP_KERNEL, ++ bm, n810bm_pmm_block_found); ++ if (err) { ++ dev_err(&bm->pdev->dev, ++ "Failed to request CAL PMM block image file (%d)", err); + goto err_free; -+ err = device_create_file(&pdev->dev, &dev_attr_batt_charge); -+ if (err) -+ goto err_exit; -+ err = device_create_file(&pdev->dev, &dev_attr_batt_capacity); -+ if (err) -+ goto err_rem_charge; -+ err = device_create_file(&pdev->dev, &dev_attr_batt_temp); -+ if (err) -+ goto err_rem_capa; -+ err = device_create_file(&pdev->dev, &dev_attr_charger_voltage); -+ if (err) -+ goto err_rem_temp; -+ err = device_create_file(&pdev->dev, &dev_attr_backup_batt_voltage); -+ if (err) -+ goto err_rem_chg; -+ err = retu_request_irq(RETU_INT_ADCS, n810bm_adc_irq_handler, -+ (unsigned long)bm, "n810bm"); -+ if (err) -+ goto err_rem_bkup; -+ -+ mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL)); -+ -+ dev_info(&pdev->dev, "Battery management initialized"); ++ } + + return 0; + -+err_rem_bkup: -+ device_remove_file(&pdev->dev, &dev_attr_backup_batt_voltage); -+err_rem_chg: -+ device_remove_file(&pdev->dev, &dev_attr_charger_voltage); -+err_rem_temp: -+ device_remove_file(&pdev->dev, &dev_attr_batt_temp); -+err_rem_capa: -+ device_remove_file(&pdev->dev, &dev_attr_batt_capacity); -+err_rem_charge: -+ device_remove_file(&pdev->dev, &dev_attr_batt_charge); -+err_exit: -+ n810bm_hw_exit(bm); +err_free: + kfree(bm); -+ platform_set_drvdata(pdev, NULL); ++ + return err; +} + @@ -568,14 +971,7 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c +{ + struct n810bm *bm = platform_get_drvdata(pdev); + -+ retu_free_irq(RETU_INT_ADCS); -+ del_timer_sync(&bm->check_timer); -+ device_remove_file(&pdev->dev, &dev_attr_backup_batt_voltage); -+ device_remove_file(&pdev->dev, &dev_attr_charger_voltage); -+ device_remove_file(&pdev->dev, &dev_attr_batt_temp); -+ device_remove_file(&pdev->dev, &dev_attr_batt_capacity); -+ device_remove_file(&pdev->dev, &dev_attr_batt_charge); -+ n810bm_hw_exit(bm); ++ n810bm_device_exit(bm); + + kfree(bm); + platform_set_drvdata(pdev, NULL); @@ -603,12 +999,13 @@ Index: linux-2.6.37/drivers/cbus/n810bm_main.c +module_exit(n810bm_modexit); + +MODULE_DESCRIPTION("Nokia n810 battery management"); ++MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Buesch"); Index: linux-2.6.37/drivers/cbus/retu.c =================================================================== ---- linux-2.6.37.orig/drivers/cbus/retu.c 2011-01-28 22:33:39.695216760 +0100 -+++ linux-2.6.37/drivers/cbus/retu.c 2011-01-28 22:33:39.754206648 +0100 +--- linux-2.6.37.orig/drivers/cbus/retu.c 2011-02-06 00:24:48.493004751 +0100 ++++ linux-2.6.37/drivers/cbus/retu.c 2011-02-06 00:24:48.551008149 +0100 @@ -85,10 +85,10 @@ * * This function writes a value to the specified register @@ -624,17 +1021,18 @@ Index: linux-2.6.37/drivers/cbus/retu.c void retu_set_clear_reg_bits(int reg, u16 set, u16 clear) Index: linux-2.6.37/drivers/cbus/retu.h =================================================================== ---- linux-2.6.37.orig/drivers/cbus/retu.h 2011-01-28 22:33:39.695216760 +0100 -+++ linux-2.6.37/drivers/cbus/retu.h 2011-01-28 22:40:55.380584650 +0100 -@@ -39,6 +39,7 @@ - #define RETU_REG_CC2 0x0e /* Common control register 2 */ +--- linux-2.6.37.orig/drivers/cbus/retu.h 2011-02-06 00:24:48.493004751 +0100 ++++ linux-2.6.37/drivers/cbus/retu.h 2011-02-06 00:24:48.551008149 +0100 +@@ -40,6 +40,8 @@ #define RETU_REG_CTRL_CLR 0x0f /* Regulator clear register */ #define RETU_REG_CTRL_SET 0x10 /* Regulator set register */ -+#define RETU_REG_UNK1 0x14 /* 0x1000 is set when charger is plugged in */ #define RETU_REG_STATUS 0x16 /* Status register */ ++#define RETU_REG_STATUS_BATAVAIL 0x0100 /* Battery available */ ++#define RETU_REG_STATUS_CHGPLUG 0x1000 /* Charger is plugged in */ #define RETU_REG_WATCHDOG 0x17 /* Watchdog register */ #define RETU_REG_AUDTXR 0x18 /* Audio Codec Tx register */ -@@ -57,8 +58,25 @@ + #define RETU_REG_MAX 0x1f +@@ -57,8 +59,25 @@ #define MAX_RETU_IRQ_HANDLERS 16 @@ -663,8 +1061,8 @@ Index: linux-2.6.37/drivers/cbus/retu.h int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name); Index: linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c =================================================================== ---- linux-2.6.37.orig/arch/arm/mach-omap2/board-n8x0.c 2011-01-28 22:33:39.679219500 +0100 -+++ linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c 2011-01-28 22:33:39.754206648 +0100 +--- linux-2.6.37.orig/arch/arm/mach-omap2/board-n8x0.c 2011-02-06 00:24:48.478003872 +0100 ++++ linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c 2011-02-06 00:24:48.551008149 +0100 @@ -907,6 +907,17 @@ ARRAY_SIZE(n8x0_gpio_switches)); } @@ -695,8 +1093,8 @@ Index: linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c Index: linux-2.6.37/drivers/cbus/lipocharge.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.37/drivers/cbus/lipocharge.c 2011-01-28 22:33:39.755206476 +0100 -@@ -0,0 +1,63 @@ ++++ linux-2.6.37/drivers/cbus/lipocharge.c 2011-02-06 12:57:01.427475867 +0100 +@@ -0,0 +1,58 @@ +/* + * Generic LIPO battery charger + * @@ -727,32 +1125,27 @@ Index: linux-2.6.37/drivers/cbus/lipocharge.c + spin_unlock(&c->lock); +} + -+struct lipocharge * lipocharge_alloc(gfp_t gfp) ++void lipocharge_init(struct lipocharge *c) +{ -+ struct lipocharge *c; -+ -+ c = kzalloc(sizeof(*c), gfp); -+ if (!c) -+ return NULL; + spin_lock_init(&c->lock); + setup_timer(&c->timer, lipocharge_timer, (unsigned long)c); -+ -+ return c; +} + -+void lipocharge_free(struct lipocharge *c) ++void lipocharge_exit(struct lipocharge *c) +{ -+ kfree(c); +} + +int lipocharge_start(struct lipocharge *c) +{ -+ if (!c->set_current || !c->get_voltage || ++ if (!c->set_charge_current || !c->get_charge_current || ++ !c->get_voltage || + !c->finished || !c->emergency) + return -EINVAL; + if (!c->top_voltage || c->top_voltage > 4200) + return -EINVAL; + //TODO ++ ++ return 0; +} + +void lipocharge_stop(struct lipocharge *c) @@ -763,8 +1156,8 @@ Index: linux-2.6.37/drivers/cbus/lipocharge.c Index: linux-2.6.37/drivers/cbus/lipocharge.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.37/drivers/cbus/lipocharge.h 2011-01-28 22:33:39.755206476 +0100 -@@ -0,0 +1,50 @@ ++++ linux-2.6.37/drivers/cbus/lipocharge.h 2011-02-06 12:36:29.800830614 +0100 +@@ -0,0 +1,55 @@ +#ifndef LIPOCHARGE_H_ +#define LIPOCHARGE_H_ + @@ -775,6 +1168,7 @@ Index: linux-2.6.37/drivers/cbus/lipocharge.h +#define LIPORATE(a,b) (((a) * 1000) + (b)) +#define LIPORATE_1C LIPORATE(1,0) /* 1C */ +#define LIPORATE_p8C LIPORATE(0,8) /* 0.8C */ ++#define LIPORATE_p6C LIPORATE(0,6) /* 0.6C */ + +/** struct lipocharge - A generic LIPO charger + * @@ -782,55 +1176,91 @@ Index: linux-2.6.37/drivers/cbus/lipocharge.h + * @rate: Charge rate. + * @top_voltage: Fully charged voltage, in mV. + * -+ * @set_current: Set the charge current, in mA. ++ * @set_charge_current: Set the charge current, in mA. ++ * @get_charge_current: Get the battery current, in signed mA. + * @get_voltage: Get the battery voltage, in mV. + * ++ * @finished: Charging finished. + * @emergency: Something went wrong. Force shutdown. -+ * -+ * @priv: opaque pointer. + */ -+struct lipocharge -+{ ++struct lipocharge { + unsigned int capacity; + unsigned int rate; + unsigned int top_voltage; + -+ int (*set_current)(struct lipocharge *c, unsigned int ma); ++ int (*set_charge_current)(struct lipocharge *c, unsigned int ma); ++ int (*get_charge_current)(struct lipocharge *c, int *ma); + int (*get_voltage)(struct lipocharge *c, unsigned int *mv); + + void (*finished)(struct lipocharge *c); + void (*emergency)(struct lipocharge *c); + -+ void *priv; -+ + /* internal */ + spinlock_t lock; + struct timer_list timer; ++ bool charging; +}; + -+struct lipocharge * lipocharge_alloc(gfp_t gfp); -+void lipocharge_free(struct lipocharge *c); ++void lipocharge_init(struct lipocharge *c); ++void lipocharge_exit(struct lipocharge *c); + +int lipocharge_start(struct lipocharge *c); +void lipocharge_stop(struct lipocharge *c); + ++static inline bool lipocharge_is_charging(struct lipocharge *c) ++{ ++ return c->charging; ++} ++ +#endif /* LIPOCHARGE_H_ */ Index: linux-2.6.37/drivers/cbus/tahvo.h =================================================================== ---- linux-2.6.37.orig/drivers/cbus/tahvo.h 2011-01-28 22:33:39.696216589 +0100 -+++ linux-2.6.37/drivers/cbus/tahvo.h 2011-01-28 22:33:39.755206476 +0100 -@@ -30,8 +30,14 @@ +--- linux-2.6.37.orig/drivers/cbus/tahvo.h 2011-02-06 00:24:48.494004810 +0100 ++++ linux-2.6.37/drivers/cbus/tahvo.h 2011-02-06 00:24:48.551008149 +0100 +@@ -30,17 +30,28 @@ #define TAHVO_REG_IDR 0x01 /* Interrupt ID */ #define TAHVO_REG_IDSR 0x02 /* Interrupt status */ #define TAHVO_REG_IMR 0x03 /* Interrupt mask */ -+#define TAHVO_REG_CHGCURR 0x04 /* Charge current control (8-bit) */ ++#define TAHVO_REG_CHGCURR 0x04 /* Charge current control PWM (8-bit) */ #define TAHVO_REG_LEDPWMR 0x05 /* LED PWM */ #define TAHVO_REG_USBR 0x06 /* USB control */ +#define TAHVO_REG_CHGCTL 0x08 /* Charge control register */ -+#define TAHVO_REG_CHGCTL_EN 0x0001 /* Global charge enable */ -+#define TAHVO_REG_CHGCTL2 0x0c /* Charge control register 2 */ ++#define TAHVO_REG_CHGCTL_EN 0x0001 /* Global charge enable */ ++#define TAHVO_REG_CHGCTL_PWMOVR 0x0004 /* PWM override. Force charge PWM to 100% duty cycle. */ ++#define TAHVO_REG_CHGCTL_UNK8 0x0008 /* XXX: Unknown. Written on init. */ ++#define TAHVO_REG_CHGCTL_CURMEAS 0x0040 /* Enable battery current measurement. */ ++#define TAHVO_REG_CHGCTL_CURTIMRST 0x0080 /* Current measure timer reset. */ ++#define TAHVO_REG_BATCURRTIMER 0x0c /* Battery current measure timer (8-bit) */ +#define TAHVO_REG_BATCURR 0x0d /* Battery (dis)charge current (signed 16-bit) */ + #define TAHVO_REG_MAX 0x0d /* Interrupt sources */ + #define TAHVO_INT_VBUSON 0 ++#define TAHVO_INT_BATCURR 7 /* Battery current measure timer */ + + #define MAX_TAHVO_IRQ_HANDLERS 8 + + int tahvo_read_reg(int reg); +-void tahvo_write_reg(int reg, u16 val); ++int tahvo_write_reg(int reg, u16 val); + void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear); + int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name); + void tahvo_free_irq(int id); +Index: linux-2.6.37/drivers/cbus/tahvo.c +=================================================================== +--- linux-2.6.37.orig/drivers/cbus/tahvo.c 2011-02-06 00:24:48.494004810 +0100 ++++ linux-2.6.37/drivers/cbus/tahvo.c 2011-02-06 00:24:48.552008207 +0100 +@@ -85,10 +85,10 @@ + * + * This function writes a value to the specified register + */ +-void tahvo_write_reg(int reg, u16 val) ++int tahvo_write_reg(int reg, u16 val) + { + BUG_ON(!tahvo_initialized); +- cbus_write_reg(cbus_host, TAHVO_ID, reg, val); ++ return cbus_write_reg(cbus_host, TAHVO_ID, reg, val); + } + + /**