linux/drivers/power/supply/olpc_battery.c
<<
>>
Prefs
   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");
 731