/* * Driver for batteries with DS2746 chips inside. * * Copyright © 2009 Matthew Kern * 2007 Anton Vorontsov * 2004-2007 Matt Reimer * 2004 Szabolcs Gyurko * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * * Author: Matthew Kern * February 2007 * * Matt Reimer * April 2004, 2005, 2007 * * Szabolcs Gyurko * September 2004 * * Martin Johnson * February 2009 - Simplified for HTC Kaiser * * Stefan Seidel * March 2009 - Correct battery charge pctg. calculation */ /* I was originally writing this as a generic ds2746 driver, but there are too many kaiser-specific things, so... no. */ #include #include #include #include #include #include #include #include #ifdef CONFIG_ANDROID_POWER #include static android_suspend_lock_t vbus_suspend_lock; #endif /* definitions for registers we care about. */ #define DS2746_DATA_SIZE 0x12 #define DS2746_STATUS_REG 0x01 #define DS2746_AUX0_MSB 0x08 #define DS2746_AUX0_LSB 0x09 #define DS2746_AUX1_MSB 0x0a #define DS2746_AUX1_LSB 0x0b #define DS2746_VOLTAGE_MSB 0x0c #define DS2746_VOLTAGE_LSB 0x0d #define DS2746_CURRENT_MSB 0x0e #define DS2746_CURRENT_LSB 0x0f #define DS2746_CURRENT_ACCUM_MSB 0x10 #define DS2746_CURRENT_ACCUM_LSB 0x11 #define KAISER_RSNS 10 //in mOHM. From the maths, this should be correct #define KAISER_BATTERY_RATING 2700 //in mAh - the standard battery (KAIS160) is this) struct i2c_client *pclient=0; static int battery_capacity = KAISER_BATTERY_RATING; module_param(battery_capacity, int, 0644); MODULE_PARM_DESC(battery_capacity, "Estimated battery capacity in mAh"); static int i2c_read(int r) { unsigned char i2c_msg[1]; unsigned char i2c_data[2]; i2c_msg[0]=r; i2c_master_send(pclient, i2c_msg, 1); i2c_master_recv(pclient, i2c_data, 2); // printk("ds2746_i2c_read(%x)=%x %x\n",r,i2c_data[0],i2c_data[1]); return i2c_data[0]; } static void i2c_write(int r, int v) { unsigned char i2c_msg[3]; i2c_msg[0]=r; i2c_msg[1]=v>>8; i2c_msg[2]=v&0xFF; i2c_master_send(pclient, i2c_msg, 3); } static int reading2capacity(int r) { return (625L * r) / (100L * KAISER_RSNS); } static int capacity2reading(int c) { return (100L * c * KAISER_RSNS) / 625; } struct battery_info_reply { u32 batt_id; /* Battery ID from ADC */ u32 batt_vol; /* Battery voltage from ADC */ u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ int batt_current; /* Battery current from ADC */ u32 level; /* formula */ u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ u32 charging_enabled; /* 0: Disable, 1: Enable */ u32 full_bat; /* Full capacity of battery (mAh) */ }; int ds2746_battery_read_status(struct battery_info_reply *b) { short s; int aux0,aux1; int aux0r,aux1r; // printk("read battery status\n"); if(!pclient) { printk("client is null\n"); b->level=50; return 0; } s=i2c_read(DS2746_VOLTAGE_LSB) | i2c_read(DS2746_VOLTAGE_MSB)<<8; b->batt_vol=((s>>4)*2440)/1000; s=i2c_read(DS2746_CURRENT_LSB) | i2c_read(DS2746_CURRENT_MSB)<<8; b->batt_current=((s>>2)*6250) / (KAISER_RSNS*1000); /* if battery voltage is < 3.3V and depleting, we assume it's almost empty! */ if (b->batt_vol < 3300 && b->batt_current < 0) { /* use approximate formula: 3.3V=5%, 3.0V=0% */ i2c_write(DS2746_CURRENT_ACCUM_MSB, 1L*battery_capacity*KAISER_RSNS*((5*b->batt_vol)/3-5000)/62500); } s=i2c_read(DS2746_CURRENT_ACCUM_LSB) | i2c_read(DS2746_CURRENT_ACCUM_MSB)<<8; if (s > 0) { int max_seen_capacity = capacity2reading(battery_capacity); if (s > max_seen_capacity) { /* if the battery is "fuller" than expected, * update our expectations */ battery_capacity = reading2capacity(s); } else { /* check if we're >4.2V and <5mA */ if ((b->batt_vol >= 4200) && (b->batt_current < 5)) { /* that's almost full, so let's assume it is * (but leave 10mAh space to the max) */ battery_capacity = reading2capacity(s) + 10; } } } // level = 100 * (s * 6.25 / (BATTERY_RATING * KAISER_RSNS)) b->level=(s * 625) / (battery_capacity * KAISER_RSNS); /* if we read 0%, */ if(b->level<1) { /* only report 0% if we're really down, <3.1V */ b->level=((b->batt_vol <= 3100 )?0:1); } if(b->level>100) b->level=100; b->full_bat = 100; aux0 = i2c_read(DS2746_AUX0_LSB)>>4 | i2c_read(DS2746_AUX0_MSB)<<4; aux1 = i2c_read(DS2746_AUX1_LSB)>>4 | i2c_read(DS2746_AUX1_MSB)<<4; // ratio aux0r = (1000L*aux0)/2047; aux1r = (1000L*aux1)/2047; // resistance in Ohm (R_AUX=10kOhm) aux0r = (10000L*aux0r)/(1000-aux0r); aux1r = (10000L*aux1r)/(1000-aux1r); printk("ds2746: %dmV %dmA charge: %d/100 (%d units)\n aux0: %d (%d) aux1: %d (%d)\n",b->batt_vol,b->batt_current,b->level,s,aux0,aux0r,aux1,aux1r); return 0; } static int ds2746_probe(struct i2c_client *client) { printk("DS-2746 Probe\n"); pclient = client; return 0; } static int ds2746_detach_client(struct i2c_client *client) { i2c_detach_client(client); return 0; } static struct i2c_driver ds2746_battery_driver = { .probe = ds2746_probe, .remove = ds2746_detach_client, .suspend = NULL, .resume = NULL, .driver = { .name = "ds2746-battery", }, }; static int __init ds2746_battery_init(void) { #ifdef CONFIG_ANDROID_POWER vbus_suspend_lock.name = "vbus_present"; android_init_suspend_lock(&vbus_suspend_lock); #endif return i2c_add_driver(&ds2746_battery_driver); } static void __exit ds2746_battery_exit(void) { i2c_del_driver(&ds2746_battery_driver); } module_init(ds2746_battery_init); module_exit(ds2746_battery_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Matthew Kern , " "Szabolcs Gyurko , " "Matt Reimer , " "Anton Vorontsov "); MODULE_DESCRIPTION("Battery driver for HTC kaiser");