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

surface_battery.c (22453B)


      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");