Blame view

drivers/power/olpc_battery.c 15.3 KB
fb972873a   David Woodhouse   [BATTERY] One Lap...
1
2
3
  /*
   * Battery driver for One Laptop Per Child board.
   *
690e85a39   David Woodhouse   olpc_battery: Fix...
4
   *	Copyright © 2006-2010  David Woodhouse <dwmw2@infradead.org>
fb972873a   David Woodhouse   [BATTERY] One Lap...
5
6
7
8
9
   *
   * This program 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.
   */
04a820ead   Andres Salomon   olpc_battery: Fix...
10
  #include <linux/kernel.h>
fb972873a   David Woodhouse   [BATTERY] One Lap...
11
  #include <linux/module.h>
144bbeaed   Andres Salomon   olpc_battery: Add...
12
  #include <linux/types.h>
fb972873a   David Woodhouse   [BATTERY] One Lap...
13
  #include <linux/err.h>
144bbeaed   Andres Salomon   olpc_battery: Add...
14
  #include <linux/device.h>
fb972873a   David Woodhouse   [BATTERY] One Lap...
15
16
17
18
19
20
21
22
23
  #include <linux/platform_device.h>
  #include <linux/power_supply.h>
  #include <linux/jiffies.h>
  #include <linux/sched.h>
  #include <asm/olpc.h>
  
  
  #define EC_BAT_VOLTAGE	0x10	/* uint16_t,	*9.76/32,    mV   */
  #define EC_BAT_CURRENT	0x11	/* int16_t,	*15.625/120, mA   */
75d880796   Andres Salomon   power_supply: fix...
24
  #define EC_BAT_ACR	0x12	/* int16_t,	*6250/15,    µAh  */
fb972873a   David Woodhouse   [BATTERY] One Lap...
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  #define EC_BAT_TEMP	0x13	/* uint16_t,	*100/256,   °C  */
  #define EC_AMB_TEMP	0x14	/* uint16_t,	*100/256,   °C  */
  #define EC_BAT_STATUS	0x15	/* uint8_t,	bitmask */
  #define EC_BAT_SOC	0x16	/* uint8_t,	percentage */
  #define EC_BAT_SERIAL	0x17	/* uint8_t[6] */
  #define EC_BAT_EEPROM	0x18	/* uint8_t adr as input, uint8_t output */
  #define EC_BAT_ERRCODE	0x1f	/* uint8_t,	bitmask */
  
  #define BAT_STAT_PRESENT	0x01
  #define BAT_STAT_FULL		0x02
  #define BAT_STAT_LOW		0x04
  #define BAT_STAT_DESTROY	0x08
  #define BAT_STAT_AC		0x10
  #define BAT_STAT_CHARGING	0x20
  #define BAT_STAT_DISCHARGING	0x40
8f7e57985   Andres Salomon   olpc_battery: Ens...
40
  #define BAT_STAT_TRICKLE	0x80
fb972873a   David Woodhouse   [BATTERY] One Lap...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  #define BAT_ERR_INFOFAIL	0x02
  #define BAT_ERR_OVERVOLTAGE	0x04
  #define BAT_ERR_OVERTEMP	0x05
  #define BAT_ERR_GAUGESTOP	0x06
  #define BAT_ERR_OUT_OF_CONTROL	0x07
  #define BAT_ERR_ID_FAIL		0x09
  #define BAT_ERR_ACR_FAIL	0x10
  
  #define BAT_ADDR_MFR_TYPE	0x5F
  
  /*********************************************************************
   *		Power
   *********************************************************************/
  
  static int olpc_ac_get_prop(struct power_supply *psy,
  			    enum power_supply_property psp,
  			    union power_supply_propval *val)
  {
  	int ret = 0;
  	uint8_t status;
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_ONLINE:
  		ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
  		if (ret)
  			return ret;
  
  		val->intval = !!(status & BAT_STAT_AC);
  		break;
  	default:
  		ret = -EINVAL;
  		break;
  	}
  	return ret;
  }
  
  static enum power_supply_property olpc_ac_props[] = {
  	POWER_SUPPLY_PROP_ONLINE,
  };
  
  static struct power_supply olpc_ac = {
  	.name = "olpc-ac",
  	.type = POWER_SUPPLY_TYPE_MAINS,
  	.properties = olpc_ac_props,
  	.num_properties = ARRAY_SIZE(olpc_ac_props),
  	.get_property = olpc_ac_get_prop,
  };
1ca5b9d21   David Woodhouse   power_supply: Sup...
89
  static char bat_serial[17]; /* Ick */
b2bd8a3bc   Andres Salomon   power_supply: cle...
90
91
92
  static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte)
  {
  	if (olpc_platform_info.ecver > 0x44) {
8f7e57985   Andres Salomon   olpc_battery: Ens...
93
  		if (ec_byte & (BAT_STAT_CHARGING | BAT_STAT_TRICKLE))
b2bd8a3bc   Andres Salomon   power_supply: cle...
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
  			val->intval = POWER_SUPPLY_STATUS_CHARGING;
  		else if (ec_byte & BAT_STAT_DISCHARGING)
  			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
  		else if (ec_byte & BAT_STAT_FULL)
  			val->intval = POWER_SUPPLY_STATUS_FULL;
  		else /* er,... */
  			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
  	} else {
  		/* Older EC didn't report charge/discharge bits */
  		if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */
  			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
  		else if (ec_byte & BAT_STAT_FULL)
  			val->intval = POWER_SUPPLY_STATUS_FULL;
  		else /* Not _necessarily_ true but EC doesn't tell all yet */
  			val->intval = POWER_SUPPLY_STATUS_CHARGING;
  	}
  
  	return 0;
  }
  
  static int olpc_bat_get_health(union power_supply_propval *val)
  {
  	uint8_t ec_byte;
  	int ret;
  
  	ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
  	if (ret)
  		return ret;
  
  	switch (ec_byte) {
  	case 0:
  		val->intval = POWER_SUPPLY_HEALTH_GOOD;
  		break;
  
  	case BAT_ERR_OVERTEMP:
  		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
  		break;
  
  	case BAT_ERR_OVERVOLTAGE:
  		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
  		break;
  
  	case BAT_ERR_INFOFAIL:
  	case BAT_ERR_OUT_OF_CONTROL:
  	case BAT_ERR_ID_FAIL:
  	case BAT_ERR_ACR_FAIL:
  		val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
  		break;
  
  	default:
  		/* Eep. We don't know this failure code */
  		ret = -EIO;
  	}
  
  	return ret;
  }
  
  static int olpc_bat_get_mfr(union power_supply_propval *val)
  {
  	uint8_t ec_byte;
  	int ret;
  
  	ec_byte = BAT_ADDR_MFR_TYPE;
  	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
  	if (ret)
  		return ret;
  
  	switch (ec_byte >> 4) {
  	case 1:
  		val->strval = "Gold Peak";
  		break;
  	case 2:
  		val->strval = "BYD";
  		break;
  	default:
  		val->strval = "Unknown";
  		break;
  	}
  
  	return ret;
  }
  
  static int olpc_bat_get_tech(union power_supply_propval *val)
  {
  	uint8_t ec_byte;
  	int ret;
  
  	ec_byte = BAT_ADDR_MFR_TYPE;
  	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
  	if (ret)
  		return ret;
  
  	switch (ec_byte & 0xf) {
  	case 1:
  		val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
  		break;
  	case 2:
  		val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
  		break;
  	default:
  		val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
  		break;
  	}
  
  	return ret;
  }
b202a5e6c   Sascha Silbe   olpc_battery: Add...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  static int olpc_bat_get_charge_full_design(union power_supply_propval *val)
  {
  	uint8_t ec_byte;
  	union power_supply_propval tech;
  	int ret, mfr;
  
  	ret = olpc_bat_get_tech(&tech);
  	if (ret)
  		return ret;
  
  	ec_byte = BAT_ADDR_MFR_TYPE;
  	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
  	if (ret)
  		return ret;
  
  	mfr = ec_byte >> 4;
  
  	switch (tech.intval) {
  	case POWER_SUPPLY_TECHNOLOGY_NiMH:
  		switch (mfr) {
  		case 1: /* Gold Peak */
  			val->intval = 3000000*.8;
  			break;
  		default:
  			return -EIO;
  		}
  		break;
  
  	case POWER_SUPPLY_TECHNOLOGY_LiFe:
  		switch (mfr) {
  		case 1: /* Gold Peak */
  			val->intval = 2800000;
  			break;
  		case 2: /* BYD */
  			val->intval = 3100000;
  			break;
  		default:
  			return -EIO;
  		}
  		break;
  
  	default:
  		return -EIO;
  	}
  
  	return ret;
  }
20fd9830c   Sascha Silbe   olpc_battery: Add...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  static int olpc_bat_get_charge_now(union power_supply_propval *val)
  {
  	uint8_t soc;
  	union power_supply_propval full;
  	int ret;
  
  	ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &soc, 1);
  	if (ret)
  		return ret;
  
  	ret = olpc_bat_get_charge_full_design(&full);
  	if (ret)
  		return ret;
  
  	val->intval = soc * (full.intval / 100);
  	return 0;
  }
fb972873a   David Woodhouse   [BATTERY] One Lap...
264
265
266
267
268
269
270
271
  /*********************************************************************
   *		Battery properties
   *********************************************************************/
  static int olpc_bat_get_property(struct power_supply *psy,
  				 enum power_supply_property psp,
  				 union power_supply_propval *val)
  {
  	int ret = 0;
8e9c7716c   Harvey Harrison   olpc: olpc_batter...
272
  	__be16 ec_word;
fb972873a   David Woodhouse   [BATTERY] One Lap...
273
  	uint8_t ec_byte;
8e9c7716c   Harvey Harrison   olpc: olpc_batter...
274
  	__be64 ser_buf;
fb972873a   David Woodhouse   [BATTERY] One Lap...
275
276
277
278
279
280
281
282
283
284
285
  
  	ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1);
  	if (ret)
  		return ret;
  
  	/* Theoretically there's a race here -- the battery could be
  	   removed immediately after we check whether it's present, and
  	   then we query for some other property of the now-absent battery.
  	   It doesn't matter though -- the EC will return the last-known
  	   information, and it's as if we just ran that _little_ bit faster
  	   and managed to read it out before the battery went away. */
8f7e57985   Andres Salomon   olpc_battery: Ens...
286
287
  	if (!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)) &&
  			psp != POWER_SUPPLY_PROP_PRESENT)
fb972873a   David Woodhouse   [BATTERY] One Lap...
288
289
290
291
  		return -ENODEV;
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_STATUS:
b2bd8a3bc   Andres Salomon   power_supply: cle...
292
293
294
295
  		ret = olpc_bat_get_status(val, ec_byte);
  		if (ret)
  			return ret;
  		break;
ee8076ed3   Andres Salomon   power_supply: Add...
296
297
298
299
300
301
302
303
  	case POWER_SUPPLY_PROP_CHARGE_TYPE:
  		if (ec_byte & BAT_STAT_TRICKLE)
  			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
  		else if (ec_byte & BAT_STAT_CHARGING)
  			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
  		else
  			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
  		break;
fb972873a   David Woodhouse   [BATTERY] One Lap...
304
  	case POWER_SUPPLY_PROP_PRESENT:
8f7e57985   Andres Salomon   olpc_battery: Ens...
305
306
  		val->intval = !!(ec_byte & (BAT_STAT_PRESENT |
  					    BAT_STAT_TRICKLE));
fb972873a   David Woodhouse   [BATTERY] One Lap...
307
308
309
310
311
312
  		break;
  
  	case POWER_SUPPLY_PROP_HEALTH:
  		if (ec_byte & BAT_STAT_DESTROY)
  			val->intval = POWER_SUPPLY_HEALTH_DEAD;
  		else {
b2bd8a3bc   Andres Salomon   power_supply: cle...
313
  			ret = olpc_bat_get_health(val);
fb972873a   David Woodhouse   [BATTERY] One Lap...
314
315
  			if (ret)
  				return ret;
fb972873a   David Woodhouse   [BATTERY] One Lap...
316
317
318
319
  		}
  		break;
  
  	case POWER_SUPPLY_PROP_MANUFACTURER:
b2bd8a3bc   Andres Salomon   power_supply: cle...
320
  		ret = olpc_bat_get_mfr(val);
fb972873a   David Woodhouse   [BATTERY] One Lap...
321
322
  		if (ret)
  			return ret;
fb972873a   David Woodhouse   [BATTERY] One Lap...
323
324
  		break;
  	case POWER_SUPPLY_PROP_TECHNOLOGY:
b2bd8a3bc   Andres Salomon   power_supply: cle...
325
  		ret = olpc_bat_get_tech(val);
fb972873a   David Woodhouse   [BATTERY] One Lap...
326
327
  		if (ret)
  			return ret;
fb972873a   David Woodhouse   [BATTERY] One Lap...
328
329
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
22fadd766   Sascha Silbe   olpc_battery: Add...
330
  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
fb972873a   David Woodhouse   [BATTERY] One Lap...
331
332
333
  		ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2);
  		if (ret)
  			return ret;
7cfbb2946   Richard A. Smith   olpc_battery: Fix...
334
  		val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32;
fb972873a   David Woodhouse   [BATTERY] One Lap...
335
336
  		break;
  	case POWER_SUPPLY_PROP_CURRENT_AVG:
22fadd766   Sascha Silbe   olpc_battery: Add...
337
  	case POWER_SUPPLY_PROP_CURRENT_NOW:
fb972873a   David Woodhouse   [BATTERY] One Lap...
338
339
340
  		ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
  		if (ret)
  			return ret;
7cfbb2946   Richard A. Smith   olpc_battery: Fix...
341
  		val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120;
fb972873a   David Woodhouse   [BATTERY] One Lap...
342
343
344
345
346
347
348
  		break;
  	case POWER_SUPPLY_PROP_CAPACITY:
  		ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
  		if (ret)
  			return ret;
  		val->intval = ec_byte;
  		break;
b294a290d   Andres Salomon   Revert "power: re...
349
350
351
352
353
354
355
356
  	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
  		if (ec_byte & BAT_STAT_FULL)
  			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
  		else if (ec_byte & BAT_STAT_LOW)
  			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
  		else
  			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
  		break;
b202a5e6c   Sascha Silbe   olpc_battery: Add...
357
358
359
360
361
  	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
  		ret = olpc_bat_get_charge_full_design(val);
  		if (ret)
  			return ret;
  		break;
20fd9830c   Sascha Silbe   olpc_battery: Add...
362
363
364
365
366
  	case POWER_SUPPLY_PROP_CHARGE_NOW:
  		ret = olpc_bat_get_charge_now(val);
  		if (ret)
  			return ret;
  		break;
fb972873a   David Woodhouse   [BATTERY] One Lap...
367
368
369
370
  	case POWER_SUPPLY_PROP_TEMP:
  		ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
  		if (ret)
  			return ret;
8e9c7716c   Harvey Harrison   olpc: olpc_batter...
371

7cfbb2946   Richard A. Smith   olpc_battery: Fix...
372
  		val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256;
fb972873a   David Woodhouse   [BATTERY] One Lap...
373
374
375
376
377
  		break;
  	case POWER_SUPPLY_PROP_TEMP_AMBIENT:
  		ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
  		if (ret)
  			return ret;
8e9c7716c   Harvey Harrison   olpc: olpc_batter...
378
  		val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
fb972873a   David Woodhouse   [BATTERY] One Lap...
379
  		break;
8e552c36d   Andres Salomon   power_supply: add...
380
381
382
383
  	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
  		ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2);
  		if (ret)
  			return ret;
7cfbb2946   Richard A. Smith   olpc_battery: Fix...
384
  		val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15;
8e552c36d   Andres Salomon   power_supply: add...
385
  		break;
1ca5b9d21   David Woodhouse   power_supply: Sup...
386
387
388
389
390
391
392
393
  	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
  		ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
  		if (ret)
  			return ret;
  
  		sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
  		val->strval = bat_serial;
  		break;
fb972873a   David Woodhouse   [BATTERY] One Lap...
394
395
396
397
398
399
400
  	default:
  		ret = -EINVAL;
  		break;
  	}
  
  	return ret;
  }
c566d299f   Daniel Drake   olpc_battery: Amb...
401
  static enum power_supply_property olpc_xo1_bat_props[] = {
fb972873a   David Woodhouse   [BATTERY] One Lap...
402
  	POWER_SUPPLY_PROP_STATUS,
ee8076ed3   Andres Salomon   power_supply: Add...
403
  	POWER_SUPPLY_PROP_CHARGE_TYPE,
fb972873a   David Woodhouse   [BATTERY] One Lap...
404
405
406
407
  	POWER_SUPPLY_PROP_PRESENT,
  	POWER_SUPPLY_PROP_HEALTH,
  	POWER_SUPPLY_PROP_TECHNOLOGY,
  	POWER_SUPPLY_PROP_VOLTAGE_AVG,
22fadd766   Sascha Silbe   olpc_battery: Add...
408
  	POWER_SUPPLY_PROP_VOLTAGE_NOW,
fb972873a   David Woodhouse   [BATTERY] One Lap...
409
  	POWER_SUPPLY_PROP_CURRENT_AVG,
22fadd766   Sascha Silbe   olpc_battery: Add...
410
  	POWER_SUPPLY_PROP_CURRENT_NOW,
fb972873a   David Woodhouse   [BATTERY] One Lap...
411
  	POWER_SUPPLY_PROP_CAPACITY,
b294a290d   Andres Salomon   Revert "power: re...
412
  	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
b202a5e6c   Sascha Silbe   olpc_battery: Add...
413
  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
20fd9830c   Sascha Silbe   olpc_battery: Add...
414
  	POWER_SUPPLY_PROP_CHARGE_NOW,
fb972873a   David Woodhouse   [BATTERY] One Lap...
415
416
417
  	POWER_SUPPLY_PROP_TEMP,
  	POWER_SUPPLY_PROP_TEMP_AMBIENT,
  	POWER_SUPPLY_PROP_MANUFACTURER,
1ca5b9d21   David Woodhouse   power_supply: Sup...
418
  	POWER_SUPPLY_PROP_SERIAL_NUMBER,
8e552c36d   Andres Salomon   power_supply: add...
419
  	POWER_SUPPLY_PROP_CHARGE_COUNTER,
fb972873a   David Woodhouse   [BATTERY] One Lap...
420
  };
c566d299f   Daniel Drake   olpc_battery: Amb...
421
422
423
424
425
426
427
428
  /* XO-1.5 does not have ambient temperature property */
  static enum power_supply_property olpc_xo15_bat_props[] = {
  	POWER_SUPPLY_PROP_STATUS,
  	POWER_SUPPLY_PROP_CHARGE_TYPE,
  	POWER_SUPPLY_PROP_PRESENT,
  	POWER_SUPPLY_PROP_HEALTH,
  	POWER_SUPPLY_PROP_TECHNOLOGY,
  	POWER_SUPPLY_PROP_VOLTAGE_AVG,
bf542a4e7   Sascha Silbe   olpc_battery: Fix...
429
  	POWER_SUPPLY_PROP_VOLTAGE_NOW,
c566d299f   Daniel Drake   olpc_battery: Amb...
430
  	POWER_SUPPLY_PROP_CURRENT_AVG,
bf542a4e7   Sascha Silbe   olpc_battery: Fix...
431
  	POWER_SUPPLY_PROP_CURRENT_NOW,
c566d299f   Daniel Drake   olpc_battery: Amb...
432
433
  	POWER_SUPPLY_PROP_CAPACITY,
  	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
bf542a4e7   Sascha Silbe   olpc_battery: Fix...
434
435
  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
  	POWER_SUPPLY_PROP_CHARGE_NOW,
c566d299f   Daniel Drake   olpc_battery: Amb...
436
437
438
439
440
  	POWER_SUPPLY_PROP_TEMP,
  	POWER_SUPPLY_PROP_MANUFACTURER,
  	POWER_SUPPLY_PROP_SERIAL_NUMBER,
  	POWER_SUPPLY_PROP_CHARGE_COUNTER,
  };
d7eb9e36c   Andres Salomon   power_supply: add...
441
442
443
444
445
  /* EEPROM reading goes completely around the power_supply API, sadly */
  
  #define EEPROM_START	0x20
  #define EEPROM_END	0x80
  #define EEPROM_SIZE	(EEPROM_END - EEPROM_START)
2c3c8bea6   Chris Wright   sysfs: add struct...
446
  static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
d7eb9e36c   Andres Salomon   power_supply: add...
447
448
449
  		struct bin_attribute *attr, char *buf, loff_t off, size_t count)
  {
  	uint8_t ec_byte;
04a820ead   Andres Salomon   olpc_battery: Fix...
450
451
  	int ret;
  	int i;
d7eb9e36c   Andres Salomon   power_supply: add...
452
453
454
455
456
  
  	if (off >= EEPROM_SIZE)
  		return 0;
  	if (off + count > EEPROM_SIZE)
  		count = EEPROM_SIZE - off;
04a820ead   Andres Salomon   olpc_battery: Fix...
457
458
459
  	for (i = 0; i < count; i++) {
  		ec_byte = EEPROM_START + off + i;
  		ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
d7eb9e36c   Andres Salomon   power_supply: add...
460
  		if (ret) {
04a820ead   Andres Salomon   olpc_battery: Fix...
461
462
463
464
  			pr_err("olpc-battery: "
  			       "EC_BAT_EEPROM cmd @ 0x%x failed - %d!
  ",
  			       ec_byte, ret);
d7eb9e36c   Andres Salomon   power_supply: add...
465
466
467
468
469
470
471
472
473
474
475
  			return -EIO;
  		}
  	}
  
  	return count;
  }
  
  static struct bin_attribute olpc_bat_eeprom = {
  	.attr = {
  		.name = "eeprom",
  		.mode = S_IRUGO,
d7eb9e36c   Andres Salomon   power_supply: add...
476
477
478
479
  	},
  	.size = 0,
  	.read = olpc_bat_eeprom_read,
  };
144bbeaed   Andres Salomon   olpc_battery: Add...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  /* Allow userspace to see the specific error value pulled from the EC */
  
  static ssize_t olpc_bat_error_read(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	uint8_t ec_byte;
  	ssize_t ret;
  
  	ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
  	if (ret < 0)
  		return ret;
  
  	return sprintf(buf, "%d
  ", ec_byte);
  }
  
  static struct device_attribute olpc_bat_error = {
  	.attr = {
  		.name = "error",
  		.mode = S_IRUGO,
  	},
  	.show = olpc_bat_error_read,
  };
fb972873a   David Woodhouse   [BATTERY] One Lap...
503
504
505
  /*********************************************************************
   *		Initialisation
   *********************************************************************/
fb972873a   David Woodhouse   [BATTERY] One Lap...
506
  static struct power_supply olpc_bat = {
c3503fd02   Daniel Drake   olpc_battery: Bin...
507
  	.name = "olpc-battery",
fb972873a   David Woodhouse   [BATTERY] One Lap...
508
509
510
  	.get_property = olpc_bat_get_property,
  	.use_for_apm = 1,
  };
cae659af8   Daniel Drake   olpc_battery: Add...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  static int olpc_battery_suspend(struct platform_device *pdev,
  				pm_message_t state)
  {
  	if (device_may_wakeup(olpc_ac.dev))
  		olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR);
  	else
  		olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR);
  
  	if (device_may_wakeup(olpc_bat.dev))
  		olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
  				   | EC_SCI_SRC_BATERR);
  	else
  		olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
  				     | EC_SCI_SRC_BATERR);
  
  	return 0;
  }
c3503fd02   Daniel Drake   olpc_battery: Bin...
528
  static int __devinit olpc_battery_probe(struct platform_device *pdev)
fb972873a   David Woodhouse   [BATTERY] One Lap...
529
  {
c3503fd02   Daniel Drake   olpc_battery: Bin...
530
  	int ret;
fb972873a   David Woodhouse   [BATTERY] One Lap...
531
  	uint8_t status;
484d6d50c   Andres Salomon   power_supply: bum...
532
533
534
535
536
537
538
539
  	/*
  	 * We've seen a number of EC protocol changes; this driver requires
  	 * the latest EC protocol, supported by 0x44 and above.
  	 */
  	if (olpc_platform_info.ecver < 0x44) {
  		printk(KERN_NOTICE "OLPC EC version 0x%02x too old for "
  			"battery driver.
  ", olpc_platform_info.ecver);
fb972873a   David Woodhouse   [BATTERY] One Lap...
540
541
542
543
544
545
546
547
  		return -ENXIO;
  	}
  
  	ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
  	if (ret)
  		return ret;
  
  	/* Ignore the status. It doesn't actually matter */
c3503fd02   Daniel Drake   olpc_battery: Bin...
548
  	ret = power_supply_register(&pdev->dev, &olpc_ac);
fb972873a   David Woodhouse   [BATTERY] One Lap...
549
  	if (ret)
c3503fd02   Daniel Drake   olpc_battery: Bin...
550
  		return ret;
fb972873a   David Woodhouse   [BATTERY] One Lap...
551

c566d299f   Daniel Drake   olpc_battery: Amb...
552
553
554
555
556
557
558
  	if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */
  		olpc_bat.properties = olpc_xo15_bat_props;
  		olpc_bat.num_properties = ARRAY_SIZE(olpc_xo15_bat_props);
  	} else { /* XO-1 */
  		olpc_bat.properties = olpc_xo1_bat_props;
  		olpc_bat.num_properties = ARRAY_SIZE(olpc_xo1_bat_props);
  	}
fb972873a   David Woodhouse   [BATTERY] One Lap...
559

c3503fd02   Daniel Drake   olpc_battery: Bin...
560
  	ret = power_supply_register(&pdev->dev, &olpc_bat);
fb972873a   David Woodhouse   [BATTERY] One Lap...
561
562
  	if (ret)
  		goto battery_failed;
d7eb9e36c   Andres Salomon   power_supply: add...
563
564
565
  	ret = device_create_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
  	if (ret)
  		goto eeprom_failed;
144bbeaed   Andres Salomon   olpc_battery: Add...
566
567
568
  	ret = device_create_file(olpc_bat.dev, &olpc_bat_error);
  	if (ret)
  		goto error_failed;
cae659af8   Daniel Drake   olpc_battery: Add...
569
570
571
572
  	if (olpc_ec_wakeup_available()) {
  		device_set_wakeup_capable(olpc_ac.dev, true);
  		device_set_wakeup_capable(olpc_bat.dev, true);
  	}
c3503fd02   Daniel Drake   olpc_battery: Bin...
573
  	return 0;
fb972873a   David Woodhouse   [BATTERY] One Lap...
574

144bbeaed   Andres Salomon   olpc_battery: Add...
575
576
  error_failed:
  	device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
d7eb9e36c   Andres Salomon   power_supply: add...
577
578
  eeprom_failed:
  	power_supply_unregister(&olpc_bat);
fb972873a   David Woodhouse   [BATTERY] One Lap...
579
580
  battery_failed:
  	power_supply_unregister(&olpc_ac);
fb972873a   David Woodhouse   [BATTERY] One Lap...
581
582
  	return ret;
  }
c3503fd02   Daniel Drake   olpc_battery: Bin...
583
  static int __devexit olpc_battery_remove(struct platform_device *pdev)
fb972873a   David Woodhouse   [BATTERY] One Lap...
584
  {
144bbeaed   Andres Salomon   olpc_battery: Add...
585
  	device_remove_file(olpc_bat.dev, &olpc_bat_error);
d7eb9e36c   Andres Salomon   power_supply: add...
586
  	device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
fb972873a   David Woodhouse   [BATTERY] One Lap...
587
588
  	power_supply_unregister(&olpc_bat);
  	power_supply_unregister(&olpc_ac);
c3503fd02   Daniel Drake   olpc_battery: Bin...
589
  	return 0;
fb972873a   David Woodhouse   [BATTERY] One Lap...
590
  }
c3503fd02   Daniel Drake   olpc_battery: Bin...
591
592
593
594
595
  static const struct of_device_id olpc_battery_ids[] __devinitconst = {
  	{ .compatible = "olpc,xo1-battery" },
  	{}
  };
  MODULE_DEVICE_TABLE(of, olpc_battery_ids);
5519d00e6   Anton Vorontsov   olpc_battery: Fix...
596
  static struct platform_driver olpc_battery_driver = {
c3503fd02   Daniel Drake   olpc_battery: Bin...
597
598
599
600
601
602
603
  	.driver = {
  		.name = "olpc-battery",
  		.owner = THIS_MODULE,
  		.of_match_table = olpc_battery_ids,
  	},
  	.probe = olpc_battery_probe,
  	.remove = __devexit_p(olpc_battery_remove),
cae659af8   Daniel Drake   olpc_battery: Add...
604
  	.suspend = olpc_battery_suspend,
c3503fd02   Daniel Drake   olpc_battery: Bin...
605
  };
300bac7fb   Axel Lin   power_supply: Con...
606
  module_platform_driver(olpc_battery_driver);
fb972873a   David Woodhouse   [BATTERY] One Lap...
607
608
609
610
  
  MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");