18c0984e5SSebastian Reichel /* 28c0984e5SSebastian Reichel * Driver for batteries with DS2760 chips inside. 38c0984e5SSebastian Reichel * 48c0984e5SSebastian Reichel * Copyright © 2007 Anton Vorontsov 58c0984e5SSebastian Reichel * 2004-2007 Matt Reimer 68c0984e5SSebastian Reichel * 2004 Szabolcs Gyurko 78c0984e5SSebastian Reichel * 88c0984e5SSebastian Reichel * Use consistent with the GNU GPL is permitted, 98c0984e5SSebastian Reichel * provided that this copyright notice is 108c0984e5SSebastian Reichel * preserved in its entirety in all copies and derived works. 118c0984e5SSebastian Reichel * 128c0984e5SSebastian Reichel * Author: Anton Vorontsov <cbou@mail.ru> 138c0984e5SSebastian Reichel * February 2007 148c0984e5SSebastian Reichel * 158c0984e5SSebastian Reichel * Matt Reimer <mreimer@vpop.net> 168c0984e5SSebastian Reichel * April 2004, 2005, 2007 178c0984e5SSebastian Reichel * 188c0984e5SSebastian Reichel * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> 198c0984e5SSebastian Reichel * September 2004 208c0984e5SSebastian Reichel */ 218c0984e5SSebastian Reichel 228c0984e5SSebastian Reichel #include <linux/module.h> 238c0984e5SSebastian Reichel #include <linux/param.h> 248c0984e5SSebastian Reichel #include <linux/jiffies.h> 258c0984e5SSebastian Reichel #include <linux/workqueue.h> 268c0984e5SSebastian Reichel #include <linux/pm.h> 278c0984e5SSebastian Reichel #include <linux/slab.h> 288c0984e5SSebastian Reichel #include <linux/platform_device.h> 298c0984e5SSebastian Reichel #include <linux/power_supply.h> 308c0984e5SSebastian Reichel 31de0d6dbdSAndrew F. Davis #include <linux/w1.h> 328c0984e5SSebastian Reichel #include "../../w1/slaves/w1_ds2760.h" 338c0984e5SSebastian Reichel 348c0984e5SSebastian Reichel struct ds2760_device_info { 358c0984e5SSebastian Reichel struct device *dev; 368c0984e5SSebastian Reichel 378c0984e5SSebastian Reichel /* DS2760 data, valid after calling ds2760_battery_read_status() */ 388c0984e5SSebastian Reichel unsigned long update_time; /* jiffies when data read */ 398c0984e5SSebastian Reichel char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */ 408c0984e5SSebastian Reichel int voltage_raw; /* units of 4.88 mV */ 418c0984e5SSebastian Reichel int voltage_uV; /* units of µV */ 428c0984e5SSebastian Reichel int current_raw; /* units of 0.625 mA */ 438c0984e5SSebastian Reichel int current_uA; /* units of µA */ 448c0984e5SSebastian Reichel int accum_current_raw; /* units of 0.25 mAh */ 458c0984e5SSebastian Reichel int accum_current_uAh; /* units of µAh */ 468c0984e5SSebastian Reichel int temp_raw; /* units of 0.125 °C */ 478c0984e5SSebastian Reichel int temp_C; /* units of 0.1 °C */ 488c0984e5SSebastian Reichel int rated_capacity; /* units of µAh */ 498c0984e5SSebastian Reichel int rem_capacity; /* percentage */ 508c0984e5SSebastian Reichel int full_active_uAh; /* units of µAh */ 518c0984e5SSebastian Reichel int empty_uAh; /* units of µAh */ 528c0984e5SSebastian Reichel int life_sec; /* units of seconds */ 538c0984e5SSebastian Reichel int charge_status; /* POWER_SUPPLY_STATUS_* */ 548c0984e5SSebastian Reichel 558c0984e5SSebastian Reichel int full_counter; 568c0984e5SSebastian Reichel struct power_supply *bat; 578c0984e5SSebastian Reichel struct power_supply_desc bat_desc; 588c0984e5SSebastian Reichel struct device *w1_dev; 598c0984e5SSebastian Reichel struct workqueue_struct *monitor_wqueue; 608c0984e5SSebastian Reichel struct delayed_work monitor_work; 618c0984e5SSebastian Reichel struct delayed_work set_charged_work; 628c0984e5SSebastian Reichel }; 638c0984e5SSebastian Reichel 648c0984e5SSebastian Reichel static unsigned int cache_time = 1000; 658c0984e5SSebastian Reichel module_param(cache_time, uint, 0644); 668c0984e5SSebastian Reichel MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); 678c0984e5SSebastian Reichel 688c0984e5SSebastian Reichel static bool pmod_enabled; 698c0984e5SSebastian Reichel module_param(pmod_enabled, bool, 0644); 708c0984e5SSebastian Reichel MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit"); 718c0984e5SSebastian Reichel 728c0984e5SSebastian Reichel static unsigned int rated_capacity; 738c0984e5SSebastian Reichel module_param(rated_capacity, uint, 0644); 748c0984e5SSebastian Reichel MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index"); 758c0984e5SSebastian Reichel 768c0984e5SSebastian Reichel static unsigned int current_accum; 778c0984e5SSebastian Reichel module_param(current_accum, uint, 0644); 788c0984e5SSebastian Reichel MODULE_PARM_DESC(current_accum, "current accumulator value"); 798c0984e5SSebastian Reichel 808c0984e5SSebastian Reichel /* Some batteries have their rated capacity stored a N * 10 mAh, while 818c0984e5SSebastian Reichel * others use an index into this table. */ 828c0984e5SSebastian Reichel static int rated_capacities[] = { 838c0984e5SSebastian Reichel 0, 848c0984e5SSebastian Reichel 920, /* Samsung */ 858c0984e5SSebastian Reichel 920, /* BYD */ 868c0984e5SSebastian Reichel 920, /* Lishen */ 878c0984e5SSebastian Reichel 920, /* NEC */ 888c0984e5SSebastian Reichel 1440, /* Samsung */ 898c0984e5SSebastian Reichel 1440, /* BYD */ 908c0984e5SSebastian Reichel #ifdef CONFIG_MACH_H4700 918c0984e5SSebastian Reichel 1800, /* HP iPAQ hx4700 3.7V 1800mAh (359113-001) */ 928c0984e5SSebastian Reichel #else 938c0984e5SSebastian Reichel 1440, /* Lishen */ 948c0984e5SSebastian Reichel #endif 958c0984e5SSebastian Reichel 1440, /* NEC */ 968c0984e5SSebastian Reichel 2880, /* Samsung */ 978c0984e5SSebastian Reichel 2880, /* BYD */ 988c0984e5SSebastian Reichel 2880, /* Lishen */ 998c0984e5SSebastian Reichel 2880, /* NEC */ 1008c0984e5SSebastian Reichel #ifdef CONFIG_MACH_H4700 1018c0984e5SSebastian Reichel 0, 1028c0984e5SSebastian Reichel 3600, /* HP iPAQ hx4700 3.7V 3600mAh (359114-001) */ 1038c0984e5SSebastian Reichel #endif 1048c0984e5SSebastian Reichel }; 1058c0984e5SSebastian Reichel 1068c0984e5SSebastian Reichel /* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C 1078c0984e5SSebastian Reichel * temp is in Celsius */ 1088c0984e5SSebastian Reichel static int battery_interpolate(int array[], int temp) 1098c0984e5SSebastian Reichel { 1108c0984e5SSebastian Reichel int index, dt; 1118c0984e5SSebastian Reichel 1128c0984e5SSebastian Reichel if (temp <= 0) 1138c0984e5SSebastian Reichel return array[0]; 1148c0984e5SSebastian Reichel if (temp >= 40) 1158c0984e5SSebastian Reichel return array[4]; 1168c0984e5SSebastian Reichel 1178c0984e5SSebastian Reichel index = temp / 10; 1188c0984e5SSebastian Reichel dt = temp % 10; 1198c0984e5SSebastian Reichel 1208c0984e5SSebastian Reichel return array[index] + (((array[index + 1] - array[index]) * dt) / 10); 1218c0984e5SSebastian Reichel } 1228c0984e5SSebastian Reichel 1238c0984e5SSebastian Reichel static int ds2760_battery_read_status(struct ds2760_device_info *di) 1248c0984e5SSebastian Reichel { 1258c0984e5SSebastian Reichel int ret, i, start, count, scale[5]; 1268c0984e5SSebastian Reichel 1278c0984e5SSebastian Reichel if (di->update_time && time_before(jiffies, di->update_time + 1288c0984e5SSebastian Reichel msecs_to_jiffies(cache_time))) 1298c0984e5SSebastian Reichel return 0; 1308c0984e5SSebastian Reichel 1318c0984e5SSebastian Reichel /* The first time we read the entire contents of SRAM/EEPROM, 1328c0984e5SSebastian Reichel * but after that we just read the interesting bits that change. */ 1338c0984e5SSebastian Reichel if (di->update_time == 0) { 1348c0984e5SSebastian Reichel start = 0; 1358c0984e5SSebastian Reichel count = DS2760_DATA_SIZE; 1368c0984e5SSebastian Reichel } else { 1378c0984e5SSebastian Reichel start = DS2760_VOLTAGE_MSB; 1388c0984e5SSebastian Reichel count = DS2760_TEMP_LSB - start + 1; 1398c0984e5SSebastian Reichel } 1408c0984e5SSebastian Reichel 1418c0984e5SSebastian Reichel ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count); 1428c0984e5SSebastian Reichel if (ret != count) { 1438c0984e5SSebastian Reichel dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n", 1448c0984e5SSebastian Reichel di->w1_dev); 1458c0984e5SSebastian Reichel return 1; 1468c0984e5SSebastian Reichel } 1478c0984e5SSebastian Reichel 1488c0984e5SSebastian Reichel di->update_time = jiffies; 1498c0984e5SSebastian Reichel 1508c0984e5SSebastian Reichel /* DS2760 reports voltage in units of 4.88mV, but the battery class 1518c0984e5SSebastian Reichel * reports in units of uV, so convert by multiplying by 4880. */ 1528c0984e5SSebastian Reichel di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) | 1538c0984e5SSebastian Reichel (di->raw[DS2760_VOLTAGE_LSB] >> 5); 1548c0984e5SSebastian Reichel di->voltage_uV = di->voltage_raw * 4880; 1558c0984e5SSebastian Reichel 1568c0984e5SSebastian Reichel /* DS2760 reports current in signed units of 0.625mA, but the battery 1578c0984e5SSebastian Reichel * class reports in units of µA, so convert by multiplying by 625. */ 1588c0984e5SSebastian Reichel di->current_raw = 1598c0984e5SSebastian Reichel (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) | 1608c0984e5SSebastian Reichel (di->raw[DS2760_CURRENT_LSB] >> 3); 1618c0984e5SSebastian Reichel di->current_uA = di->current_raw * 625; 1628c0984e5SSebastian Reichel 1638c0984e5SSebastian Reichel /* DS2760 reports accumulated current in signed units of 0.25mAh. */ 1648c0984e5SSebastian Reichel di->accum_current_raw = 1658c0984e5SSebastian Reichel (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) | 1668c0984e5SSebastian Reichel di->raw[DS2760_CURRENT_ACCUM_LSB]; 1678c0984e5SSebastian Reichel di->accum_current_uAh = di->accum_current_raw * 250; 1688c0984e5SSebastian Reichel 1698c0984e5SSebastian Reichel /* DS2760 reports temperature in signed units of 0.125°C, but the 1708c0984e5SSebastian Reichel * battery class reports in units of 1/10 °C, so we convert by 1718c0984e5SSebastian Reichel * multiplying by .125 * 10 = 1.25. */ 1728c0984e5SSebastian Reichel di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) | 1738c0984e5SSebastian Reichel (di->raw[DS2760_TEMP_LSB] >> 5); 1748c0984e5SSebastian Reichel di->temp_C = di->temp_raw + (di->temp_raw / 4); 1758c0984e5SSebastian Reichel 1768c0984e5SSebastian Reichel /* At least some battery monitors (e.g. HP iPAQ) store the battery's 1778c0984e5SSebastian Reichel * maximum rated capacity. */ 1788c0984e5SSebastian Reichel if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities)) 1798c0984e5SSebastian Reichel di->rated_capacity = rated_capacities[ 1808c0984e5SSebastian Reichel (unsigned int)di->raw[DS2760_RATED_CAPACITY]]; 1818c0984e5SSebastian Reichel else 1828c0984e5SSebastian Reichel di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10; 1838c0984e5SSebastian Reichel 1848c0984e5SSebastian Reichel di->rated_capacity *= 1000; /* convert to µAh */ 1858c0984e5SSebastian Reichel 1868c0984e5SSebastian Reichel /* Calculate the full level at the present temperature. */ 1878c0984e5SSebastian Reichel di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | 1888c0984e5SSebastian Reichel di->raw[DS2760_ACTIVE_FULL + 1]; 1898c0984e5SSebastian Reichel 1908c0984e5SSebastian Reichel /* If the full_active_uAh value is not given, fall back to the rated 1918c0984e5SSebastian Reichel * capacity. This is likely to happen when chips are not part of the 1928c0984e5SSebastian Reichel * battery pack and is therefore not bootstrapped. */ 1938c0984e5SSebastian Reichel if (di->full_active_uAh == 0) 1948c0984e5SSebastian Reichel di->full_active_uAh = di->rated_capacity / 1000L; 1958c0984e5SSebastian Reichel 1968c0984e5SSebastian Reichel scale[0] = di->full_active_uAh; 1978c0984e5SSebastian Reichel for (i = 1; i < 5; i++) 1988c0984e5SSebastian Reichel scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 1 + i]; 1998c0984e5SSebastian Reichel 2008c0984e5SSebastian Reichel di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); 2018c0984e5SSebastian Reichel di->full_active_uAh *= 1000; /* convert to µAh */ 2028c0984e5SSebastian Reichel 2038c0984e5SSebastian Reichel /* Calculate the empty level at the present temperature. */ 2048c0984e5SSebastian Reichel scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4]; 2058c0984e5SSebastian Reichel for (i = 3; i >= 0; i--) 2068c0984e5SSebastian Reichel scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i]; 2078c0984e5SSebastian Reichel 2088c0984e5SSebastian Reichel di->empty_uAh = battery_interpolate(scale, di->temp_C / 10); 2098c0984e5SSebastian Reichel di->empty_uAh *= 1000; /* convert to µAh */ 2108c0984e5SSebastian Reichel 2118c0984e5SSebastian Reichel if (di->full_active_uAh == di->empty_uAh) 2128c0984e5SSebastian Reichel di->rem_capacity = 0; 2138c0984e5SSebastian Reichel else 2148c0984e5SSebastian Reichel /* From Maxim Application Note 131: remaining capacity = 2158c0984e5SSebastian Reichel * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */ 2168c0984e5SSebastian Reichel di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) / 2178c0984e5SSebastian Reichel (di->full_active_uAh - di->empty_uAh); 2188c0984e5SSebastian Reichel 2198c0984e5SSebastian Reichel if (di->rem_capacity < 0) 2208c0984e5SSebastian Reichel di->rem_capacity = 0; 2218c0984e5SSebastian Reichel if (di->rem_capacity > 100) 2228c0984e5SSebastian Reichel di->rem_capacity = 100; 2238c0984e5SSebastian Reichel 2248c0984e5SSebastian Reichel if (di->current_uA < -100L) 2258c0984e5SSebastian Reichel di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L) 2268c0984e5SSebastian Reichel / (di->current_uA / 100L); 2278c0984e5SSebastian Reichel else 2288c0984e5SSebastian Reichel di->life_sec = 0; 2298c0984e5SSebastian Reichel 2308c0984e5SSebastian Reichel return 0; 2318c0984e5SSebastian Reichel } 2328c0984e5SSebastian Reichel 2338c0984e5SSebastian Reichel static void ds2760_battery_set_current_accum(struct ds2760_device_info *di, 2348c0984e5SSebastian Reichel unsigned int acr_val) 2358c0984e5SSebastian Reichel { 2368c0984e5SSebastian Reichel unsigned char acr[2]; 2378c0984e5SSebastian Reichel 2388c0984e5SSebastian Reichel /* acr is in units of 0.25 mAh */ 2398c0984e5SSebastian Reichel acr_val *= 4L; 2408c0984e5SSebastian Reichel acr_val /= 1000; 2418c0984e5SSebastian Reichel 2428c0984e5SSebastian Reichel acr[0] = acr_val >> 8; 2438c0984e5SSebastian Reichel acr[1] = acr_val & 0xff; 2448c0984e5SSebastian Reichel 2458c0984e5SSebastian Reichel if (w1_ds2760_write(di->w1_dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2) 2468c0984e5SSebastian Reichel dev_warn(di->dev, "ACR write failed\n"); 2478c0984e5SSebastian Reichel } 2488c0984e5SSebastian Reichel 2498c0984e5SSebastian Reichel static void ds2760_battery_update_status(struct ds2760_device_info *di) 2508c0984e5SSebastian Reichel { 2518c0984e5SSebastian Reichel int old_charge_status = di->charge_status; 2528c0984e5SSebastian Reichel 2538c0984e5SSebastian Reichel ds2760_battery_read_status(di); 2548c0984e5SSebastian Reichel 2558c0984e5SSebastian Reichel if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) 2568c0984e5SSebastian Reichel di->full_counter = 0; 2578c0984e5SSebastian Reichel 2588c0984e5SSebastian Reichel if (power_supply_am_i_supplied(di->bat)) { 2598c0984e5SSebastian Reichel if (di->current_uA > 10000) { 2608c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 2618c0984e5SSebastian Reichel di->full_counter = 0; 2628c0984e5SSebastian Reichel } else if (di->current_uA < -5000) { 2638c0984e5SSebastian Reichel if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING) 2648c0984e5SSebastian Reichel dev_notice(di->dev, "not enough power to " 2658c0984e5SSebastian Reichel "charge\n"); 2668c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; 2678c0984e5SSebastian Reichel di->full_counter = 0; 2688c0984e5SSebastian Reichel } else if (di->current_uA < 10000 && 2698c0984e5SSebastian Reichel di->charge_status != POWER_SUPPLY_STATUS_FULL) { 2708c0984e5SSebastian Reichel 2718c0984e5SSebastian Reichel /* Don't consider the battery to be full unless 2728c0984e5SSebastian Reichel * we've seen the current < 10 mA at least two 2738c0984e5SSebastian Reichel * consecutive times. */ 2748c0984e5SSebastian Reichel 2758c0984e5SSebastian Reichel di->full_counter++; 2768c0984e5SSebastian Reichel 2778c0984e5SSebastian Reichel if (di->full_counter < 2) { 2788c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 2798c0984e5SSebastian Reichel } else { 2808c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_FULL; 2818c0984e5SSebastian Reichel ds2760_battery_set_current_accum(di, 2828c0984e5SSebastian Reichel di->full_active_uAh); 2838c0984e5SSebastian Reichel } 2848c0984e5SSebastian Reichel } 2858c0984e5SSebastian Reichel } else { 2868c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; 2878c0984e5SSebastian Reichel di->full_counter = 0; 2888c0984e5SSebastian Reichel } 2898c0984e5SSebastian Reichel 2908c0984e5SSebastian Reichel if (di->charge_status != old_charge_status) 2918c0984e5SSebastian Reichel power_supply_changed(di->bat); 2928c0984e5SSebastian Reichel } 2938c0984e5SSebastian Reichel 2948c0984e5SSebastian Reichel static void ds2760_battery_write_status(struct ds2760_device_info *di, 2958c0984e5SSebastian Reichel char status) 2968c0984e5SSebastian Reichel { 2978c0984e5SSebastian Reichel if (status == di->raw[DS2760_STATUS_REG]) 2988c0984e5SSebastian Reichel return; 2998c0984e5SSebastian Reichel 3008c0984e5SSebastian Reichel w1_ds2760_write(di->w1_dev, &status, DS2760_STATUS_WRITE_REG, 1); 3018c0984e5SSebastian Reichel w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); 3028c0984e5SSebastian Reichel w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); 3038c0984e5SSebastian Reichel } 3048c0984e5SSebastian Reichel 3058c0984e5SSebastian Reichel static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di, 3068c0984e5SSebastian Reichel unsigned char rated_capacity) 3078c0984e5SSebastian Reichel { 3088c0984e5SSebastian Reichel if (rated_capacity == di->raw[DS2760_RATED_CAPACITY]) 3098c0984e5SSebastian Reichel return; 3108c0984e5SSebastian Reichel 3118c0984e5SSebastian Reichel w1_ds2760_write(di->w1_dev, &rated_capacity, DS2760_RATED_CAPACITY, 1); 3128c0984e5SSebastian Reichel w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); 3138c0984e5SSebastian Reichel w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); 3148c0984e5SSebastian Reichel } 3158c0984e5SSebastian Reichel 3168c0984e5SSebastian Reichel static void ds2760_battery_write_active_full(struct ds2760_device_info *di, 3178c0984e5SSebastian Reichel int active_full) 3188c0984e5SSebastian Reichel { 3198c0984e5SSebastian Reichel unsigned char tmp[2] = { 3208c0984e5SSebastian Reichel active_full >> 8, 3218c0984e5SSebastian Reichel active_full & 0xff 3228c0984e5SSebastian Reichel }; 3238c0984e5SSebastian Reichel 3248c0984e5SSebastian Reichel if (tmp[0] == di->raw[DS2760_ACTIVE_FULL] && 3258c0984e5SSebastian Reichel tmp[1] == di->raw[DS2760_ACTIVE_FULL + 1]) 3268c0984e5SSebastian Reichel return; 3278c0984e5SSebastian Reichel 3288c0984e5SSebastian Reichel w1_ds2760_write(di->w1_dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp)); 3298c0984e5SSebastian Reichel w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0); 3308c0984e5SSebastian Reichel w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0); 3318c0984e5SSebastian Reichel 3328c0984e5SSebastian Reichel /* Write to the di->raw[] buffer directly - the DS2760_ACTIVE_FULL 3338c0984e5SSebastian Reichel * values won't be read back by ds2760_battery_read_status() */ 3348c0984e5SSebastian Reichel di->raw[DS2760_ACTIVE_FULL] = tmp[0]; 3358c0984e5SSebastian Reichel di->raw[DS2760_ACTIVE_FULL + 1] = tmp[1]; 3368c0984e5SSebastian Reichel } 3378c0984e5SSebastian Reichel 3388c0984e5SSebastian Reichel static void ds2760_battery_work(struct work_struct *work) 3398c0984e5SSebastian Reichel { 3408c0984e5SSebastian Reichel struct ds2760_device_info *di = container_of(work, 3418c0984e5SSebastian Reichel struct ds2760_device_info, monitor_work.work); 3428c0984e5SSebastian Reichel const int interval = HZ * 60; 3438c0984e5SSebastian Reichel 3448c0984e5SSebastian Reichel dev_dbg(di->dev, "%s\n", __func__); 3458c0984e5SSebastian Reichel 3468c0984e5SSebastian Reichel ds2760_battery_update_status(di); 3478c0984e5SSebastian Reichel queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); 3488c0984e5SSebastian Reichel } 3498c0984e5SSebastian Reichel 3508c0984e5SSebastian Reichel static void ds2760_battery_external_power_changed(struct power_supply *psy) 3518c0984e5SSebastian Reichel { 3528c0984e5SSebastian Reichel struct ds2760_device_info *di = power_supply_get_drvdata(psy); 3538c0984e5SSebastian Reichel 3548c0984e5SSebastian Reichel dev_dbg(di->dev, "%s\n", __func__); 3558c0984e5SSebastian Reichel 3568c0984e5SSebastian Reichel mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); 3578c0984e5SSebastian Reichel } 3588c0984e5SSebastian Reichel 3598c0984e5SSebastian Reichel 3608c0984e5SSebastian Reichel static void ds2760_battery_set_charged_work(struct work_struct *work) 3618c0984e5SSebastian Reichel { 3628c0984e5SSebastian Reichel char bias; 3638c0984e5SSebastian Reichel struct ds2760_device_info *di = container_of(work, 3648c0984e5SSebastian Reichel struct ds2760_device_info, set_charged_work.work); 3658c0984e5SSebastian Reichel 3668c0984e5SSebastian Reichel dev_dbg(di->dev, "%s\n", __func__); 3678c0984e5SSebastian Reichel 3688c0984e5SSebastian Reichel ds2760_battery_read_status(di); 3698c0984e5SSebastian Reichel 3708c0984e5SSebastian Reichel /* When we get notified by external circuitry that the battery is 3718c0984e5SSebastian Reichel * considered fully charged now, we know that there is no current 3728c0984e5SSebastian Reichel * flow any more. However, the ds2760's internal current meter is 3738c0984e5SSebastian Reichel * too inaccurate to rely on - spec say something ~15% failure. 3748c0984e5SSebastian Reichel * Hence, we use the current offset bias register to compensate 3758c0984e5SSebastian Reichel * that error. 3768c0984e5SSebastian Reichel */ 3778c0984e5SSebastian Reichel 3788c0984e5SSebastian Reichel if (!power_supply_am_i_supplied(di->bat)) 3798c0984e5SSebastian Reichel return; 3808c0984e5SSebastian Reichel 3818c0984e5SSebastian Reichel bias = (signed char) di->current_raw + 3828c0984e5SSebastian Reichel (signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS]; 3838c0984e5SSebastian Reichel 3848c0984e5SSebastian Reichel dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias); 3858c0984e5SSebastian Reichel 3868c0984e5SSebastian Reichel w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1); 3878c0984e5SSebastian Reichel w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); 3888c0984e5SSebastian Reichel w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); 3898c0984e5SSebastian Reichel 3908c0984e5SSebastian Reichel /* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS 3918c0984e5SSebastian Reichel * value won't be read back by ds2760_battery_read_status() */ 3928c0984e5SSebastian Reichel di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias; 3938c0984e5SSebastian Reichel } 3948c0984e5SSebastian Reichel 3958c0984e5SSebastian Reichel static void ds2760_battery_set_charged(struct power_supply *psy) 3968c0984e5SSebastian Reichel { 3978c0984e5SSebastian Reichel struct ds2760_device_info *di = power_supply_get_drvdata(psy); 3988c0984e5SSebastian Reichel 3998c0984e5SSebastian Reichel /* postpone the actual work by 20 secs. This is for debouncing GPIO 4008c0984e5SSebastian Reichel * signals and to let the current value settle. See AN4188. */ 4018c0984e5SSebastian Reichel mod_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20); 4028c0984e5SSebastian Reichel } 4038c0984e5SSebastian Reichel 4048c0984e5SSebastian Reichel static int ds2760_battery_get_property(struct power_supply *psy, 4058c0984e5SSebastian Reichel enum power_supply_property psp, 4068c0984e5SSebastian Reichel union power_supply_propval *val) 4078c0984e5SSebastian Reichel { 4088c0984e5SSebastian Reichel struct ds2760_device_info *di = power_supply_get_drvdata(psy); 4098c0984e5SSebastian Reichel 4108c0984e5SSebastian Reichel switch (psp) { 4118c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 4128c0984e5SSebastian Reichel val->intval = di->charge_status; 4138c0984e5SSebastian Reichel return 0; 4148c0984e5SSebastian Reichel default: 4158c0984e5SSebastian Reichel break; 4168c0984e5SSebastian Reichel } 4178c0984e5SSebastian Reichel 4188c0984e5SSebastian Reichel ds2760_battery_read_status(di); 4198c0984e5SSebastian Reichel 4208c0984e5SSebastian Reichel switch (psp) { 4218c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 4228c0984e5SSebastian Reichel val->intval = di->voltage_uV; 4238c0984e5SSebastian Reichel break; 4248c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CURRENT_NOW: 4258c0984e5SSebastian Reichel val->intval = di->current_uA; 4268c0984e5SSebastian Reichel break; 4278c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 4288c0984e5SSebastian Reichel val->intval = di->rated_capacity; 4298c0984e5SSebastian Reichel break; 4308c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 4318c0984e5SSebastian Reichel val->intval = di->full_active_uAh; 4328c0984e5SSebastian Reichel break; 4338c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_EMPTY: 4348c0984e5SSebastian Reichel val->intval = di->empty_uAh; 4358c0984e5SSebastian Reichel break; 4368c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 4378c0984e5SSebastian Reichel val->intval = di->accum_current_uAh; 4388c0984e5SSebastian Reichel break; 4398c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP: 4408c0984e5SSebastian Reichel val->intval = di->temp_C; 4418c0984e5SSebastian Reichel break; 4428c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 4438c0984e5SSebastian Reichel val->intval = di->life_sec; 4448c0984e5SSebastian Reichel break; 4458c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 4468c0984e5SSebastian Reichel val->intval = di->rem_capacity; 4478c0984e5SSebastian Reichel break; 4488c0984e5SSebastian Reichel default: 4498c0984e5SSebastian Reichel return -EINVAL; 4508c0984e5SSebastian Reichel } 4518c0984e5SSebastian Reichel 4528c0984e5SSebastian Reichel return 0; 4538c0984e5SSebastian Reichel } 4548c0984e5SSebastian Reichel 4558c0984e5SSebastian Reichel static int ds2760_battery_set_property(struct power_supply *psy, 4568c0984e5SSebastian Reichel enum power_supply_property psp, 4578c0984e5SSebastian Reichel const union power_supply_propval *val) 4588c0984e5SSebastian Reichel { 4598c0984e5SSebastian Reichel struct ds2760_device_info *di = power_supply_get_drvdata(psy); 4608c0984e5SSebastian Reichel 4618c0984e5SSebastian Reichel switch (psp) { 4628c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 4638c0984e5SSebastian Reichel /* the interface counts in uAh, convert the value */ 4648c0984e5SSebastian Reichel ds2760_battery_write_active_full(di, val->intval / 1000L); 4658c0984e5SSebastian Reichel break; 4668c0984e5SSebastian Reichel 4678c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 4688c0984e5SSebastian Reichel /* ds2760_battery_set_current_accum() does the conversion */ 4698c0984e5SSebastian Reichel ds2760_battery_set_current_accum(di, val->intval); 4708c0984e5SSebastian Reichel break; 4718c0984e5SSebastian Reichel 4728c0984e5SSebastian Reichel default: 4738c0984e5SSebastian Reichel return -EPERM; 4748c0984e5SSebastian Reichel } 4758c0984e5SSebastian Reichel 4768c0984e5SSebastian Reichel return 0; 4778c0984e5SSebastian Reichel } 4788c0984e5SSebastian Reichel 4798c0984e5SSebastian Reichel static int ds2760_battery_property_is_writeable(struct power_supply *psy, 4808c0984e5SSebastian Reichel enum power_supply_property psp) 4818c0984e5SSebastian Reichel { 4828c0984e5SSebastian Reichel switch (psp) { 4838c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 4848c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 4858c0984e5SSebastian Reichel return 1; 4868c0984e5SSebastian Reichel 4878c0984e5SSebastian Reichel default: 4888c0984e5SSebastian Reichel break; 4898c0984e5SSebastian Reichel } 4908c0984e5SSebastian Reichel 4918c0984e5SSebastian Reichel return 0; 4928c0984e5SSebastian Reichel } 4938c0984e5SSebastian Reichel 4948c0984e5SSebastian Reichel static enum power_supply_property ds2760_battery_props[] = { 4958c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4968c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4978c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 4998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 5008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_EMPTY, 5018c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 5028c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 5038c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 5048c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 5058c0984e5SSebastian Reichel }; 5068c0984e5SSebastian Reichel 5078c0984e5SSebastian Reichel static int ds2760_battery_probe(struct platform_device *pdev) 5088c0984e5SSebastian Reichel { 5098c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 5108c0984e5SSebastian Reichel char status; 5118c0984e5SSebastian Reichel int retval = 0; 5128c0984e5SSebastian Reichel struct ds2760_device_info *di; 5138c0984e5SSebastian Reichel 5148c0984e5SSebastian Reichel di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); 5158c0984e5SSebastian Reichel if (!di) { 5168c0984e5SSebastian Reichel retval = -ENOMEM; 5178c0984e5SSebastian Reichel goto di_alloc_failed; 5188c0984e5SSebastian Reichel } 5198c0984e5SSebastian Reichel 5208c0984e5SSebastian Reichel platform_set_drvdata(pdev, di); 5218c0984e5SSebastian Reichel 5228c0984e5SSebastian Reichel di->dev = &pdev->dev; 5238c0984e5SSebastian Reichel di->w1_dev = pdev->dev.parent; 5248c0984e5SSebastian Reichel di->bat_desc.name = dev_name(&pdev->dev); 5258c0984e5SSebastian Reichel di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; 5268c0984e5SSebastian Reichel di->bat_desc.properties = ds2760_battery_props; 5278c0984e5SSebastian Reichel di->bat_desc.num_properties = ARRAY_SIZE(ds2760_battery_props); 5288c0984e5SSebastian Reichel di->bat_desc.get_property = ds2760_battery_get_property; 5298c0984e5SSebastian Reichel di->bat_desc.set_property = ds2760_battery_set_property; 5308c0984e5SSebastian Reichel di->bat_desc.property_is_writeable = 5318c0984e5SSebastian Reichel ds2760_battery_property_is_writeable; 5328c0984e5SSebastian Reichel di->bat_desc.set_charged = ds2760_battery_set_charged; 5338c0984e5SSebastian Reichel di->bat_desc.external_power_changed = 5348c0984e5SSebastian Reichel ds2760_battery_external_power_changed; 5358c0984e5SSebastian Reichel 5368c0984e5SSebastian Reichel psy_cfg.drv_data = di; 5378c0984e5SSebastian Reichel 5388c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; 5398c0984e5SSebastian Reichel 5408c0984e5SSebastian Reichel /* enable sleep mode feature */ 5418c0984e5SSebastian Reichel ds2760_battery_read_status(di); 5428c0984e5SSebastian Reichel status = di->raw[DS2760_STATUS_REG]; 5438c0984e5SSebastian Reichel if (pmod_enabled) 5448c0984e5SSebastian Reichel status |= DS2760_STATUS_PMOD; 5458c0984e5SSebastian Reichel else 5468c0984e5SSebastian Reichel status &= ~DS2760_STATUS_PMOD; 5478c0984e5SSebastian Reichel 5488c0984e5SSebastian Reichel ds2760_battery_write_status(di, status); 5498c0984e5SSebastian Reichel 5508c0984e5SSebastian Reichel /* set rated capacity from module param */ 5518c0984e5SSebastian Reichel if (rated_capacity) 5528c0984e5SSebastian Reichel ds2760_battery_write_rated_capacity(di, rated_capacity); 5538c0984e5SSebastian Reichel 5548c0984e5SSebastian Reichel /* set current accumulator if given as parameter. 5558c0984e5SSebastian Reichel * this should only be done for bootstrapping the value */ 5568c0984e5SSebastian Reichel if (current_accum) 5578c0984e5SSebastian Reichel ds2760_battery_set_current_accum(di, current_accum); 5588c0984e5SSebastian Reichel 5598c0984e5SSebastian Reichel di->bat = power_supply_register(&pdev->dev, &di->bat_desc, &psy_cfg); 5608c0984e5SSebastian Reichel if (IS_ERR(di->bat)) { 5618c0984e5SSebastian Reichel dev_err(di->dev, "failed to register battery\n"); 5628c0984e5SSebastian Reichel retval = PTR_ERR(di->bat); 5638c0984e5SSebastian Reichel goto batt_failed; 5648c0984e5SSebastian Reichel } 5658c0984e5SSebastian Reichel 5668c0984e5SSebastian Reichel INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); 5678c0984e5SSebastian Reichel INIT_DELAYED_WORK(&di->set_charged_work, 5688c0984e5SSebastian Reichel ds2760_battery_set_charged_work); 569b732ace4SBhaktipriya Shridhar di->monitor_wqueue = alloc_ordered_workqueue(dev_name(&pdev->dev), 570b732ace4SBhaktipriya Shridhar WQ_MEM_RECLAIM); 5718c0984e5SSebastian Reichel if (!di->monitor_wqueue) { 5728c0984e5SSebastian Reichel retval = -ESRCH; 5738c0984e5SSebastian Reichel goto workqueue_failed; 5748c0984e5SSebastian Reichel } 5758c0984e5SSebastian Reichel queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1); 5768c0984e5SSebastian Reichel 5778c0984e5SSebastian Reichel goto success; 5788c0984e5SSebastian Reichel 5798c0984e5SSebastian Reichel workqueue_failed: 5808c0984e5SSebastian Reichel power_supply_unregister(di->bat); 5818c0984e5SSebastian Reichel batt_failed: 5828c0984e5SSebastian Reichel di_alloc_failed: 5838c0984e5SSebastian Reichel success: 5848c0984e5SSebastian Reichel return retval; 5858c0984e5SSebastian Reichel } 5868c0984e5SSebastian Reichel 5878c0984e5SSebastian Reichel static int ds2760_battery_remove(struct platform_device *pdev) 5888c0984e5SSebastian Reichel { 5898c0984e5SSebastian Reichel struct ds2760_device_info *di = platform_get_drvdata(pdev); 5908c0984e5SSebastian Reichel 5918c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->monitor_work); 5928c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->set_charged_work); 5938c0984e5SSebastian Reichel destroy_workqueue(di->monitor_wqueue); 5948c0984e5SSebastian Reichel power_supply_unregister(di->bat); 5958c0984e5SSebastian Reichel 5968c0984e5SSebastian Reichel return 0; 5978c0984e5SSebastian Reichel } 5988c0984e5SSebastian Reichel 5998c0984e5SSebastian Reichel #ifdef CONFIG_PM 6008c0984e5SSebastian Reichel 6018c0984e5SSebastian Reichel static int ds2760_battery_suspend(struct platform_device *pdev, 6028c0984e5SSebastian Reichel pm_message_t state) 6038c0984e5SSebastian Reichel { 6048c0984e5SSebastian Reichel struct ds2760_device_info *di = platform_get_drvdata(pdev); 6058c0984e5SSebastian Reichel 6068c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; 6078c0984e5SSebastian Reichel 6088c0984e5SSebastian Reichel return 0; 6098c0984e5SSebastian Reichel } 6108c0984e5SSebastian Reichel 6118c0984e5SSebastian Reichel static int ds2760_battery_resume(struct platform_device *pdev) 6128c0984e5SSebastian Reichel { 6138c0984e5SSebastian Reichel struct ds2760_device_info *di = platform_get_drvdata(pdev); 6148c0984e5SSebastian Reichel 6158c0984e5SSebastian Reichel di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; 6168c0984e5SSebastian Reichel power_supply_changed(di->bat); 6178c0984e5SSebastian Reichel 6188c0984e5SSebastian Reichel mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); 6198c0984e5SSebastian Reichel 6208c0984e5SSebastian Reichel return 0; 6218c0984e5SSebastian Reichel } 6228c0984e5SSebastian Reichel 6238c0984e5SSebastian Reichel #else 6248c0984e5SSebastian Reichel 6258c0984e5SSebastian Reichel #define ds2760_battery_suspend NULL 6268c0984e5SSebastian Reichel #define ds2760_battery_resume NULL 6278c0984e5SSebastian Reichel 6288c0984e5SSebastian Reichel #endif /* CONFIG_PM */ 6298c0984e5SSebastian Reichel 6308c0984e5SSebastian Reichel MODULE_ALIAS("platform:ds2760-battery"); 6318c0984e5SSebastian Reichel 6328c0984e5SSebastian Reichel static struct platform_driver ds2760_battery_driver = { 6338c0984e5SSebastian Reichel .driver = { 6348c0984e5SSebastian Reichel .name = "ds2760-battery", 6358c0984e5SSebastian Reichel }, 6368c0984e5SSebastian Reichel .probe = ds2760_battery_probe, 6378c0984e5SSebastian Reichel .remove = ds2760_battery_remove, 6388c0984e5SSebastian Reichel .suspend = ds2760_battery_suspend, 6398c0984e5SSebastian Reichel .resume = ds2760_battery_resume, 6408c0984e5SSebastian Reichel }; 6418c0984e5SSebastian Reichel 6428c0984e5SSebastian Reichel module_platform_driver(ds2760_battery_driver); 6438c0984e5SSebastian Reichel 6448c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 6458c0984e5SSebastian Reichel MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " 6468c0984e5SSebastian Reichel "Matt Reimer <mreimer@vpop.net>, " 6478c0984e5SSebastian Reichel "Anton Vorontsov <cbou@mail.ru>"); 6488c0984e5SSebastian Reichel MODULE_DESCRIPTION("ds2760 battery driver"); 649