linux/drivers/power/supply/surface_battery.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Battery driver for 7th-generation Microsoft Surface devices via Surface
   4 * System Aggregator Module (SSAM).
   5 *
   6 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
   7 */
   8
   9#include <asm/unaligned.h>
  10#include <linux/jiffies.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/mutex.h>
  14#include <linux/power_supply.h>
  15#include <linux/sysfs.h>
  16#include <linux/types.h>
  17#include <linux/workqueue.h>
  18
  19#include <linux/surface_aggregator/device.h>
  20
  21
  22/* -- SAM interface. -------------------------------------------------------- */
  23
  24enum sam_event_cid_bat {
  25        SAM_EVENT_CID_BAT_BIX         = 0x15,
  26        SAM_EVENT_CID_BAT_BST         = 0x16,
  27        SAM_EVENT_CID_BAT_ADP         = 0x17,
  28        SAM_EVENT_CID_BAT_PROT        = 0x18,
  29        SAM_EVENT_CID_BAT_DPTF        = 0x53,
  30};
  31
  32enum sam_battery_sta {
  33        SAM_BATTERY_STA_OK            = 0x0f,
  34        SAM_BATTERY_STA_PRESENT       = 0x10,
  35};
  36
  37enum sam_battery_state {
  38        SAM_BATTERY_STATE_DISCHARGING = BIT(0),
  39        SAM_BATTERY_STATE_CHARGING    = BIT(1),
  40        SAM_BATTERY_STATE_CRITICAL    = BIT(2),
  41};
  42
  43enum sam_battery_power_unit {
  44        SAM_BATTERY_POWER_UNIT_mW     = 0,
  45        SAM_BATTERY_POWER_UNIT_mA     = 1,
  46};
  47
  48/* Equivalent to data returned in ACPI _BIX method, revision 0. */
  49struct spwr_bix {
  50        u8  revision;
  51        __le32 power_unit;
  52        __le32 design_cap;
  53        __le32 last_full_charge_cap;
  54        __le32 technology;
  55        __le32 design_voltage;
  56        __le32 design_cap_warn;
  57        __le32 design_cap_low;
  58        __le32 cycle_count;
  59        __le32 measurement_accuracy;
  60        __le32 max_sampling_time;
  61        __le32 min_sampling_time;
  62        __le32 max_avg_interval;
  63        __le32 min_avg_interval;
  64        __le32 bat_cap_granularity_1;
  65        __le32 bat_cap_granularity_2;
  66        __u8 model[21];
  67        __u8 serial[11];
  68        __u8 type[5];
  69        __u8 oem_info[21];
  70} __packed;
  71
  72static_assert(sizeof(struct spwr_bix) == 119);
  73
  74/* Equivalent to data returned in ACPI _BST method. */
  75struct spwr_bst {
  76        __le32 state;
  77        __le32 present_rate;
  78        __le32 remaining_cap;
  79        __le32 present_voltage;
  80} __packed;
  81
  82static_assert(sizeof(struct spwr_bst) == 16);
  83
  84#define SPWR_BIX_REVISION               0
  85#define SPWR_BATTERY_VALUE_UNKNOWN      0xffffffff
  86
  87/* Get battery status (_STA) */
  88SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
  89        .target_category = SSAM_SSH_TC_BAT,
  90        .command_id      = 0x01,
  91});
  92
  93/* Get battery static information (_BIX). */
  94SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_bix, struct spwr_bix, {
  95        .target_category = SSAM_SSH_TC_BAT,
  96        .command_id      = 0x02,
  97});
  98
  99/* Get battery dynamic information (_BST). */
 100SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_bst, struct spwr_bst, {
 101        .target_category = SSAM_SSH_TC_BAT,
 102        .command_id      = 0x03,
 103});
 104
 105/* Set battery trip point (_BTP). */
 106SSAM_DEFINE_SYNC_REQUEST_CL_W(ssam_bat_set_btp, __le32, {
 107        .target_category = SSAM_SSH_TC_BAT,
 108        .command_id      = 0x04,
 109});
 110
 111
 112/* -- Device structures. ---------------------------------------------------- */
 113
 114struct spwr_psy_properties {
 115        const char *name;
 116        struct ssam_event_registry registry;
 117};
 118
 119struct spwr_battery_device {
 120        struct ssam_device *sdev;
 121
 122        char name[32];
 123        struct power_supply *psy;
 124        struct power_supply_desc psy_desc;
 125
 126        struct delayed_work update_work;
 127
 128        struct ssam_event_notifier notif;
 129
 130        struct mutex lock;  /* Guards access to state data below. */
 131        unsigned long timestamp;
 132
 133        __le32 sta;
 134        struct spwr_bix bix;
 135        struct spwr_bst bst;
 136        u32 alarm;
 137};
 138
 139
 140/* -- Module parameters. ---------------------------------------------------- */
 141
 142static unsigned int cache_time = 1000;
 143module_param(cache_time, uint, 0644);
 144MODULE_PARM_DESC(cache_time, "battery state caching time in milliseconds [default: 1000]");
 145
 146
 147/* -- State management. ----------------------------------------------------- */
 148
 149/*
 150 * Delay for battery update quirk. See spwr_external_power_changed() below
 151 * for more details.
 152 */
 153#define SPWR_AC_BAT_UPDATE_DELAY        msecs_to_jiffies(5000)
 154
 155static bool spwr_battery_present(struct spwr_battery_device *bat)
 156{
 157        lockdep_assert_held(&bat->lock);
 158
 159        return le32_to_cpu(bat->sta) & SAM_BATTERY_STA_PRESENT;
 160}
 161
 162static int spwr_battery_load_sta(struct spwr_battery_device *bat)
 163{
 164        lockdep_assert_held(&bat->lock);
 165
 166        return ssam_retry(ssam_bat_get_sta, bat->sdev, &bat->sta);
 167}
 168
 169static int spwr_battery_load_bix(struct spwr_battery_device *bat)
 170{
 171        int status;
 172
 173        lockdep_assert_held(&bat->lock);
 174
 175        if (!spwr_battery_present(bat))
 176                return 0;
 177
 178        status = ssam_retry(ssam_bat_get_bix, bat->sdev, &bat->bix);
 179
 180        /* Enforce NULL terminated strings in case anything goes wrong... */
 181        bat->bix.model[ARRAY_SIZE(bat->bix.model) - 1] = 0;
 182        bat->bix.serial[ARRAY_SIZE(bat->bix.serial) - 1] = 0;
 183        bat->bix.type[ARRAY_SIZE(bat->bix.type) - 1] = 0;
 184        bat->bix.oem_info[ARRAY_SIZE(bat->bix.oem_info) - 1] = 0;
 185
 186        return status;
 187}
 188
 189static int spwr_battery_load_bst(struct spwr_battery_device *bat)
 190{
 191        lockdep_assert_held(&bat->lock);
 192
 193        if (!spwr_battery_present(bat))
 194                return 0;
 195
 196        return ssam_retry(ssam_bat_get_bst, bat->sdev, &bat->bst);
 197}
 198
 199static int spwr_battery_set_alarm_unlocked(struct spwr_battery_device *bat, u32 value)
 200{
 201        __le32 value_le = cpu_to_le32(value);
 202
 203        lockdep_assert_held(&bat->lock);
 204
 205        bat->alarm = value;
 206        return ssam_retry(ssam_bat_set_btp, bat->sdev, &value_le);
 207}
 208
 209static int spwr_battery_update_bst_unlocked(struct spwr_battery_device *bat, bool cached)
 210{
 211        unsigned long cache_deadline = bat->timestamp + msecs_to_jiffies(cache_time);
 212        int status;
 213
 214        lockdep_assert_held(&bat->lock);
 215
 216        if (cached && bat->timestamp && time_is_after_jiffies(cache_deadline))
 217                return 0;
 218
 219        status = spwr_battery_load_sta(bat);
 220        if (status)
 221                return status;
 222
 223        status = spwr_battery_load_bst(bat);
 224        if (status)
 225                return status;
 226
 227        bat->timestamp = jiffies;
 228        return 0;
 229}
 230
 231static int spwr_battery_update_bst(struct spwr_battery_device *bat, bool cached)
 232{
 233        int status;
 234
 235        mutex_lock(&bat->lock);
 236        status = spwr_battery_update_bst_unlocked(bat, cached);
 237        mutex_unlock(&bat->lock);
 238
 239        return status;
 240}
 241
 242static int spwr_battery_update_bix_unlocked(struct spwr_battery_device *bat)
 243{
 244        int status;
 245
 246        lockdep_assert_held(&bat->lock);
 247
 248        status = spwr_battery_load_sta(bat);
 249        if (status)
 250                return status;
 251
 252        status = spwr_battery_load_bix(bat);
 253        if (status)
 254                return status;
 255
 256        status = spwr_battery_load_bst(bat);
 257        if (status)
 258                return status;
 259
 260        if (bat->bix.revision != SPWR_BIX_REVISION)
 261                dev_warn(&bat->sdev->dev, "unsupported battery revision: %u\n", bat->bix.revision);
 262
 263        bat->timestamp = jiffies;
 264        return 0;
 265}
 266
 267static u32 sprw_battery_get_full_cap_safe(struct spwr_battery_device *bat)
 268{
 269        u32 full_cap = get_unaligned_le32(&bat->bix.last_full_charge_cap);
 270
 271        lockdep_assert_held(&bat->lock);
 272
 273        if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN)
 274                full_cap = get_unaligned_le32(&bat->bix.design_cap);
 275
 276        return full_cap;
 277}
 278
 279static bool spwr_battery_is_full(struct spwr_battery_device *bat)
 280{
 281        u32 state = get_unaligned_le32(&bat->bst.state);
 282        u32 full_cap = sprw_battery_get_full_cap_safe(bat);
 283        u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap);
 284
 285        lockdep_assert_held(&bat->lock);
 286
 287        return full_cap != SPWR_BATTERY_VALUE_UNKNOWN && full_cap != 0 &&
 288                remaining_cap != SPWR_BATTERY_VALUE_UNKNOWN &&
 289                remaining_cap >= full_cap &&
 290                state == 0;
 291}
 292
 293static int spwr_battery_recheck_full(struct spwr_battery_device *bat)
 294{
 295        bool present;
 296        u32 unit;
 297        int status;
 298
 299        mutex_lock(&bat->lock);
 300        unit = get_unaligned_le32(&bat->bix.power_unit);
 301        present = spwr_battery_present(bat);
 302
 303        status = spwr_battery_update_bix_unlocked(bat);
 304        if (status)
 305                goto out;
 306
 307        /* If battery has been attached, (re-)initialize alarm. */
 308        if (!present && spwr_battery_present(bat)) {
 309                u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn);
 310
 311                status = spwr_battery_set_alarm_unlocked(bat, cap_warn);
 312                if (status)
 313                        goto out;
 314        }
 315
 316        /*
 317         * Warn if the unit has changed. This is something we genuinely don't
 318         * expect to happen, so make this a big warning. If it does, we'll
 319         * need to add support for it.
 320         */
 321        WARN_ON(unit != get_unaligned_le32(&bat->bix.power_unit));
 322
 323out:
 324        mutex_unlock(&bat->lock);
 325
 326        if (!status)
 327                power_supply_changed(bat->psy);
 328
 329        return status;
 330}
 331
 332static int spwr_battery_recheck_status(struct spwr_battery_device *bat)
 333{
 334        int status;
 335
 336        status = spwr_battery_update_bst(bat, false);
 337        if (!status)
 338                power_supply_changed(bat->psy);
 339
 340        return status;
 341}
 342
 343static u32 spwr_notify_bat(struct ssam_event_notifier *nf, const struct ssam_event *event)
 344{
 345        struct spwr_battery_device *bat = container_of(nf, struct spwr_battery_device, notif);
 346        int status;
 347
 348        /*
 349         * We cannot use strict matching when registering the notifier as the
 350         * EC expects us to register it against instance ID 0. Strict matching
 351         * would thus drop events, as those may have non-zero instance IDs in
 352         * this subsystem. So we need to check the instance ID of the event
 353         * here manually.
 354         */
 355        if (event->instance_id != bat->sdev->uid.instance)
 356                return 0;
 357
 358        dev_dbg(&bat->sdev->dev, "power event (cid = %#04x, iid = %#04x, tid = %#04x)\n",
 359                event->command_id, event->instance_id, event->target_id);
 360
 361        switch (event->command_id) {
 362        case SAM_EVENT_CID_BAT_BIX:
 363                status = spwr_battery_recheck_full(bat);
 364                break;
 365
 366        case SAM_EVENT_CID_BAT_BST:
 367                status = spwr_battery_recheck_status(bat);
 368                break;
 369
 370        case SAM_EVENT_CID_BAT_PROT:
 371                /*
 372                 * TODO: Implement support for battery protection status change
 373                 *       event.
 374                 */
 375                status = 0;
 376                break;
 377
 378        case SAM_EVENT_CID_BAT_DPTF:
 379                /*
 380                 * TODO: Implement support for DPTF event.
 381                 */
 382                status = 0;
 383                break;
 384
 385        default:
 386                return 0;
 387        }
 388
 389        return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
 390}
 391
 392static void spwr_battery_update_bst_workfn(struct work_struct *work)
 393{
 394        struct delayed_work *dwork = to_delayed_work(work);
 395        struct spwr_battery_device *bat;
 396        int status;
 397
 398        bat = container_of(dwork, struct spwr_battery_device, update_work);
 399
 400        status = spwr_battery_update_bst(bat, false);
 401        if (status) {
 402                dev_err(&bat->sdev->dev, "failed to update battery state: %d\n", status);
 403                return;
 404        }
 405
 406        power_supply_changed(bat->psy);
 407}
 408
 409static void spwr_external_power_changed(struct power_supply *psy)
 410{
 411        struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
 412
 413        /*
 414         * Handle battery update quirk: When the battery is fully charged (or
 415         * charged up to the limit imposed by the UEFI battery limit) and the
 416         * adapter is plugged in or removed, the EC does not send a separate
 417         * event for the state (charging/discharging) change. Furthermore it
 418         * may take some time until the state is updated on the battery.
 419         * Schedule an update to solve this.
 420         */
 421
 422        schedule_delayed_work(&bat->update_work, SPWR_AC_BAT_UPDATE_DELAY);
 423}
 424
 425
 426/* -- Properties. ----------------------------------------------------------- */
 427
 428static const enum power_supply_property spwr_battery_props_chg[] = {
 429        POWER_SUPPLY_PROP_STATUS,
 430        POWER_SUPPLY_PROP_PRESENT,
 431        POWER_SUPPLY_PROP_TECHNOLOGY,
 432        POWER_SUPPLY_PROP_CYCLE_COUNT,
 433        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
 434        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 435        POWER_SUPPLY_PROP_CURRENT_NOW,
 436        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 437        POWER_SUPPLY_PROP_CHARGE_FULL,
 438        POWER_SUPPLY_PROP_CHARGE_NOW,
 439        POWER_SUPPLY_PROP_CAPACITY,
 440        POWER_SUPPLY_PROP_CAPACITY_LEVEL,
 441        POWER_SUPPLY_PROP_MODEL_NAME,
 442        POWER_SUPPLY_PROP_MANUFACTURER,
 443        POWER_SUPPLY_PROP_SERIAL_NUMBER,
 444};
 445
 446static const enum power_supply_property spwr_battery_props_eng[] = {
 447        POWER_SUPPLY_PROP_STATUS,
 448        POWER_SUPPLY_PROP_PRESENT,
 449        POWER_SUPPLY_PROP_TECHNOLOGY,
 450        POWER_SUPPLY_PROP_CYCLE_COUNT,
 451        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
 452        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 453        POWER_SUPPLY_PROP_POWER_NOW,
 454        POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
 455        POWER_SUPPLY_PROP_ENERGY_FULL,
 456        POWER_SUPPLY_PROP_ENERGY_NOW,
 457        POWER_SUPPLY_PROP_CAPACITY,
 458        POWER_SUPPLY_PROP_CAPACITY_LEVEL,
 459        POWER_SUPPLY_PROP_MODEL_NAME,
 460        POWER_SUPPLY_PROP_MANUFACTURER,
 461        POWER_SUPPLY_PROP_SERIAL_NUMBER,
 462};
 463
 464static int spwr_battery_prop_status(struct spwr_battery_device *bat)
 465{
 466        u32 state = get_unaligned_le32(&bat->bst.state);
 467        u32 present_rate = get_unaligned_le32(&bat->bst.present_rate);
 468
 469        lockdep_assert_held(&bat->lock);
 470
 471        if (state & SAM_BATTERY_STATE_DISCHARGING)
 472                return POWER_SUPPLY_STATUS_DISCHARGING;
 473
 474        if (state & SAM_BATTERY_STATE_CHARGING)
 475                return POWER_SUPPLY_STATUS_CHARGING;
 476
 477        if (spwr_battery_is_full(bat))
 478                return POWER_SUPPLY_STATUS_FULL;
 479
 480        if (present_rate == 0)
 481                return POWER_SUPPLY_STATUS_NOT_CHARGING;
 482
 483        return POWER_SUPPLY_STATUS_UNKNOWN;
 484}
 485
 486static int spwr_battery_prop_technology(struct spwr_battery_device *bat)
 487{
 488        lockdep_assert_held(&bat->lock);
 489
 490        if (!strcasecmp("NiCd", bat->bix.type))
 491                return POWER_SUPPLY_TECHNOLOGY_NiCd;
 492
 493        if (!strcasecmp("NiMH", bat->bix.type))
 494                return POWER_SUPPLY_TECHNOLOGY_NiMH;
 495
 496        if (!strcasecmp("LION", bat->bix.type))
 497                return POWER_SUPPLY_TECHNOLOGY_LION;
 498
 499        if (!strncasecmp("LI-ION", bat->bix.type, 6))
 500                return POWER_SUPPLY_TECHNOLOGY_LION;
 501
 502        if (!strcasecmp("LiP", bat->bix.type))
 503                return POWER_SUPPLY_TECHNOLOGY_LIPO;
 504
 505        return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
 506}
 507
 508static int spwr_battery_prop_capacity(struct spwr_battery_device *bat)
 509{
 510        u32 full_cap = sprw_battery_get_full_cap_safe(bat);
 511        u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap);
 512
 513        lockdep_assert_held(&bat->lock);
 514
 515        if (full_cap == 0 || full_cap == SPWR_BATTERY_VALUE_UNKNOWN)
 516                return -ENODATA;
 517
 518        if (remaining_cap == SPWR_BATTERY_VALUE_UNKNOWN)
 519                return -ENODATA;
 520
 521        return remaining_cap * 100 / full_cap;
 522}
 523
 524static int spwr_battery_prop_capacity_level(struct spwr_battery_device *bat)
 525{
 526        u32 state = get_unaligned_le32(&bat->bst.state);
 527        u32 remaining_cap = get_unaligned_le32(&bat->bst.remaining_cap);
 528
 529        lockdep_assert_held(&bat->lock);
 530
 531        if (state & SAM_BATTERY_STATE_CRITICAL)
 532                return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
 533
 534        if (spwr_battery_is_full(bat))
 535                return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
 536
 537        if (remaining_cap <= bat->alarm)
 538                return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
 539
 540        return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
 541}
 542
 543static int spwr_battery_get_property(struct power_supply *psy, enum power_supply_property psp,
 544                                     union power_supply_propval *val)
 545{
 546        struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
 547        u32 value;
 548        int status;
 549
 550        mutex_lock(&bat->lock);
 551
 552        status = spwr_battery_update_bst_unlocked(bat, true);
 553        if (status)
 554                goto out;
 555
 556        /* Abort if battery is not present. */
 557        if (!spwr_battery_present(bat) && psp != POWER_SUPPLY_PROP_PRESENT) {
 558                status = -ENODEV;
 559                goto out;
 560        }
 561
 562        switch (psp) {
 563        case POWER_SUPPLY_PROP_STATUS:
 564                val->intval = spwr_battery_prop_status(bat);
 565                break;
 566
 567        case POWER_SUPPLY_PROP_PRESENT:
 568                val->intval = spwr_battery_present(bat);
 569                break;
 570
 571        case POWER_SUPPLY_PROP_TECHNOLOGY:
 572                val->intval = spwr_battery_prop_technology(bat);
 573                break;
 574
 575        case POWER_SUPPLY_PROP_CYCLE_COUNT:
 576                value = get_unaligned_le32(&bat->bix.cycle_count);
 577                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 578                        val->intval = value;
 579                else
 580                        status = -ENODATA;
 581                break;
 582
 583        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
 584                value = get_unaligned_le32(&bat->bix.design_voltage);
 585                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 586                        val->intval = value * 1000;
 587                else
 588                        status = -ENODATA;
 589                break;
 590
 591        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 592                value = get_unaligned_le32(&bat->bst.present_voltage);
 593                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 594                        val->intval = value * 1000;
 595                else
 596                        status = -ENODATA;
 597                break;
 598
 599        case POWER_SUPPLY_PROP_CURRENT_NOW:
 600        case POWER_SUPPLY_PROP_POWER_NOW:
 601                value = get_unaligned_le32(&bat->bst.present_rate);
 602                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 603                        val->intval = value * 1000;
 604                else
 605                        status = -ENODATA;
 606                break;
 607
 608        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 609        case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
 610                value = get_unaligned_le32(&bat->bix.design_cap);
 611                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 612                        val->intval = value * 1000;
 613                else
 614                        status = -ENODATA;
 615                break;
 616
 617        case POWER_SUPPLY_PROP_CHARGE_FULL:
 618        case POWER_SUPPLY_PROP_ENERGY_FULL:
 619                value = get_unaligned_le32(&bat->bix.last_full_charge_cap);
 620                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 621                        val->intval = value * 1000;
 622                else
 623                        status = -ENODATA;
 624                break;
 625
 626        case POWER_SUPPLY_PROP_CHARGE_NOW:
 627        case POWER_SUPPLY_PROP_ENERGY_NOW:
 628                value = get_unaligned_le32(&bat->bst.remaining_cap);
 629                if (value != SPWR_BATTERY_VALUE_UNKNOWN)
 630                        val->intval = value * 1000;
 631                else
 632                        status = -ENODATA;
 633                break;
 634
 635        case POWER_SUPPLY_PROP_CAPACITY:
 636                val->intval = spwr_battery_prop_capacity(bat);
 637                break;
 638
 639        case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
 640                val->intval = spwr_battery_prop_capacity_level(bat);
 641                break;
 642
 643        case POWER_SUPPLY_PROP_MODEL_NAME:
 644                val->strval = bat->bix.model;
 645                break;
 646
 647        case POWER_SUPPLY_PROP_MANUFACTURER:
 648                val->strval = bat->bix.oem_info;
 649                break;
 650
 651        case POWER_SUPPLY_PROP_SERIAL_NUMBER:
 652                val->strval = bat->bix.serial;
 653                break;
 654
 655        default:
 656                status = -EINVAL;
 657                break;
 658        }
 659
 660out:
 661        mutex_unlock(&bat->lock);
 662        return status;
 663}
 664
 665
 666/* -- Alarm attribute. ------------------------------------------------------ */
 667
 668static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf)
 669{
 670        struct power_supply *psy = dev_get_drvdata(dev);
 671        struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
 672        int status;
 673
 674        mutex_lock(&bat->lock);
 675        status = sysfs_emit(buf, "%d\n", bat->alarm * 1000);
 676        mutex_unlock(&bat->lock);
 677
 678        return status;
 679}
 680
 681static ssize_t alarm_store(struct device *dev, struct device_attribute *attr, const char *buf,
 682                           size_t count)
 683{
 684        struct power_supply *psy = dev_get_drvdata(dev);
 685        struct spwr_battery_device *bat = power_supply_get_drvdata(psy);
 686        unsigned long value;
 687        int status;
 688
 689        status = kstrtoul(buf, 0, &value);
 690        if (status)
 691                return status;
 692
 693        mutex_lock(&bat->lock);
 694
 695        if (!spwr_battery_present(bat)) {
 696                mutex_unlock(&bat->lock);
 697                return -ENODEV;
 698        }
 699
 700        status = spwr_battery_set_alarm_unlocked(bat, value / 1000);
 701        if (status) {
 702                mutex_unlock(&bat->lock);
 703                return status;
 704        }
 705
 706        mutex_unlock(&bat->lock);
 707        return count;
 708}
 709
 710static DEVICE_ATTR_RW(alarm);
 711
 712static struct attribute *spwr_battery_attrs[] = {
 713        &dev_attr_alarm.attr,
 714        NULL,
 715};
 716ATTRIBUTE_GROUPS(spwr_battery);
 717
 718
 719/* -- Device setup. --------------------------------------------------------- */
 720
 721static void spwr_battery_init(struct spwr_battery_device *bat, struct ssam_device *sdev,
 722                              struct ssam_event_registry registry, const char *name)
 723{
 724        mutex_init(&bat->lock);
 725        strncpy(bat->name, name, ARRAY_SIZE(bat->name) - 1);
 726
 727        bat->sdev = sdev;
 728
 729        bat->notif.base.priority = 1;
 730        bat->notif.base.fn = spwr_notify_bat;
 731        bat->notif.event.reg = registry;
 732        bat->notif.event.id.target_category = sdev->uid.category;
 733        bat->notif.event.id.instance = 0;       /* need to register with instance 0 */
 734        bat->notif.event.mask = SSAM_EVENT_MASK_TARGET;
 735        bat->notif.event.flags = SSAM_EVENT_SEQUENCED;
 736
 737        bat->psy_desc.name = bat->name;
 738        bat->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
 739        bat->psy_desc.get_property = spwr_battery_get_property;
 740
 741        INIT_DELAYED_WORK(&bat->update_work, spwr_battery_update_bst_workfn);
 742}
 743
 744static int spwr_battery_register(struct spwr_battery_device *bat)
 745{
 746        struct power_supply_config psy_cfg = {};
 747        __le32 sta;
 748        int status;
 749
 750        /* Make sure the device is there and functioning properly. */
 751        status = ssam_retry(ssam_bat_get_sta, bat->sdev, &sta);
 752        if (status)
 753                return status;
 754
 755        if ((le32_to_cpu(sta) & SAM_BATTERY_STA_OK) != SAM_BATTERY_STA_OK)
 756                return -ENODEV;
 757
 758        /* Satisfy lockdep although we are in an exclusive context here. */
 759        mutex_lock(&bat->lock);
 760
 761        status = spwr_battery_update_bix_unlocked(bat);
 762        if (status) {
 763                mutex_unlock(&bat->lock);
 764                return status;
 765        }
 766
 767        if (spwr_battery_present(bat)) {
 768                u32 cap_warn = get_unaligned_le32(&bat->bix.design_cap_warn);
 769
 770                status = spwr_battery_set_alarm_unlocked(bat, cap_warn);
 771                if (status) {
 772                        mutex_unlock(&bat->lock);
 773                        return status;
 774                }
 775        }
 776
 777        mutex_unlock(&bat->lock);
 778
 779        bat->psy_desc.external_power_changed = spwr_external_power_changed;
 780
 781        switch (get_unaligned_le32(&bat->bix.power_unit)) {
 782        case SAM_BATTERY_POWER_UNIT_mW:
 783                bat->psy_desc.properties = spwr_battery_props_eng;
 784                bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_eng);
 785                break;
 786
 787        case SAM_BATTERY_POWER_UNIT_mA:
 788                bat->psy_desc.properties = spwr_battery_props_chg;
 789                bat->psy_desc.num_properties = ARRAY_SIZE(spwr_battery_props_chg);
 790                break;
 791
 792        default:
 793                dev_err(&bat->sdev->dev, "unsupported battery power unit: %u\n",
 794                        get_unaligned_le32(&bat->bix.power_unit));
 795                return -EINVAL;
 796        }
 797
 798        psy_cfg.drv_data = bat;
 799        psy_cfg.attr_grp = spwr_battery_groups;
 800
 801        bat->psy = devm_power_supply_register(&bat->sdev->dev, &bat->psy_desc, &psy_cfg);
 802        if (IS_ERR(bat->psy))
 803                return PTR_ERR(bat->psy);
 804
 805        return ssam_notifier_register(bat->sdev->ctrl, &bat->notif);
 806}
 807
 808
 809/* -- Driver setup. --------------------------------------------------------- */
 810
 811static int __maybe_unused surface_battery_resume(struct device *dev)
 812{
 813        return spwr_battery_recheck_full(dev_get_drvdata(dev));
 814}
 815static SIMPLE_DEV_PM_OPS(surface_battery_pm_ops, NULL, surface_battery_resume);
 816
 817static int surface_battery_probe(struct ssam_device *sdev)
 818{
 819        const struct spwr_psy_properties *p;
 820        struct spwr_battery_device *bat;
 821
 822        p = ssam_device_get_match_data(sdev);
 823        if (!p)
 824                return -ENODEV;
 825
 826        bat = devm_kzalloc(&sdev->dev, sizeof(*bat), GFP_KERNEL);
 827        if (!bat)
 828                return -ENOMEM;
 829
 830        spwr_battery_init(bat, sdev, p->registry, p->name);
 831        ssam_device_set_drvdata(sdev, bat);
 832
 833        return spwr_battery_register(bat);
 834}
 835
 836static void surface_battery_remove(struct ssam_device *sdev)
 837{
 838        struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev);
 839
 840        ssam_notifier_unregister(sdev->ctrl, &bat->notif);
 841        cancel_delayed_work_sync(&bat->update_work);
 842}
 843
 844static const struct spwr_psy_properties spwr_psy_props_bat1 = {
 845        .name = "BAT1",
 846        .registry = SSAM_EVENT_REGISTRY_SAM,
 847};
 848
 849static const struct spwr_psy_properties spwr_psy_props_bat2_sb3 = {
 850        .name = "BAT2",
 851        .registry = SSAM_EVENT_REGISTRY_KIP,
 852};
 853
 854static const struct ssam_device_id surface_battery_match[] = {
 855        { SSAM_SDEV(BAT, 0x01, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat1     },
 856        { SSAM_SDEV(BAT, 0x02, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat2_sb3 },
 857        { },
 858};
 859MODULE_DEVICE_TABLE(ssam, surface_battery_match);
 860
 861static struct ssam_device_driver surface_battery_driver = {
 862        .probe = surface_battery_probe,
 863        .remove = surface_battery_remove,
 864        .match_table = surface_battery_match,
 865        .driver = {
 866                .name = "surface_battery",
 867                .pm = &surface_battery_pm_ops,
 868                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
 869        },
 870};
 871module_ssam_device_driver(surface_battery_driver);
 872
 873MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
 874MODULE_DESCRIPTION("Battery driver for Surface System Aggregator Module");
 875MODULE_LICENSE("GPL");
 876