876 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Battery driver for 7th-generation Microsoft Surface devices via Surface4 * System Aggregator Module (SSAM).5 *6 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>7 */8 9#include <linux/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 085#define SPWR_BATTERY_VALUE_UNKNOWN 0xffffffff86 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() below151 * 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't318 * expect to happen, so make this a big warning. If it does, we'll319 * 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 the350 * EC expects us to register it against instance ID 0. Strict matching351 * would thus drop events, as those may have non-zero instance IDs in352 * this subsystem. So we need to check the instance ID of the event353 * 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 change373 * 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 (or415 * charged up to the limit imposed by the UEFI battery limit) and the416 * adapter is plugged in or removed, the EC does not send a separate417 * event for the state (charging/discharging) change. Furthermore it418 * 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 else580 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 else588 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 else596 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 else605 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 else614 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 else623 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 else632 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 strscpy(bat->name, name, sizeof(bat->name));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_device_notifier_register(bat->sdev, &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_device_notifier_unregister(sdev, &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, SAM, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat1 },856 { SSAM_SDEV(BAT, KIP, 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