Blame view
drivers/power/bq27x00_battery.c
21.6 KB
b996ad0e9 power_supply: Sup... |
1 2 3 4 5 |
/* * BQ27x00 battery driver * * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> |
7fb7ba588 bq27x00: Add bq27... |
6 |
* Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> |
73c244a8a bq27x00_battery: ... |
7 |
* Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com> |
b996ad0e9 power_supply: Sup... |
8 9 10 11 12 13 14 15 16 17 18 19 |
* * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ |
631c17ee5 bq27x00: Add new ... |
20 21 22 23 24 25 |
/* * Datasheets: * http://focus.ti.com/docs/prod/folders/print/bq27000.html * http://focus.ti.com/docs/prod/folders/print/bq27500.html */ |
b996ad0e9 power_supply: Sup... |
26 27 28 29 30 31 32 33 |
#include <linux/module.h> #include <linux/param.h> #include <linux/jiffies.h> #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/idr.h> |
b996ad0e9 power_supply: Sup... |
34 |
#include <linux/i2c.h> |
5a0e3ad6a include cleanup: ... |
35 |
#include <linux/slab.h> |
8aef7e8f8 bq27x00_battery: ... |
36 |
#include <asm/unaligned.h> |
b996ad0e9 power_supply: Sup... |
37 |
|
7fb7ba588 bq27x00: Add bq27... |
38 |
#include <linux/power/bq27x00_battery.h> |
631c17ee5 bq27x00: Add new ... |
39 |
#define DRIVER_VERSION "1.2.0" |
b996ad0e9 power_supply: Sup... |
40 41 42 |
#define BQ27x00_REG_TEMP 0x06 #define BQ27x00_REG_VOLT 0x08 |
b996ad0e9 power_supply: Sup... |
43 44 |
#define BQ27x00_REG_AI 0x14 #define BQ27x00_REG_FLAGS 0x0A |
4e924a814 power_supply: bq2... |
45 46 47 |
#define BQ27x00_REG_TTE 0x16 #define BQ27x00_REG_TTF 0x18 #define BQ27x00_REG_TTECP 0x26 |
631c17ee5 bq27x00: Add new ... |
48 49 50 51 |
#define BQ27x00_REG_NAC 0x0C /* Nominal available capaciy */ #define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ #define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ #define BQ27x00_REG_AE 0x22 /* Available enery */ |
b996ad0e9 power_supply: Sup... |
52 |
|
e20908d95 power_supply: bq2... |
53 |
#define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ |
631c17ee5 bq27x00: Add new ... |
54 |
#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ |
d66bab3f3 bq27x00_battery: ... |
55 56 |
#define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ #define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ |
4b226c2cf bq27x00_battery: ... |
57 |
#define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ |
c1b9ab67e bq27x00: Give mor... |
58 |
#define BQ27000_FLAG_FC BIT(5) |
d66bab3f3 bq27x00_battery: ... |
59 |
#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ |
e20908d95 power_supply: bq2... |
60 |
|
bf7d41404 bq27x00: Minor cl... |
61 |
#define BQ27500_REG_SOC 0x2C |
631c17ee5 bq27x00: Add new ... |
62 |
#define BQ27500_REG_DCAP 0x3C /* Design capacity */ |
270968c09 bq27x00_battery: ... |
63 |
#define BQ27500_FLAG_DSG BIT(0) /* Discharging */ |
d66bab3f3 bq27x00_battery: ... |
64 65 |
#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ #define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ |
270968c09 bq27x00_battery: ... |
66 67 |
#define BQ27500_FLAG_CHG BIT(8) /* Charging */ #define BQ27500_FLAG_FC BIT(9) /* Fully charged */ |
e20908d95 power_supply: bq2... |
68 |
|
a2e5118c3 bq27x00: Fix CURR... |
69 |
#define BQ27000_RS 20 /* Resistor sense */ |
b996ad0e9 power_supply: Sup... |
70 71 |
struct bq27x00_device_info; struct bq27x00_access_methods { |
297a533b3 bq27x00: Cache ba... |
72 |
int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); |
b996ad0e9 power_supply: Sup... |
73 |
}; |
e20908d95 power_supply: bq2... |
74 |
enum bq27x00_chip { BQ27000, BQ27500 }; |
297a533b3 bq27x00: Cache ba... |
75 76 77 78 79 |
struct bq27x00_reg_cache { int temperature; int time_to_empty; int time_to_empty_avg; int time_to_full; |
631c17ee5 bq27x00: Add new ... |
80 |
int charge_full; |
73c244a8a bq27x00_battery: ... |
81 |
int cycle_count; |
297a533b3 bq27x00: Cache ba... |
82 |
int capacity; |
a8f6bd23c bq27x00_battery: ... |
83 |
int energy; |
297a533b3 bq27x00: Cache ba... |
84 |
int flags; |
297a533b3 bq27x00: Cache ba... |
85 |
}; |
b996ad0e9 power_supply: Sup... |
86 87 88 |
struct bq27x00_device_info { struct device *dev; int id; |
e20908d95 power_supply: bq2... |
89 |
enum bq27x00_chip chip; |
b996ad0e9 power_supply: Sup... |
90 |
|
297a533b3 bq27x00: Cache ba... |
91 |
struct bq27x00_reg_cache cache; |
631c17ee5 bq27x00: Add new ... |
92 |
int charge_design_full; |
297a533b3 bq27x00: Cache ba... |
93 |
unsigned long last_update; |
740b755a3 bq27x00: Poll bat... |
94 |
struct delayed_work work; |
297a533b3 bq27x00: Cache ba... |
95 |
|
a40402ef0 bq27x00: Prepare ... |
96 97 98 |
struct power_supply bat; struct bq27x00_access_methods bus; |
740b755a3 bq27x00: Poll bat... |
99 100 |
struct mutex lock; |
b996ad0e9 power_supply: Sup... |
101 102 103 |
}; static enum power_supply_property bq27x00_battery_props[] = { |
4e924a814 power_supply: bq2... |
104 |
POWER_SUPPLY_PROP_STATUS, |
b996ad0e9 power_supply: Sup... |
105 106 107 108 |
POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CAPACITY, |
d66bab3f3 bq27x00_battery: ... |
109 |
POWER_SUPPLY_PROP_CAPACITY_LEVEL, |
b996ad0e9 power_supply: Sup... |
110 |
POWER_SUPPLY_PROP_TEMP, |
4e924a814 power_supply: bq2... |
111 112 113 |
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, |
5661f334e bq27x00: Add type... |
114 |
POWER_SUPPLY_PROP_TECHNOLOGY, |
631c17ee5 bq27x00: Add new ... |
115 116 117 |
POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, |
73c244a8a bq27x00_battery: ... |
118 |
POWER_SUPPLY_PROP_CYCLE_COUNT, |
631c17ee5 bq27x00: Add new ... |
119 |
POWER_SUPPLY_PROP_ENERGY_NOW, |
b996ad0e9 power_supply: Sup... |
120 |
}; |
740b755a3 bq27x00: Poll bat... |
121 122 123 124 |
static unsigned int poll_interval = 360; module_param(poll_interval, uint, 0644); MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ "0 disables polling"); |
b996ad0e9 power_supply: Sup... |
125 126 127 |
/* * Common code for BQ27x00 devices */ |
a40402ef0 bq27x00: Prepare ... |
128 |
static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, |
297a533b3 bq27x00: Cache ba... |
129 |
bool single) |
b996ad0e9 power_supply: Sup... |
130 |
{ |
297a533b3 bq27x00: Cache ba... |
131 |
return di->bus.read(di, reg, single); |
b996ad0e9 power_supply: Sup... |
132 133 134 |
} /* |
297a533b3 bq27x00: Cache ba... |
135 |
* Return the battery Relative State-of-Charge |
b996ad0e9 power_supply: Sup... |
136 137 |
* Or < 0 if something fails. */ |
297a533b3 bq27x00: Cache ba... |
138 |
static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) |
b996ad0e9 power_supply: Sup... |
139 |
{ |
297a533b3 bq27x00: Cache ba... |
140 |
int rsoc; |
b996ad0e9 power_supply: Sup... |
141 |
|
e20908d95 power_supply: bq2... |
142 |
if (di->chip == BQ27500) |
297a533b3 bq27x00: Cache ba... |
143 |
rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); |
e20908d95 power_supply: bq2... |
144 |
else |
297a533b3 bq27x00: Cache ba... |
145 146 147 |
rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); if (rsoc < 0) |
c6cd4f267 bq27x00_battery: ... |
148 149 |
dev_dbg(di->dev, "error reading relative State-of-Charge "); |
297a533b3 bq27x00: Cache ba... |
150 151 |
return rsoc; |
b996ad0e9 power_supply: Sup... |
152 153 154 |
} /* |
631c17ee5 bq27x00: Add new ... |
155 156 157 158 159 160 161 162 163 |
* Return a battery charge value in µAh * Or < 0 if something fails. */ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) { int charge; charge = bq27x00_read(di, reg, false); if (charge < 0) { |
c6cd4f267 bq27x00_battery: ... |
164 165 166 |
dev_dbg(di->dev, "error reading charge register %02x: %d ", reg, charge); |
631c17ee5 bq27x00: Add new ... |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
return charge; } if (di->chip == BQ27500) charge *= 1000; else charge = charge * 3570 / BQ27000_RS; return charge; } /* * Return the battery Nominal available capaciy in µAh * Or < 0 if something fails. */ static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di) { return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC); } /* * Return the battery Last measured discharge in µAh * Or < 0 if something fails. */ static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di) { return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD); } /* * Return the battery Initial last measured discharge in µAh * Or < 0 if something fails. */ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) { int ilmd; if (di->chip == BQ27500) ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); else ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); if (ilmd < 0) { |
c6cd4f267 bq27x00_battery: ... |
210 211 |
dev_dbg(di->dev, "error reading initial last measured discharge "); |
631c17ee5 bq27x00: Add new ... |
212 213 214 215 216 217 218 219 220 221 222 223 |
return ilmd; } if (di->chip == BQ27500) ilmd *= 1000; else ilmd = ilmd * 256 * 3570 / BQ27000_RS; return ilmd; } /* |
a8f6bd23c bq27x00_battery: ... |
224 225 226 227 228 229 230 231 232 |
* Return the battery Available energy in µWh * Or < 0 if something fails. */ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di) { int ae; ae = bq27x00_read(di, BQ27x00_REG_AE, false); if (ae < 0) { |
c6cd4f267 bq27x00_battery: ... |
233 234 |
dev_dbg(di->dev, "error reading available energy "); |
a8f6bd23c bq27x00_battery: ... |
235 236 237 238 239 240 241 242 243 244 245 246 |
return ae; } if (di->chip == BQ27500) ae *= 1000; else ae = ae * 29200 / BQ27000_RS; return ae; } /* |
d149e98e0 bq27x00_battery: ... |
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
* Return the battery temperature in tenths of degree Celsius * Or < 0 if something fails. */ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di) { int temp; temp = bq27x00_read(di, BQ27x00_REG_TEMP, false); if (temp < 0) { dev_err(di->dev, "error reading temperature "); return temp; } if (di->chip == BQ27500) temp -= 2731; else temp = ((temp * 5) - 5463) / 2; return temp; } /* |
631c17ee5 bq27x00: Add new ... |
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
* Return the battery Cycle count total * Or < 0 if something fails. */ static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di) { int cyct; cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false); if (cyct < 0) dev_err(di->dev, "error reading cycle count total "); return cyct; } /* |
297a533b3 bq27x00: Cache ba... |
286 287 |
* Read a time register. * Return < 0 if something fails. |
b996ad0e9 power_supply: Sup... |
288 |
*/ |
297a533b3 bq27x00: Cache ba... |
289 |
static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg) |
b996ad0e9 power_supply: Sup... |
290 |
{ |
297a533b3 bq27x00: Cache ba... |
291 |
int tval; |
b996ad0e9 power_supply: Sup... |
292 |
|
297a533b3 bq27x00: Cache ba... |
293 294 |
tval = bq27x00_read(di, reg, false); if (tval < 0) { |
c6cd4f267 bq27x00_battery: ... |
295 296 297 |
dev_dbg(di->dev, "error reading time register %02x: %d ", reg, tval); |
297a533b3 bq27x00: Cache ba... |
298 |
return tval; |
b996ad0e9 power_supply: Sup... |
299 |
} |
297a533b3 bq27x00: Cache ba... |
300 301 302 303 304 305 306 307 308 309 310 311 312 |
if (tval == 65535) return -ENODATA; return tval * 60; } static void bq27x00_update(struct bq27x00_device_info *di) { struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); if (cache.flags >= 0) { |
4b226c2cf bq27x00_battery: ... |
313 |
if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { |
c6cd4f267 bq27x00_battery: ... |
314 315 |
dev_info(di->dev, "battery is not calibrated! ignoring capacity values "); |
4b226c2cf bq27x00_battery: ... |
316 |
cache.capacity = -ENODATA; |
a8f6bd23c bq27x00_battery: ... |
317 |
cache.energy = -ENODATA; |
4b226c2cf bq27x00_battery: ... |
318 319 320 321 322 323 |
cache.time_to_empty = -ENODATA; cache.time_to_empty_avg = -ENODATA; cache.time_to_full = -ENODATA; cache.charge_full = -ENODATA; } else { cache.capacity = bq27x00_battery_read_rsoc(di); |
a8f6bd23c bq27x00_battery: ... |
324 |
cache.energy = bq27x00_battery_read_energy(di); |
4b226c2cf bq27x00_battery: ... |
325 326 327 328 329 |
cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); cache.charge_full = bq27x00_battery_read_lmd(di); } |
d149e98e0 bq27x00_battery: ... |
330 |
cache.temperature = bq27x00_battery_read_temperature(di); |
73c244a8a bq27x00_battery: ... |
331 |
cache.cycle_count = bq27x00_battery_read_cyct(di); |
297a533b3 bq27x00: Cache ba... |
332 |
|
631c17ee5 bq27x00: Add new ... |
333 334 335 |
/* We only have to read charge design full once */ if (di->charge_design_full <= 0) di->charge_design_full = bq27x00_battery_read_ilmd(di); |
297a533b3 bq27x00: Cache ba... |
336 |
} |
b68f6216c bq27x00_battery: ... |
337 |
if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { |
297a533b3 bq27x00: Cache ba... |
338 339 340 341 342 343 |
di->cache = cache; power_supply_changed(&di->bat); } di->last_update = jiffies; } |
740b755a3 bq27x00: Poll bat... |
344 345 346 347 348 349 350 351 352 353 354 355 356 |
static void bq27x00_battery_poll(struct work_struct *work) { struct bq27x00_device_info *di = container_of(work, struct bq27x00_device_info, work.work); bq27x00_update(di); if (poll_interval > 0) { /* The timer does not have to be accurate. */ set_timer_slack(&di->work.timer, poll_interval * HZ / 4); schedule_delayed_work(&di->work, poll_interval * HZ); } } |
b996ad0e9 power_supply: Sup... |
357 |
/* |
bf7d41404 bq27x00: Minor cl... |
358 |
* Return the battery average current in µA |
b996ad0e9 power_supply: Sup... |
359 360 361 |
* Note that current can be negative signed as well * Or 0 if something fails. */ |
297a533b3 bq27x00: Cache ba... |
362 363 |
static int bq27x00_battery_current(struct bq27x00_device_info *di, union power_supply_propval *val) |
b996ad0e9 power_supply: Sup... |
364 |
{ |
297a533b3 bq27x00: Cache ba... |
365 |
int curr; |
b68f6216c bq27x00_battery: ... |
366 |
int flags; |
b996ad0e9 power_supply: Sup... |
367 |
|
b68f6216c bq27x00_battery: ... |
368 |
curr = bq27x00_read(di, BQ27x00_REG_AI, false); |
c6cd4f267 bq27x00_battery: ... |
369 370 371 |
if (curr < 0) { dev_err(di->dev, "error reading current "); |
297a533b3 bq27x00: Cache ba... |
372 |
return curr; |
c6cd4f267 bq27x00_battery: ... |
373 |
} |
e20908d95 power_supply: bq2... |
374 375 376 |
if (di->chip == BQ27500) { /* bq27500 returns signed value */ |
297a533b3 bq27x00: Cache ba... |
377 |
val->intval = (int)((s16)curr) * 1000; |
e20908d95 power_supply: bq2... |
378 |
} else { |
b68f6216c bq27x00_battery: ... |
379 380 |
flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false); if (flags & BQ27000_FLAG_CHGS) { |
e20908d95 power_supply: bq2... |
381 382 |
dev_dbg(di->dev, "negative current! "); |
afbc74fdc power_supply: bq2... |
383 |
curr = -curr; |
e20908d95 power_supply: bq2... |
384 |
} |
b996ad0e9 power_supply: Sup... |
385 |
|
297a533b3 bq27x00: Cache ba... |
386 |
val->intval = curr * 3570 / BQ27000_RS; |
b996ad0e9 power_supply: Sup... |
387 |
} |
297a533b3 bq27x00: Cache ba... |
388 |
return 0; |
b996ad0e9 power_supply: Sup... |
389 |
} |
4e924a814 power_supply: bq2... |
390 |
static int bq27x00_battery_status(struct bq27x00_device_info *di, |
297a533b3 bq27x00: Cache ba... |
391 |
union power_supply_propval *val) |
4e924a814 power_supply: bq2... |
392 |
{ |
4e924a814 power_supply: bq2... |
393 |
int status; |
4e924a814 power_supply: bq2... |
394 395 |
if (di->chip == BQ27500) { |
297a533b3 bq27x00: Cache ba... |
396 |
if (di->cache.flags & BQ27500_FLAG_FC) |
4e924a814 power_supply: bq2... |
397 |
status = POWER_SUPPLY_STATUS_FULL; |
270968c09 bq27x00_battery: ... |
398 |
else if (di->cache.flags & BQ27500_FLAG_DSG) |
4e924a814 power_supply: bq2... |
399 |
status = POWER_SUPPLY_STATUS_DISCHARGING; |
270968c09 bq27x00_battery: ... |
400 |
else if (di->cache.flags & BQ27500_FLAG_CHG) |
4e924a814 power_supply: bq2... |
401 |
status = POWER_SUPPLY_STATUS_CHARGING; |
270968c09 bq27x00_battery: ... |
402 403 404 405 |
else if (power_supply_am_i_supplied(&di->bat)) status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_UNKNOWN; |
4e924a814 power_supply: bq2... |
406 |
} else { |
c1b9ab67e bq27x00: Give mor... |
407 408 409 |
if (di->cache.flags & BQ27000_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27000_FLAG_CHGS) |
4e924a814 power_supply: bq2... |
410 |
status = POWER_SUPPLY_STATUS_CHARGING; |
c1b9ab67e bq27x00: Give mor... |
411 412 |
else if (power_supply_am_i_supplied(&di->bat)) status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
4e924a814 power_supply: bq2... |
413 414 415 416 417 |
else status = POWER_SUPPLY_STATUS_DISCHARGING; } val->intval = status; |
297a533b3 bq27x00: Cache ba... |
418 |
|
4e924a814 power_supply: bq2... |
419 420 |
return 0; } |
d66bab3f3 bq27x00_battery: ... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, union power_supply_propval *val) { int level; if (di->chip == BQ27500) { if (di->cache.flags & BQ27500_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (di->cache.flags & BQ27500_FLAG_SOC1) level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else if (di->cache.flags & BQ27500_FLAG_SOCF) level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; else level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } else { if (di->cache.flags & BQ27000_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; else if (di->cache.flags & BQ27000_FLAG_EDV1) level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; else if (di->cache.flags & BQ27000_FLAG_EDVF) level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; else level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } val->intval = level; return 0; } |
4e924a814 power_supply: bq2... |
450 |
/* |
297a533b3 bq27x00: Cache ba... |
451 452 |
* Return the battery Voltage in milivolts * Or < 0 if something fails. |
4e924a814 power_supply: bq2... |
453 |
*/ |
297a533b3 bq27x00: Cache ba... |
454 455 |
static int bq27x00_battery_voltage(struct bq27x00_device_info *di, union power_supply_propval *val) |
4e924a814 power_supply: bq2... |
456 |
{ |
297a533b3 bq27x00: Cache ba... |
457 |
int volt; |
4e924a814 power_supply: bq2... |
458 |
|
297a533b3 bq27x00: Cache ba... |
459 |
volt = bq27x00_read(di, BQ27x00_REG_VOLT, false); |
c6cd4f267 bq27x00_battery: ... |
460 461 462 |
if (volt < 0) { dev_err(di->dev, "error reading voltage "); |
297a533b3 bq27x00: Cache ba... |
463 |
return volt; |
c6cd4f267 bq27x00_battery: ... |
464 |
} |
4e924a814 power_supply: bq2... |
465 |
|
297a533b3 bq27x00: Cache ba... |
466 467 468 469 470 471 472 473 474 475 476 477 |
val->intval = volt * 1000; return 0; } static int bq27x00_simple_value(int value, union power_supply_propval *val) { if (value < 0) return value; val->intval = value; |
4e924a814 power_supply: bq2... |
478 |
|
4e924a814 power_supply: bq2... |
479 480 |
return 0; } |
b996ad0e9 power_supply: Sup... |
481 482 483 484 485 486 487 |
#define to_bq27x00_device_info(x) container_of((x), \ struct bq27x00_device_info, bat); static int bq27x00_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { |
4e924a814 power_supply: bq2... |
488 |
int ret = 0; |
b996ad0e9 power_supply: Sup... |
489 |
struct bq27x00_device_info *di = to_bq27x00_device_info(psy); |
3413b4ea2 bq27x00: Return -... |
490 |
|
740b755a3 bq27x00: Poll bat... |
491 492 493 494 495 496 |
mutex_lock(&di->lock); if (time_is_before_jiffies(di->last_update + 5 * HZ)) { cancel_delayed_work_sync(&di->work); bq27x00_battery_poll(&di->work.work); } mutex_unlock(&di->lock); |
297a533b3 bq27x00: Cache ba... |
497 498 |
if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) |
3413b4ea2 bq27x00: Return -... |
499 |
return -ENODEV; |
b996ad0e9 power_supply: Sup... |
500 501 |
switch (psp) { |
4e924a814 power_supply: bq2... |
502 503 504 |
case POWER_SUPPLY_PROP_STATUS: ret = bq27x00_battery_status(di, val); break; |
b996ad0e9 power_supply: Sup... |
505 |
case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
297a533b3 bq27x00: Cache ba... |
506 |
ret = bq27x00_battery_voltage(di, val); |
3413b4ea2 bq27x00: Return -... |
507 |
break; |
b996ad0e9 power_supply: Sup... |
508 |
case POWER_SUPPLY_PROP_PRESENT: |
297a533b3 bq27x00: Cache ba... |
509 |
val->intval = di->cache.flags < 0 ? 0 : 1; |
b996ad0e9 power_supply: Sup... |
510 511 |
break; case POWER_SUPPLY_PROP_CURRENT_NOW: |
297a533b3 bq27x00: Cache ba... |
512 |
ret = bq27x00_battery_current(di, val); |
b996ad0e9 power_supply: Sup... |
513 514 |
break; case POWER_SUPPLY_PROP_CAPACITY: |
297a533b3 bq27x00: Cache ba... |
515 |
ret = bq27x00_simple_value(di->cache.capacity, val); |
b996ad0e9 power_supply: Sup... |
516 |
break; |
d66bab3f3 bq27x00_battery: ... |
517 518 519 |
case POWER_SUPPLY_PROP_CAPACITY_LEVEL: ret = bq27x00_battery_capacity_level(di, val); break; |
b996ad0e9 power_supply: Sup... |
520 |
case POWER_SUPPLY_PROP_TEMP: |
d149e98e0 bq27x00_battery: ... |
521 |
ret = bq27x00_simple_value(di->cache.temperature, val); |
b996ad0e9 power_supply: Sup... |
522 |
break; |
4e924a814 power_supply: bq2... |
523 |
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: |
297a533b3 bq27x00: Cache ba... |
524 |
ret = bq27x00_simple_value(di->cache.time_to_empty, val); |
4e924a814 power_supply: bq2... |
525 526 |
break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: |
297a533b3 bq27x00: Cache ba... |
527 |
ret = bq27x00_simple_value(di->cache.time_to_empty_avg, val); |
4e924a814 power_supply: bq2... |
528 529 |
break; case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: |
297a533b3 bq27x00: Cache ba... |
530 |
ret = bq27x00_simple_value(di->cache.time_to_full, val); |
4e924a814 power_supply: bq2... |
531 |
break; |
5661f334e bq27x00: Add type... |
532 533 534 |
case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; |
631c17ee5 bq27x00: Add new ... |
535 536 537 538 539 540 541 542 543 |
case POWER_SUPPLY_PROP_CHARGE_NOW: ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val); break; case POWER_SUPPLY_PROP_CHARGE_FULL: ret = bq27x00_simple_value(di->cache.charge_full, val); break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: ret = bq27x00_simple_value(di->charge_design_full, val); break; |
73c244a8a bq27x00_battery: ... |
544 545 |
case POWER_SUPPLY_PROP_CYCLE_COUNT: ret = bq27x00_simple_value(di->cache.cycle_count, val); |
631c17ee5 bq27x00: Add new ... |
546 547 |
break; case POWER_SUPPLY_PROP_ENERGY_NOW: |
a8f6bd23c bq27x00_battery: ... |
548 |
ret = bq27x00_simple_value(di->cache.energy, val); |
631c17ee5 bq27x00: Add new ... |
549 |
break; |
b996ad0e9 power_supply: Sup... |
550 551 552 |
default: return -EINVAL; } |
4e924a814 power_supply: bq2... |
553 |
return ret; |
b996ad0e9 power_supply: Sup... |
554 |
} |
740b755a3 bq27x00: Poll bat... |
555 556 557 558 559 560 561 |
static void bq27x00_external_power_changed(struct power_supply *psy) { struct bq27x00_device_info *di = to_bq27x00_device_info(psy); cancel_delayed_work_sync(&di->work); schedule_delayed_work(&di->work, 0); } |
a40402ef0 bq27x00: Prepare ... |
562 |
static int bq27x00_powersupply_init(struct bq27x00_device_info *di) |
b996ad0e9 power_supply: Sup... |
563 |
{ |
a40402ef0 bq27x00: Prepare ... |
564 |
int ret; |
b996ad0e9 power_supply: Sup... |
565 566 567 568 |
di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.properties = bq27x00_battery_props; di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); di->bat.get_property = bq27x00_battery_get_property; |
740b755a3 bq27x00: Poll bat... |
569 570 571 572 |
di->bat.external_power_changed = bq27x00_external_power_changed; INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); mutex_init(&di->lock); |
a40402ef0 bq27x00: Prepare ... |
573 574 575 576 577 578 579 580 581 582 |
ret = power_supply_register(di->dev, &di->bat); if (ret) { dev_err(di->dev, "failed to register battery: %d ", ret); return ret; } dev_info(di->dev, "support ver. %s enabled ", DRIVER_VERSION); |
297a533b3 bq27x00: Cache ba... |
583 |
bq27x00_update(di); |
a40402ef0 bq27x00: Prepare ... |
584 |
return 0; |
b996ad0e9 power_supply: Sup... |
585 |
} |
740b755a3 bq27x00: Poll bat... |
586 587 |
static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) { |
8cfaaa811 bq27x00_battery: ... |
588 589 590 591 592 593 594 |
/* * power_supply_unregister call bq27x00_battery_get_property which * call bq27x00_battery_poll. * Make sure that bq27x00_battery_poll will not call * schedule_delayed_work again after unregister (which cause OOPS). */ poll_interval = 0; |
740b755a3 bq27x00: Poll bat... |
595 596 597 598 599 600 |
cancel_delayed_work_sync(&di->work); power_supply_unregister(&di->bat); mutex_destroy(&di->lock); } |
7fb7ba588 bq27x00: Add bq27... |
601 602 603 604 605 606 |
/* i2c specific code */ #ifdef CONFIG_BATTERY_BQ27X00_I2C /* If the system has several batteries we need a different name for each * of them... |
b996ad0e9 power_supply: Sup... |
607 |
*/ |
7fb7ba588 bq27x00: Add bq27... |
608 609 |
static DEFINE_IDR(battery_id); static DEFINE_MUTEX(battery_mutex); |
b996ad0e9 power_supply: Sup... |
610 |
|
297a533b3 bq27x00: Cache ba... |
611 |
static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) |
b996ad0e9 power_supply: Sup... |
612 |
{ |
a40402ef0 bq27x00: Prepare ... |
613 |
struct i2c_client *client = to_i2c_client(di->dev); |
9e912f452 bq27x00: Use sing... |
614 |
struct i2c_msg msg[2]; |
b996ad0e9 power_supply: Sup... |
615 |
unsigned char data[2]; |
297a533b3 bq27x00: Cache ba... |
616 |
int ret; |
b996ad0e9 power_supply: Sup... |
617 618 619 |
if (!client->adapter) return -ENODEV; |
9e912f452 bq27x00: Use sing... |
620 621 622 623 624 625 626 |
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 = data; |
2ec523a82 bq27x00: Cleanup ... |
627 |
if (single) |
9e912f452 bq27x00: Use sing... |
628 |
msg[1].len = 1; |
2ec523a82 bq27x00: Cleanup ... |
629 |
else |
9e912f452 bq27x00: Use sing... |
630 |
msg[1].len = 2; |
2ec523a82 bq27x00: Cleanup ... |
631 |
|
9e912f452 bq27x00: Use sing... |
632 |
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); |
2ec523a82 bq27x00: Cleanup ... |
633 634 635 636 637 638 639 |
if (ret < 0) return ret; if (!single) ret = get_unaligned_le16(data); else ret = data[0]; |
b996ad0e9 power_supply: Sup... |
640 |
|
297a533b3 bq27x00: Cache ba... |
641 |
return ret; |
b996ad0e9 power_supply: Sup... |
642 |
} |
e20908d95 power_supply: bq2... |
643 |
static int bq27x00_battery_probe(struct i2c_client *client, |
b996ad0e9 power_supply: Sup... |
644 645 646 647 |
const struct i2c_device_id *id) { char *name; struct bq27x00_device_info *di; |
b996ad0e9 power_supply: Sup... |
648 649 650 651 652 653 654 655 656 657 658 659 |
int num; int retval = 0; /* Get new ID for the new battery device */ retval = idr_pre_get(&battery_id, GFP_KERNEL); if (retval == 0) return -ENOMEM; mutex_lock(&battery_mutex); retval = idr_get_new(&battery_id, client, &num); mutex_unlock(&battery_mutex); if (retval < 0) return retval; |
e20908d95 power_supply: bq2... |
660 |
name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); |
b996ad0e9 power_supply: Sup... |
661 662 663 664 665 666 667 668 669 670 671 672 673 674 |
if (!name) { dev_err(&client->dev, "failed to allocate device name "); retval = -ENOMEM; goto batt_failed_1; } di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) { dev_err(&client->dev, "failed to allocate device info data "); retval = -ENOMEM; goto batt_failed_2; } |
a40402ef0 bq27x00: Prepare ... |
675 |
|
b996ad0e9 power_supply: Sup... |
676 |
di->id = num; |
a40402ef0 bq27x00: Prepare ... |
677 |
di->dev = &client->dev; |
e20908d95 power_supply: bq2... |
678 |
di->chip = id->driver_data; |
a40402ef0 bq27x00: Prepare ... |
679 680 |
di->bat.name = name; di->bus.read = &bq27x00_read_i2c; |
b996ad0e9 power_supply: Sup... |
681 |
|
a40402ef0 bq27x00: Prepare ... |
682 |
if (bq27x00_powersupply_init(di)) |
b996ad0e9 power_supply: Sup... |
683 |
goto batt_failed_3; |
b996ad0e9 power_supply: Sup... |
684 685 |
i2c_set_clientdata(client, di); |
b996ad0e9 power_supply: Sup... |
686 687 |
return 0; |
b996ad0e9 power_supply: Sup... |
688 689 690 691 692 693 694 695 696 697 698 |
batt_failed_3: kfree(di); batt_failed_2: kfree(name); batt_failed_1: mutex_lock(&battery_mutex); idr_remove(&battery_id, num); mutex_unlock(&battery_mutex); return retval; } |
e20908d95 power_supply: bq2... |
699 |
static int bq27x00_battery_remove(struct i2c_client *client) |
b996ad0e9 power_supply: Sup... |
700 701 |
{ struct bq27x00_device_info *di = i2c_get_clientdata(client); |
740b755a3 bq27x00: Poll bat... |
702 |
bq27x00_powersupply_unregister(di); |
b996ad0e9 power_supply: Sup... |
703 704 705 706 707 708 709 710 711 712 713 |
kfree(di->bat.name); mutex_lock(&battery_mutex); idr_remove(&battery_id, di->id); mutex_unlock(&battery_mutex); kfree(di); return 0; } |
e20908d95 power_supply: bq2... |
714 715 716 |
static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, |
b996ad0e9 power_supply: Sup... |
717 718 |
{}, }; |
fd9b958c5 bq27x00: Add MODU... |
719 |
MODULE_DEVICE_TABLE(i2c, bq27x00_id); |
b996ad0e9 power_supply: Sup... |
720 |
|
e20908d95 power_supply: bq2... |
721 |
static struct i2c_driver bq27x00_battery_driver = { |
b996ad0e9 power_supply: Sup... |
722 |
.driver = { |
e20908d95 power_supply: bq2... |
723 |
.name = "bq27x00-battery", |
b996ad0e9 power_supply: Sup... |
724 |
}, |
e20908d95 power_supply: bq2... |
725 726 727 |
.probe = bq27x00_battery_probe, .remove = bq27x00_battery_remove, .id_table = bq27x00_id, |
b996ad0e9 power_supply: Sup... |
728 |
}; |
7fb7ba588 bq27x00: Add bq27... |
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
static inline int bq27x00_battery_i2c_init(void) { int ret = i2c_add_driver(&bq27x00_battery_driver); if (ret) printk(KERN_ERR "Unable to register BQ27x00 i2c driver "); return ret; } static inline void bq27x00_battery_i2c_exit(void) { i2c_del_driver(&bq27x00_battery_driver); } #else static inline int bq27x00_battery_i2c_init(void) { return 0; } static inline void bq27x00_battery_i2c_exit(void) {}; #endif /* platform specific code */ #ifdef CONFIG_BATTERY_BQ27X00_PLATFORM static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, |
297a533b3 bq27x00: Cache ba... |
755 |
bool single) |
7fb7ba588 bq27x00: Add bq27... |
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
{ struct device *dev = di->dev; struct bq27000_platform_data *pdata = dev->platform_data; unsigned int timeout = 3; int upper, lower; int temp; if (!single) { /* Make sure the value has not changed in between reading the * lower and the upper part */ upper = pdata->read(dev, reg + 1); do { temp = upper; if (upper < 0) return upper; lower = pdata->read(dev, reg); if (lower < 0) return lower; upper = pdata->read(dev, reg + 1); } while (temp != upper && --timeout); if (timeout == 0) return -EIO; |
297a533b3 bq27x00: Cache ba... |
781 |
return (upper << 8) | lower; |
7fb7ba588 bq27x00: Add bq27... |
782 |
} |
297a533b3 bq27x00: Cache ba... |
783 784 |
return pdata->read(dev, reg); |
7fb7ba588 bq27x00: Add bq27... |
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 |
} static int __devinit bq27000_battery_probe(struct platform_device *pdev) { struct bq27x00_device_info *di; struct bq27000_platform_data *pdata = pdev->dev.platform_data; int ret; if (!pdata) { dev_err(&pdev->dev, "no platform_data supplied "); return -EINVAL; } if (!pdata->read) { dev_err(&pdev->dev, "no hdq read callback supplied "); return -EINVAL; } di = kzalloc(sizeof(*di), GFP_KERNEL); if (!di) { dev_err(&pdev->dev, "failed to allocate device info data "); return -ENOMEM; } platform_set_drvdata(pdev, di); di->dev = &pdev->dev; di->chip = BQ27000; di->bat.name = pdata->name ?: dev_name(&pdev->dev); di->bus.read = &bq27000_read_platform; ret = bq27x00_powersupply_init(di); if (ret) goto err_free; return 0; err_free: platform_set_drvdata(pdev, NULL); kfree(di); return ret; } static int __devexit bq27000_battery_remove(struct platform_device *pdev) { struct bq27x00_device_info *di = platform_get_drvdata(pdev); |
740b755a3 bq27x00: Poll bat... |
836 |
bq27x00_powersupply_unregister(di); |
7fb7ba588 bq27x00: Add bq27... |
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 |
platform_set_drvdata(pdev, NULL); kfree(di); return 0; } static struct platform_driver bq27000_battery_driver = { .probe = bq27000_battery_probe, .remove = __devexit_p(bq27000_battery_remove), .driver = { .name = "bq27000-battery", .owner = THIS_MODULE, }, }; static inline int bq27x00_battery_platform_init(void) { int ret = platform_driver_register(&bq27000_battery_driver); if (ret) printk(KERN_ERR "Unable to register BQ27000 platform driver "); return ret; } static inline void bq27x00_battery_platform_exit(void) { platform_driver_unregister(&bq27000_battery_driver); } #else static inline int bq27x00_battery_platform_init(void) { return 0; } static inline void bq27x00_battery_platform_exit(void) {}; #endif /* * Module stuff */ |
b996ad0e9 power_supply: Sup... |
877 878 879 |
static int __init bq27x00_battery_init(void) { int ret; |
7fb7ba588 bq27x00: Add bq27... |
880 881 882 883 884 |
ret = bq27x00_battery_i2c_init(); if (ret) return ret; ret = bq27x00_battery_platform_init(); |
b996ad0e9 power_supply: Sup... |
885 |
if (ret) |
7fb7ba588 bq27x00: Add bq27... |
886 |
bq27x00_battery_i2c_exit(); |
b996ad0e9 power_supply: Sup... |
887 888 889 890 891 892 893 |
return ret; } module_init(bq27x00_battery_init); static void __exit bq27x00_battery_exit(void) { |
7fb7ba588 bq27x00: Add bq27... |
894 895 |
bq27x00_battery_platform_exit(); bq27x00_battery_i2c_exit(); |
b996ad0e9 power_supply: Sup... |
896 897 898 899 900 901 |
} module_exit(bq27x00_battery_exit); MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); MODULE_DESCRIPTION("BQ27x00 battery monitor driver"); MODULE_LICENSE("GPL"); |