cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

olpc_battery.c (17772B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Battery driver for One Laptop Per Child board.
      4 *
      5 *	Copyright © 2006-2010  David Woodhouse <dwmw2@infradead.org>
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/module.h>
     10#include <linux/mod_devicetable.h>
     11#include <linux/types.h>
     12#include <linux/err.h>
     13#include <linux/device.h>
     14#include <linux/of.h>
     15#include <linux/platform_device.h>
     16#include <linux/power_supply.h>
     17#include <linux/jiffies.h>
     18#include <linux/sched.h>
     19#include <linux/olpc-ec.h>
     20
     21
     22#define EC_BAT_VOLTAGE	0x10	/* uint16_t,	*9.76/32,    mV   */
     23#define EC_BAT_CURRENT	0x11	/* int16_t,	*15.625/120, mA   */
     24#define EC_BAT_ACR	0x12	/* int16_t,	*6250/15,    µAh  */
     25#define EC_BAT_TEMP	0x13	/* uint16_t,	*100/256,   °C  */
     26#define EC_AMB_TEMP	0x14	/* uint16_t,	*100/256,   °C  */
     27#define EC_BAT_STATUS	0x15	/* uint8_t,	bitmask */
     28#define EC_BAT_SOC	0x16	/* uint8_t,	percentage */
     29#define EC_BAT_SERIAL	0x17	/* uint8_t[6] */
     30#define EC_BAT_EEPROM	0x18	/* uint8_t adr as input, uint8_t output */
     31#define EC_BAT_ERRCODE	0x1f	/* uint8_t,	bitmask */
     32
     33#define BAT_STAT_PRESENT	0x01
     34#define BAT_STAT_FULL		0x02
     35#define BAT_STAT_LOW		0x04
     36#define BAT_STAT_DESTROY	0x08
     37#define BAT_STAT_AC		0x10
     38#define BAT_STAT_CHARGING	0x20
     39#define BAT_STAT_DISCHARGING	0x40
     40#define BAT_STAT_TRICKLE	0x80
     41
     42#define BAT_ERR_INFOFAIL	0x02
     43#define BAT_ERR_OVERVOLTAGE	0x04
     44#define BAT_ERR_OVERTEMP	0x05
     45#define BAT_ERR_GAUGESTOP	0x06
     46#define BAT_ERR_OUT_OF_CONTROL	0x07
     47#define BAT_ERR_ID_FAIL		0x09
     48#define BAT_ERR_ACR_FAIL	0x10
     49
     50#define BAT_ADDR_MFR_TYPE	0x5F
     51
     52struct olpc_battery_data {
     53	struct power_supply *olpc_ac;
     54	struct power_supply *olpc_bat;
     55	char bat_serial[17];
     56	bool new_proto;
     57	bool little_endian;
     58};
     59
     60/*********************************************************************
     61 *		Power
     62 *********************************************************************/
     63
     64static int olpc_ac_get_prop(struct power_supply *psy,
     65			    enum power_supply_property psp,
     66			    union power_supply_propval *val)
     67{
     68	int ret = 0;
     69	uint8_t status;
     70
     71	switch (psp) {
     72	case POWER_SUPPLY_PROP_ONLINE:
     73		ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
     74		if (ret)
     75			return ret;
     76
     77		val->intval = !!(status & BAT_STAT_AC);
     78		break;
     79	default:
     80		ret = -EINVAL;
     81		break;
     82	}
     83	return ret;
     84}
     85
     86static enum power_supply_property olpc_ac_props[] = {
     87	POWER_SUPPLY_PROP_ONLINE,
     88};
     89
     90static const struct power_supply_desc olpc_ac_desc = {
     91	.name = "olpc_ac",
     92	.type = POWER_SUPPLY_TYPE_MAINS,
     93	.properties = olpc_ac_props,
     94	.num_properties = ARRAY_SIZE(olpc_ac_props),
     95	.get_property = olpc_ac_get_prop,
     96};
     97
     98static int olpc_bat_get_status(struct olpc_battery_data *data,
     99		union power_supply_propval *val, uint8_t ec_byte)
    100{
    101	if (data->new_proto) {
    102		if (ec_byte & (BAT_STAT_CHARGING | BAT_STAT_TRICKLE))
    103			val->intval = POWER_SUPPLY_STATUS_CHARGING;
    104		else if (ec_byte & BAT_STAT_DISCHARGING)
    105			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
    106		else if (ec_byte & BAT_STAT_FULL)
    107			val->intval = POWER_SUPPLY_STATUS_FULL;
    108		else /* er,... */
    109			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    110	} else {
    111		/* Older EC didn't report charge/discharge bits */
    112		if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */
    113			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
    114		else if (ec_byte & BAT_STAT_FULL)
    115			val->intval = POWER_SUPPLY_STATUS_FULL;
    116		else /* Not _necessarily_ true but EC doesn't tell all yet */
    117			val->intval = POWER_SUPPLY_STATUS_CHARGING;
    118	}
    119
    120	return 0;
    121}
    122
    123static int olpc_bat_get_health(union power_supply_propval *val)
    124{
    125	uint8_t ec_byte;
    126	int ret;
    127
    128	ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
    129	if (ret)
    130		return ret;
    131
    132	switch (ec_byte) {
    133	case 0:
    134		val->intval = POWER_SUPPLY_HEALTH_GOOD;
    135		break;
    136
    137	case BAT_ERR_OVERTEMP:
    138		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
    139		break;
    140
    141	case BAT_ERR_OVERVOLTAGE:
    142		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
    143		break;
    144
    145	case BAT_ERR_INFOFAIL:
    146	case BAT_ERR_OUT_OF_CONTROL:
    147	case BAT_ERR_ID_FAIL:
    148	case BAT_ERR_ACR_FAIL:
    149		val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
    150		break;
    151
    152	default:
    153		/* Eep. We don't know this failure code */
    154		ret = -EIO;
    155	}
    156
    157	return ret;
    158}
    159
    160static int olpc_bat_get_mfr(union power_supply_propval *val)
    161{
    162	uint8_t ec_byte;
    163	int ret;
    164
    165	ec_byte = BAT_ADDR_MFR_TYPE;
    166	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
    167	if (ret)
    168		return ret;
    169
    170	switch (ec_byte >> 4) {
    171	case 1:
    172		val->strval = "Gold Peak";
    173		break;
    174	case 2:
    175		val->strval = "BYD";
    176		break;
    177	default:
    178		val->strval = "Unknown";
    179		break;
    180	}
    181
    182	return ret;
    183}
    184
    185static int olpc_bat_get_tech(union power_supply_propval *val)
    186{
    187	uint8_t ec_byte;
    188	int ret;
    189
    190	ec_byte = BAT_ADDR_MFR_TYPE;
    191	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
    192	if (ret)
    193		return ret;
    194
    195	switch (ec_byte & 0xf) {
    196	case 1:
    197		val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
    198		break;
    199	case 2:
    200		val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
    201		break;
    202	default:
    203		val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
    204		break;
    205	}
    206
    207	return ret;
    208}
    209
    210static int olpc_bat_get_charge_full_design(union power_supply_propval *val)
    211{
    212	uint8_t ec_byte;
    213	union power_supply_propval tech;
    214	int ret, mfr;
    215
    216	ret = olpc_bat_get_tech(&tech);
    217	if (ret)
    218		return ret;
    219
    220	ec_byte = BAT_ADDR_MFR_TYPE;
    221	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
    222	if (ret)
    223		return ret;
    224
    225	mfr = ec_byte >> 4;
    226
    227	switch (tech.intval) {
    228	case POWER_SUPPLY_TECHNOLOGY_NiMH:
    229		switch (mfr) {
    230		case 1: /* Gold Peak */
    231			val->intval = 3000000*.8;
    232			break;
    233		default:
    234			return -EIO;
    235		}
    236		break;
    237
    238	case POWER_SUPPLY_TECHNOLOGY_LiFe:
    239		switch (mfr) {
    240		case 1: /* Gold Peak, fall through */
    241		case 2: /* BYD */
    242			val->intval = 2800000;
    243			break;
    244		default:
    245			return -EIO;
    246		}
    247		break;
    248
    249	default:
    250		return -EIO;
    251	}
    252
    253	return ret;
    254}
    255
    256static int olpc_bat_get_charge_now(union power_supply_propval *val)
    257{
    258	uint8_t soc;
    259	union power_supply_propval full;
    260	int ret;
    261
    262	ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &soc, 1);
    263	if (ret)
    264		return ret;
    265
    266	ret = olpc_bat_get_charge_full_design(&full);
    267	if (ret)
    268		return ret;
    269
    270	val->intval = soc * (full.intval / 100);
    271	return 0;
    272}
    273
    274static int olpc_bat_get_voltage_max_design(union power_supply_propval *val)
    275{
    276	uint8_t ec_byte;
    277	union power_supply_propval tech;
    278	int mfr;
    279	int ret;
    280
    281	ret = olpc_bat_get_tech(&tech);
    282	if (ret)
    283		return ret;
    284
    285	ec_byte = BAT_ADDR_MFR_TYPE;
    286	ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
    287	if (ret)
    288		return ret;
    289
    290	mfr = ec_byte >> 4;
    291
    292	switch (tech.intval) {
    293	case POWER_SUPPLY_TECHNOLOGY_NiMH:
    294		switch (mfr) {
    295		case 1: /* Gold Peak */
    296			val->intval = 6000000;
    297			break;
    298		default:
    299			return -EIO;
    300		}
    301		break;
    302
    303	case POWER_SUPPLY_TECHNOLOGY_LiFe:
    304		switch (mfr) {
    305		case 1: /* Gold Peak */
    306			val->intval = 6400000;
    307			break;
    308		case 2: /* BYD */
    309			val->intval = 6500000;
    310			break;
    311		default:
    312			return -EIO;
    313		}
    314		break;
    315
    316	default:
    317		return -EIO;
    318	}
    319
    320	return ret;
    321}
    322
    323static u16 ecword_to_cpu(struct olpc_battery_data *data, u16 ec_word)
    324{
    325	if (data->little_endian)
    326		return le16_to_cpu((__force __le16)ec_word);
    327	else
    328		return be16_to_cpu((__force __be16)ec_word);
    329}
    330
    331/*********************************************************************
    332 *		Battery properties
    333 *********************************************************************/
    334static int olpc_bat_get_property(struct power_supply *psy,
    335				 enum power_supply_property psp,
    336				 union power_supply_propval *val)
    337{
    338	struct olpc_battery_data *data = power_supply_get_drvdata(psy);
    339	int ret = 0;
    340	u16 ec_word;
    341	uint8_t ec_byte;
    342	__be64 ser_buf;
    343
    344	ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1);
    345	if (ret)
    346		return ret;
    347
    348	/* Theoretically there's a race here -- the battery could be
    349	   removed immediately after we check whether it's present, and
    350	   then we query for some other property of the now-absent battery.
    351	   It doesn't matter though -- the EC will return the last-known
    352	   information, and it's as if we just ran that _little_ bit faster
    353	   and managed to read it out before the battery went away. */
    354	if (!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)) &&
    355			psp != POWER_SUPPLY_PROP_PRESENT)
    356		return -ENODEV;
    357
    358	switch (psp) {
    359	case POWER_SUPPLY_PROP_STATUS:
    360		ret = olpc_bat_get_status(data, val, ec_byte);
    361		if (ret)
    362			return ret;
    363		break;
    364	case POWER_SUPPLY_PROP_CHARGE_TYPE:
    365		if (ec_byte & BAT_STAT_TRICKLE)
    366			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
    367		else if (ec_byte & BAT_STAT_CHARGING)
    368			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
    369		else
    370			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
    371		break;
    372	case POWER_SUPPLY_PROP_PRESENT:
    373		val->intval = !!(ec_byte & (BAT_STAT_PRESENT |
    374					    BAT_STAT_TRICKLE));
    375		break;
    376
    377	case POWER_SUPPLY_PROP_HEALTH:
    378		if (ec_byte & BAT_STAT_DESTROY)
    379			val->intval = POWER_SUPPLY_HEALTH_DEAD;
    380		else {
    381			ret = olpc_bat_get_health(val);
    382			if (ret)
    383				return ret;
    384		}
    385		break;
    386
    387	case POWER_SUPPLY_PROP_MANUFACTURER:
    388		ret = olpc_bat_get_mfr(val);
    389		if (ret)
    390			return ret;
    391		break;
    392	case POWER_SUPPLY_PROP_TECHNOLOGY:
    393		ret = olpc_bat_get_tech(val);
    394		if (ret)
    395			return ret;
    396		break;
    397	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
    398	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    399		ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2);
    400		if (ret)
    401			return ret;
    402
    403		val->intval = ecword_to_cpu(data, ec_word) * 9760L / 32;
    404		break;
    405	case POWER_SUPPLY_PROP_CURRENT_AVG:
    406	case POWER_SUPPLY_PROP_CURRENT_NOW:
    407		ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
    408		if (ret)
    409			return ret;
    410
    411		val->intval = ecword_to_cpu(data, ec_word) * 15625L / 120;
    412		break;
    413	case POWER_SUPPLY_PROP_CAPACITY:
    414		ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
    415		if (ret)
    416			return ret;
    417		val->intval = ec_byte;
    418		break;
    419	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
    420		if (ec_byte & BAT_STAT_FULL)
    421			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
    422		else if (ec_byte & BAT_STAT_LOW)
    423			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
    424		else
    425			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
    426		break;
    427	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
    428		ret = olpc_bat_get_charge_full_design(val);
    429		if (ret)
    430			return ret;
    431		break;
    432	case POWER_SUPPLY_PROP_CHARGE_NOW:
    433		ret = olpc_bat_get_charge_now(val);
    434		if (ret)
    435			return ret;
    436		break;
    437	case POWER_SUPPLY_PROP_TEMP:
    438		ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
    439		if (ret)
    440			return ret;
    441
    442		val->intval = ecword_to_cpu(data, ec_word) * 10 / 256;
    443		break;
    444	case POWER_SUPPLY_PROP_TEMP_AMBIENT:
    445		ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
    446		if (ret)
    447			return ret;
    448
    449		val->intval = (int)ecword_to_cpu(data, ec_word) * 10 / 256;
    450		break;
    451	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
    452		ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2);
    453		if (ret)
    454			return ret;
    455
    456		val->intval = ecword_to_cpu(data, ec_word) * 6250 / 15;
    457		break;
    458	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
    459		ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
    460		if (ret)
    461			return ret;
    462
    463		sprintf(data->bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
    464		val->strval = data->bat_serial;
    465		break;
    466	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
    467		ret = olpc_bat_get_voltage_max_design(val);
    468		if (ret)
    469			return ret;
    470		break;
    471	default:
    472		ret = -EINVAL;
    473		break;
    474	}
    475
    476	return ret;
    477}
    478
    479static enum power_supply_property olpc_xo1_bat_props[] = {
    480	POWER_SUPPLY_PROP_STATUS,
    481	POWER_SUPPLY_PROP_CHARGE_TYPE,
    482	POWER_SUPPLY_PROP_PRESENT,
    483	POWER_SUPPLY_PROP_HEALTH,
    484	POWER_SUPPLY_PROP_TECHNOLOGY,
    485	POWER_SUPPLY_PROP_VOLTAGE_AVG,
    486	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    487	POWER_SUPPLY_PROP_CURRENT_AVG,
    488	POWER_SUPPLY_PROP_CURRENT_NOW,
    489	POWER_SUPPLY_PROP_CAPACITY,
    490	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
    491	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
    492	POWER_SUPPLY_PROP_CHARGE_NOW,
    493	POWER_SUPPLY_PROP_TEMP,
    494	POWER_SUPPLY_PROP_TEMP_AMBIENT,
    495	POWER_SUPPLY_PROP_MANUFACTURER,
    496	POWER_SUPPLY_PROP_SERIAL_NUMBER,
    497	POWER_SUPPLY_PROP_CHARGE_COUNTER,
    498	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
    499};
    500
    501/* XO-1.5 does not have ambient temperature property */
    502static enum power_supply_property olpc_xo15_bat_props[] = {
    503	POWER_SUPPLY_PROP_STATUS,
    504	POWER_SUPPLY_PROP_CHARGE_TYPE,
    505	POWER_SUPPLY_PROP_PRESENT,
    506	POWER_SUPPLY_PROP_HEALTH,
    507	POWER_SUPPLY_PROP_TECHNOLOGY,
    508	POWER_SUPPLY_PROP_VOLTAGE_AVG,
    509	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    510	POWER_SUPPLY_PROP_CURRENT_AVG,
    511	POWER_SUPPLY_PROP_CURRENT_NOW,
    512	POWER_SUPPLY_PROP_CAPACITY,
    513	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
    514	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
    515	POWER_SUPPLY_PROP_CHARGE_NOW,
    516	POWER_SUPPLY_PROP_TEMP,
    517	POWER_SUPPLY_PROP_MANUFACTURER,
    518	POWER_SUPPLY_PROP_SERIAL_NUMBER,
    519	POWER_SUPPLY_PROP_CHARGE_COUNTER,
    520	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
    521};
    522
    523/* EEPROM reading goes completely around the power_supply API, sadly */
    524
    525#define EEPROM_START	0x20
    526#define EEPROM_END	0x80
    527#define EEPROM_SIZE	(EEPROM_END - EEPROM_START)
    528
    529static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
    530		struct bin_attribute *attr, char *buf, loff_t off, size_t count)
    531{
    532	uint8_t ec_byte;
    533	int ret;
    534	int i;
    535
    536	for (i = 0; i < count; i++) {
    537		ec_byte = EEPROM_START + off + i;
    538		ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
    539		if (ret) {
    540			pr_err("olpc-battery: "
    541			       "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n",
    542			       ec_byte, ret);
    543			return -EIO;
    544		}
    545	}
    546
    547	return count;
    548}
    549
    550static struct bin_attribute olpc_bat_eeprom = {
    551	.attr = {
    552		.name = "eeprom",
    553		.mode = S_IRUGO,
    554	},
    555	.size = EEPROM_SIZE,
    556	.read = olpc_bat_eeprom_read,
    557};
    558
    559/* Allow userspace to see the specific error value pulled from the EC */
    560
    561static ssize_t olpc_bat_error_read(struct device *dev,
    562		struct device_attribute *attr, char *buf)
    563{
    564	uint8_t ec_byte;
    565	ssize_t ret;
    566
    567	ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
    568	if (ret < 0)
    569		return ret;
    570
    571	return sprintf(buf, "%d\n", ec_byte);
    572}
    573
    574static struct device_attribute olpc_bat_error = {
    575	.attr = {
    576		.name = "error",
    577		.mode = S_IRUGO,
    578	},
    579	.show = olpc_bat_error_read,
    580};
    581
    582static struct attribute *olpc_bat_sysfs_attrs[] = {
    583	&olpc_bat_error.attr,
    584	NULL
    585};
    586
    587static struct bin_attribute *olpc_bat_sysfs_bin_attrs[] = {
    588	&olpc_bat_eeprom,
    589	NULL
    590};
    591
    592static const struct attribute_group olpc_bat_sysfs_group = {
    593	.attrs = olpc_bat_sysfs_attrs,
    594	.bin_attrs = olpc_bat_sysfs_bin_attrs,
    595
    596};
    597
    598static const struct attribute_group *olpc_bat_sysfs_groups[] = {
    599	&olpc_bat_sysfs_group,
    600	NULL
    601};
    602
    603/*********************************************************************
    604 *		Initialisation
    605 *********************************************************************/
    606
    607static struct power_supply_desc olpc_bat_desc = {
    608	.name = "olpc_battery",
    609	.get_property = olpc_bat_get_property,
    610	.use_for_apm = 1,
    611};
    612
    613static int olpc_battery_suspend(struct platform_device *pdev,
    614				pm_message_t state)
    615{
    616	struct olpc_battery_data *data = platform_get_drvdata(pdev);
    617
    618	if (device_may_wakeup(&data->olpc_ac->dev))
    619		olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR);
    620	else
    621		olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR);
    622
    623	if (device_may_wakeup(&data->olpc_bat->dev))
    624		olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
    625				   | EC_SCI_SRC_BATERR);
    626	else
    627		olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
    628				     | EC_SCI_SRC_BATERR);
    629
    630	return 0;
    631}
    632
    633static int olpc_battery_probe(struct platform_device *pdev)
    634{
    635	struct power_supply_config bat_psy_cfg = {};
    636	struct power_supply_config ac_psy_cfg = {};
    637	struct olpc_battery_data *data;
    638	uint8_t status;
    639	uint8_t ecver;
    640	int ret;
    641
    642	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    643	if (!data)
    644		return -ENOMEM;
    645	platform_set_drvdata(pdev, data);
    646
    647	/* See if the EC is already there and get the EC revision */
    648	ret = olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0, &ecver, 1);
    649	if (ret)
    650		return ret;
    651
    652	if (of_find_compatible_node(NULL, NULL, "olpc,xo1.75-ec")) {
    653		/* XO 1.75 */
    654		data->new_proto = true;
    655		data->little_endian = true;
    656	} else if (ecver > 0x44) {
    657		/* XO 1 or 1.5 with a new EC firmware. */
    658		data->new_proto = true;
    659	} else if (ecver < 0x44) {
    660		/*
    661		 * We've seen a number of EC protocol changes; this driver
    662		 * requires the latest EC protocol, supported by 0x44 and above.
    663		 */
    664		printk(KERN_NOTICE "OLPC EC version 0x%02x too old for "
    665			"battery driver.\n", ecver);
    666		return -ENXIO;
    667	}
    668
    669	ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
    670	if (ret)
    671		return ret;
    672
    673	/* Ignore the status. It doesn't actually matter */
    674
    675	ac_psy_cfg.of_node = pdev->dev.of_node;
    676	ac_psy_cfg.drv_data = data;
    677
    678	data->olpc_ac = devm_power_supply_register(&pdev->dev, &olpc_ac_desc,
    679								&ac_psy_cfg);
    680	if (IS_ERR(data->olpc_ac))
    681		return PTR_ERR(data->olpc_ac);
    682
    683	if (of_device_is_compatible(pdev->dev.of_node, "olpc,xo1.5-battery")) {
    684		/* XO-1.5 */
    685		olpc_bat_desc.properties = olpc_xo15_bat_props;
    686		olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo15_bat_props);
    687	} else {
    688		/* XO-1 */
    689		olpc_bat_desc.properties = olpc_xo1_bat_props;
    690		olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo1_bat_props);
    691	}
    692
    693	bat_psy_cfg.of_node = pdev->dev.of_node;
    694	bat_psy_cfg.drv_data = data;
    695	bat_psy_cfg.attr_grp = olpc_bat_sysfs_groups;
    696
    697	data->olpc_bat = devm_power_supply_register(&pdev->dev, &olpc_bat_desc,
    698								&bat_psy_cfg);
    699	if (IS_ERR(data->olpc_bat))
    700		return PTR_ERR(data->olpc_bat);
    701
    702	if (olpc_ec_wakeup_available()) {
    703		device_set_wakeup_capable(&data->olpc_ac->dev, true);
    704		device_set_wakeup_capable(&data->olpc_bat->dev, true);
    705	}
    706
    707	return 0;
    708}
    709
    710static const struct of_device_id olpc_battery_ids[] = {
    711	{ .compatible = "olpc,xo1-battery" },
    712	{ .compatible = "olpc,xo1.5-battery" },
    713	{}
    714};
    715MODULE_DEVICE_TABLE(of, olpc_battery_ids);
    716
    717static struct platform_driver olpc_battery_driver = {
    718	.driver = {
    719		.name = "olpc-battery",
    720		.of_match_table = olpc_battery_ids,
    721	},
    722	.probe = olpc_battery_probe,
    723	.suspend = olpc_battery_suspend,
    724};
    725
    726module_platform_driver(olpc_battery_driver);
    727
    728MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
    729MODULE_LICENSE("GPL");
    730MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");