xref: /kernel/linux/linux-5.10/drivers/acpi/sbs.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  sbs.c - ACPI Smart Battery System Driver ($Revision: 2.0 $)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2007 Alexey Starikovskiy <astarikovskiy@suse.de>
68c2ecf20Sopenharmony_ci *  Copyright (c) 2005-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com>
78c2ecf20Sopenharmony_ci *  Copyright (c) 2005 Rich Townsend <rhdt@bartol.udel.edu>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/acpi.h>
178c2ecf20Sopenharmony_ci#include <linux/timer.h>
188c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_data/x86/apple.h>
228c2ecf20Sopenharmony_ci#include <acpi/battery.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "sbshc.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define PREFIX "ACPI: "
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define ACPI_SBS_CLASS			"sbs"
298c2ecf20Sopenharmony_ci#define ACPI_AC_CLASS			"ac_adapter"
308c2ecf20Sopenharmony_ci#define ACPI_SBS_DEVICE_NAME		"Smart Battery System"
318c2ecf20Sopenharmony_ci#define ACPI_BATTERY_DIR_NAME		"BAT%i"
328c2ecf20Sopenharmony_ci#define ACPI_AC_DIR_NAME		"AC0"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define ACPI_SBS_NOTIFY_STATUS		0x80
358c2ecf20Sopenharmony_ci#define ACPI_SBS_NOTIFY_INFO		0x81
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic unsigned int cache_time = 1000;
428c2ecf20Sopenharmony_cimodule_param(cache_time, uint, 0644);
438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cache_time, "cache time in milliseconds");
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define MAX_SBS_BAT			4
468c2ecf20Sopenharmony_ci#define ACPI_SBS_BLOCK_MAX		32
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic const struct acpi_device_id sbs_device_ids[] = {
498c2ecf20Sopenharmony_ci	{"ACPI0002", 0},
508c2ecf20Sopenharmony_ci	{"", 0},
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, sbs_device_ids);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct acpi_battery {
558c2ecf20Sopenharmony_ci	struct power_supply *bat;
568c2ecf20Sopenharmony_ci	struct power_supply_desc bat_desc;
578c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs;
588c2ecf20Sopenharmony_ci	unsigned long update_time;
598c2ecf20Sopenharmony_ci	char name[8];
608c2ecf20Sopenharmony_ci	char manufacturer_name[ACPI_SBS_BLOCK_MAX];
618c2ecf20Sopenharmony_ci	char device_name[ACPI_SBS_BLOCK_MAX];
628c2ecf20Sopenharmony_ci	char device_chemistry[ACPI_SBS_BLOCK_MAX];
638c2ecf20Sopenharmony_ci	u16 alarm_capacity;
648c2ecf20Sopenharmony_ci	u16 full_charge_capacity;
658c2ecf20Sopenharmony_ci	u16 design_capacity;
668c2ecf20Sopenharmony_ci	u16 design_voltage;
678c2ecf20Sopenharmony_ci	u16 serial_number;
688c2ecf20Sopenharmony_ci	u16 cycle_count;
698c2ecf20Sopenharmony_ci	u16 temp_now;
708c2ecf20Sopenharmony_ci	u16 voltage_now;
718c2ecf20Sopenharmony_ci	s16 rate_now;
728c2ecf20Sopenharmony_ci	s16 rate_avg;
738c2ecf20Sopenharmony_ci	u16 capacity_now;
748c2ecf20Sopenharmony_ci	u16 state_of_charge;
758c2ecf20Sopenharmony_ci	u16 state;
768c2ecf20Sopenharmony_ci	u16 mode;
778c2ecf20Sopenharmony_ci	u16 spec;
788c2ecf20Sopenharmony_ci	u8 id;
798c2ecf20Sopenharmony_ci	u8 present:1;
808c2ecf20Sopenharmony_ci	u8 have_sysfs_alarm:1;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define to_acpi_battery(x) power_supply_get_drvdata(x)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct acpi_sbs {
868c2ecf20Sopenharmony_ci	struct power_supply *charger;
878c2ecf20Sopenharmony_ci	struct acpi_device *device;
888c2ecf20Sopenharmony_ci	struct acpi_smb_hc *hc;
898c2ecf20Sopenharmony_ci	struct mutex lock;
908c2ecf20Sopenharmony_ci	struct acpi_battery battery[MAX_SBS_BAT];
918c2ecf20Sopenharmony_ci	u8 batteries_supported:4;
928c2ecf20Sopenharmony_ci	u8 manager_present:1;
938c2ecf20Sopenharmony_ci	u8 charger_present:1;
948c2ecf20Sopenharmony_ci	u8 charger_exists:1;
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci#define to_acpi_sbs(x) power_supply_get_drvdata(x)
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int acpi_sbs_remove(struct acpi_device *device);
1008c2ecf20Sopenharmony_cistatic int acpi_battery_get_state(struct acpi_battery *battery);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic inline int battery_scale(int log)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	int scale = 1;
1058c2ecf20Sopenharmony_ci	while (log--)
1068c2ecf20Sopenharmony_ci		scale *= 10;
1078c2ecf20Sopenharmony_ci	return scale;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic inline int acpi_battery_vscale(struct acpi_battery *battery)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	return battery_scale((battery->spec & 0x0f00) >> 8);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic inline int acpi_battery_ipscale(struct acpi_battery *battery)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return battery_scale((battery->spec & 0xf000) >> 12);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic inline int acpi_battery_mode(struct acpi_battery *battery)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return (battery->mode & 0x8000);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline int acpi_battery_scale(struct acpi_battery *battery)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return (acpi_battery_mode(battery) ? 10 : 1) *
1288c2ecf20Sopenharmony_ci	    acpi_battery_ipscale(battery);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int sbs_get_ac_property(struct power_supply *psy,
1328c2ecf20Sopenharmony_ci			       enum power_supply_property psp,
1338c2ecf20Sopenharmony_ci			       union power_supply_propval *val)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs = to_acpi_sbs(psy);
1368c2ecf20Sopenharmony_ci	switch (psp) {
1378c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
1388c2ecf20Sopenharmony_ci		val->intval = sbs->charger_present;
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	default:
1418c2ecf20Sopenharmony_ci		return -EINVAL;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int acpi_battery_technology(struct acpi_battery *battery)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	if (!strcasecmp("NiCd", battery->device_chemistry))
1498c2ecf20Sopenharmony_ci		return POWER_SUPPLY_TECHNOLOGY_NiCd;
1508c2ecf20Sopenharmony_ci	if (!strcasecmp("NiMH", battery->device_chemistry))
1518c2ecf20Sopenharmony_ci		return POWER_SUPPLY_TECHNOLOGY_NiMH;
1528c2ecf20Sopenharmony_ci	if (!strcasecmp("LION", battery->device_chemistry))
1538c2ecf20Sopenharmony_ci		return POWER_SUPPLY_TECHNOLOGY_LION;
1548c2ecf20Sopenharmony_ci	if (!strcasecmp("LiP", battery->device_chemistry))
1558c2ecf20Sopenharmony_ci		return POWER_SUPPLY_TECHNOLOGY_LIPO;
1568c2ecf20Sopenharmony_ci	return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int acpi_sbs_battery_get_property(struct power_supply *psy,
1608c2ecf20Sopenharmony_ci					 enum power_supply_property psp,
1618c2ecf20Sopenharmony_ci					 union power_supply_propval *val)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct acpi_battery *battery = to_acpi_battery(psy);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT)
1668c2ecf20Sopenharmony_ci		return -ENODEV;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	acpi_battery_get_state(battery);
1698c2ecf20Sopenharmony_ci	switch (psp) {
1708c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
1718c2ecf20Sopenharmony_ci		if (battery->rate_now < 0)
1728c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
1738c2ecf20Sopenharmony_ci		else if (battery->rate_now > 0)
1748c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_CHARGING;
1758c2ecf20Sopenharmony_ci		else
1768c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_FULL;
1778c2ecf20Sopenharmony_ci		break;
1788c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
1798c2ecf20Sopenharmony_ci		val->intval = battery->present;
1808c2ecf20Sopenharmony_ci		break;
1818c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TECHNOLOGY:
1828c2ecf20Sopenharmony_ci		val->intval = acpi_battery_technology(battery);
1838c2ecf20Sopenharmony_ci		break;
1848c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CYCLE_COUNT:
1858c2ecf20Sopenharmony_ci		val->intval = battery->cycle_count;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
1888c2ecf20Sopenharmony_ci		val->intval = battery->design_voltage *
1898c2ecf20Sopenharmony_ci			acpi_battery_vscale(battery) * 1000;
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
1928c2ecf20Sopenharmony_ci		val->intval = battery->voltage_now *
1938c2ecf20Sopenharmony_ci				acpi_battery_vscale(battery) * 1000;
1948c2ecf20Sopenharmony_ci		break;
1958c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
1968c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_POWER_NOW:
1978c2ecf20Sopenharmony_ci		val->intval = abs(battery->rate_now) *
1988c2ecf20Sopenharmony_ci				acpi_battery_ipscale(battery) * 1000;
1998c2ecf20Sopenharmony_ci		val->intval *= (acpi_battery_mode(battery)) ?
2008c2ecf20Sopenharmony_ci				(battery->voltage_now *
2018c2ecf20Sopenharmony_ci				acpi_battery_vscale(battery) / 1000) : 1;
2028c2ecf20Sopenharmony_ci		break;
2038c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_AVG:
2048c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_POWER_AVG:
2058c2ecf20Sopenharmony_ci		val->intval = abs(battery->rate_avg) *
2068c2ecf20Sopenharmony_ci				acpi_battery_ipscale(battery) * 1000;
2078c2ecf20Sopenharmony_ci		val->intval *= (acpi_battery_mode(battery)) ?
2088c2ecf20Sopenharmony_ci				(battery->voltage_now *
2098c2ecf20Sopenharmony_ci				acpi_battery_vscale(battery) / 1000) : 1;
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
2128c2ecf20Sopenharmony_ci		val->intval = battery->state_of_charge;
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
2158c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
2168c2ecf20Sopenharmony_ci		val->intval = battery->design_capacity *
2178c2ecf20Sopenharmony_ci			acpi_battery_scale(battery) * 1000;
2188c2ecf20Sopenharmony_ci		break;
2198c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL:
2208c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ENERGY_FULL:
2218c2ecf20Sopenharmony_ci		val->intval = battery->full_charge_capacity *
2228c2ecf20Sopenharmony_ci			acpi_battery_scale(battery) * 1000;
2238c2ecf20Sopenharmony_ci		break;
2248c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_NOW:
2258c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ENERGY_NOW:
2268c2ecf20Sopenharmony_ci		val->intval = battery->capacity_now *
2278c2ecf20Sopenharmony_ci				acpi_battery_scale(battery) * 1000;
2288c2ecf20Sopenharmony_ci		break;
2298c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
2308c2ecf20Sopenharmony_ci		val->intval = battery->temp_now - 2730;	// dK -> dC
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_MODEL_NAME:
2338c2ecf20Sopenharmony_ci		val->strval = battery->device_name;
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_MANUFACTURER:
2368c2ecf20Sopenharmony_ci		val->strval = battery->manufacturer_name;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	default:
2398c2ecf20Sopenharmony_ci		return -EINVAL;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic enum power_supply_property sbs_ac_props[] = {
2458c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic enum power_supply_property sbs_charge_battery_props[] = {
2498c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
2508c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
2518c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TECHNOLOGY,
2528c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CYCLE_COUNT,
2538c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
2548c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2558c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
2568c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_AVG,
2578c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
2588c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
2598c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_FULL,
2608c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_NOW,
2618c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
2628c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_MODEL_NAME,
2638c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_MANUFACTURER,
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic enum power_supply_property sbs_energy_battery_props[] = {
2678c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
2688c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
2698c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TECHNOLOGY,
2708c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
2718c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
2728c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
2738c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_AVG,
2748c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_POWER_NOW,
2758c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_POWER_AVG,
2768c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
2778c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
2788c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ENERGY_FULL,
2798c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ENERGY_NOW,
2808c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
2818c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_MODEL_NAME,
2828c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_MANUFACTURER,
2838c2ecf20Sopenharmony_ci};
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic const struct power_supply_desc acpi_sbs_charger_desc = {
2868c2ecf20Sopenharmony_ci	.name		= "sbs-charger",
2878c2ecf20Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_MAINS,
2888c2ecf20Sopenharmony_ci	.properties	= sbs_ac_props,
2898c2ecf20Sopenharmony_ci	.num_properties	= ARRAY_SIZE(sbs_ac_props),
2908c2ecf20Sopenharmony_ci	.get_property	= sbs_get_ac_property,
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------
2948c2ecf20Sopenharmony_ci                            Smart Battery System Management
2958c2ecf20Sopenharmony_ci   -------------------------------------------------------------------------- */
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistruct acpi_battery_reader {
2988c2ecf20Sopenharmony_ci	u8 command;		/* command for battery */
2998c2ecf20Sopenharmony_ci	u8 mode;		/* word or block? */
3008c2ecf20Sopenharmony_ci	size_t offset;		/* offset inside struct acpi_sbs_battery */
3018c2ecf20Sopenharmony_ci};
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic struct acpi_battery_reader info_readers[] = {
3048c2ecf20Sopenharmony_ci	{0x01, SMBUS_READ_WORD, offsetof(struct acpi_battery, alarm_capacity)},
3058c2ecf20Sopenharmony_ci	{0x03, SMBUS_READ_WORD, offsetof(struct acpi_battery, mode)},
3068c2ecf20Sopenharmony_ci	{0x10, SMBUS_READ_WORD, offsetof(struct acpi_battery, full_charge_capacity)},
3078c2ecf20Sopenharmony_ci	{0x17, SMBUS_READ_WORD, offsetof(struct acpi_battery, cycle_count)},
3088c2ecf20Sopenharmony_ci	{0x18, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_capacity)},
3098c2ecf20Sopenharmony_ci	{0x19, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_voltage)},
3108c2ecf20Sopenharmony_ci	{0x1a, SMBUS_READ_WORD, offsetof(struct acpi_battery, spec)},
3118c2ecf20Sopenharmony_ci	{0x1c, SMBUS_READ_WORD, offsetof(struct acpi_battery, serial_number)},
3128c2ecf20Sopenharmony_ci	{0x20, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, manufacturer_name)},
3138c2ecf20Sopenharmony_ci	{0x21, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_name)},
3148c2ecf20Sopenharmony_ci	{0x22, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_chemistry)},
3158c2ecf20Sopenharmony_ci};
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic struct acpi_battery_reader state_readers[] = {
3188c2ecf20Sopenharmony_ci	{0x08, SMBUS_READ_WORD, offsetof(struct acpi_battery, temp_now)},
3198c2ecf20Sopenharmony_ci	{0x09, SMBUS_READ_WORD, offsetof(struct acpi_battery, voltage_now)},
3208c2ecf20Sopenharmony_ci	{0x0a, SMBUS_READ_WORD, offsetof(struct acpi_battery, rate_now)},
3218c2ecf20Sopenharmony_ci	{0x0b, SMBUS_READ_WORD, offsetof(struct acpi_battery, rate_avg)},
3228c2ecf20Sopenharmony_ci	{0x0f, SMBUS_READ_WORD, offsetof(struct acpi_battery, capacity_now)},
3238c2ecf20Sopenharmony_ci	{0x0e, SMBUS_READ_WORD, offsetof(struct acpi_battery, state_of_charge)},
3248c2ecf20Sopenharmony_ci	{0x16, SMBUS_READ_WORD, offsetof(struct acpi_battery, state)},
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int acpi_manager_get_info(struct acpi_sbs *sbs)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	int result = 0;
3308c2ecf20Sopenharmony_ci	u16 battery_system_info;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER,
3338c2ecf20Sopenharmony_ci				 0x04, (u8 *)&battery_system_info);
3348c2ecf20Sopenharmony_ci	if (!result)
3358c2ecf20Sopenharmony_ci		sbs->batteries_supported = battery_system_info & 0x000f;
3368c2ecf20Sopenharmony_ci	return result;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int acpi_battery_get_info(struct acpi_battery *battery)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	int i, result = 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(info_readers); ++i) {
3448c2ecf20Sopenharmony_ci		result = acpi_smbus_read(battery->sbs->hc,
3458c2ecf20Sopenharmony_ci					 info_readers[i].mode,
3468c2ecf20Sopenharmony_ci					 ACPI_SBS_BATTERY,
3478c2ecf20Sopenharmony_ci					 info_readers[i].command,
3488c2ecf20Sopenharmony_ci					 (u8 *) battery +
3498c2ecf20Sopenharmony_ci						info_readers[i].offset);
3508c2ecf20Sopenharmony_ci		if (result)
3518c2ecf20Sopenharmony_ci			break;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	return result;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic int acpi_battery_get_state(struct acpi_battery *battery)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	int i, result = 0;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (battery->update_time &&
3618c2ecf20Sopenharmony_ci	    time_before(jiffies, battery->update_time +
3628c2ecf20Sopenharmony_ci				msecs_to_jiffies(cache_time)))
3638c2ecf20Sopenharmony_ci		return 0;
3648c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(state_readers); ++i) {
3658c2ecf20Sopenharmony_ci		result = acpi_smbus_read(battery->sbs->hc,
3668c2ecf20Sopenharmony_ci					 state_readers[i].mode,
3678c2ecf20Sopenharmony_ci					 ACPI_SBS_BATTERY,
3688c2ecf20Sopenharmony_ci					 state_readers[i].command,
3698c2ecf20Sopenharmony_ci					 (u8 *)battery +
3708c2ecf20Sopenharmony_ci						state_readers[i].offset);
3718c2ecf20Sopenharmony_ci		if (result)
3728c2ecf20Sopenharmony_ci			goto end;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci      end:
3758c2ecf20Sopenharmony_ci	battery->update_time = jiffies;
3768c2ecf20Sopenharmony_ci	return result;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int acpi_battery_get_alarm(struct acpi_battery *battery)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	return acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
3828c2ecf20Sopenharmony_ci				 ACPI_SBS_BATTERY, 0x01,
3838c2ecf20Sopenharmony_ci				 (u8 *)&battery->alarm_capacity);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic int acpi_battery_set_alarm(struct acpi_battery *battery)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs = battery->sbs;
3898c2ecf20Sopenharmony_ci	u16 value, sel = 1 << (battery->id + 12);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	int ret;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (sbs->manager_present) {
3958c2ecf20Sopenharmony_ci		ret = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER,
3968c2ecf20Sopenharmony_ci				0x01, (u8 *)&value);
3978c2ecf20Sopenharmony_ci		if (ret)
3988c2ecf20Sopenharmony_ci			goto end;
3998c2ecf20Sopenharmony_ci		if ((value & 0xf000) != sel) {
4008c2ecf20Sopenharmony_ci			value &= 0x0fff;
4018c2ecf20Sopenharmony_ci			value |= sel;
4028c2ecf20Sopenharmony_ci			ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD,
4038c2ecf20Sopenharmony_ci					 ACPI_SBS_MANAGER,
4048c2ecf20Sopenharmony_ci					 0x01, (u8 *)&value, 2);
4058c2ecf20Sopenharmony_ci			if (ret)
4068c2ecf20Sopenharmony_ci				goto end;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY,
4108c2ecf20Sopenharmony_ci				0x01, (u8 *)&battery->alarm_capacity, 2);
4118c2ecf20Sopenharmony_ci      end:
4128c2ecf20Sopenharmony_ci	return ret;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int acpi_ac_get_present(struct acpi_sbs *sbs)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	int result;
4188c2ecf20Sopenharmony_ci	u16 status;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER,
4218c2ecf20Sopenharmony_ci				 0x13, (u8 *) & status);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (result)
4248c2ecf20Sopenharmony_ci		return result;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/*
4278c2ecf20Sopenharmony_ci	 * The spec requires that bit 4 always be 1. If it's not set, assume
4288c2ecf20Sopenharmony_ci	 * that the implementation doesn't support an SBS charger.
4298c2ecf20Sopenharmony_ci	 *
4308c2ecf20Sopenharmony_ci	 * And on some MacBooks a status of 0xffff is always returned, no
4318c2ecf20Sopenharmony_ci	 * matter whether the charger is plugged in or not, which is also
4328c2ecf20Sopenharmony_ci	 * wrong, so ignore the SBS charger for those too.
4338c2ecf20Sopenharmony_ci	 */
4348c2ecf20Sopenharmony_ci	if (!((status >> 4) & 0x1) || status == 0xffff)
4358c2ecf20Sopenharmony_ci		return -ENODEV;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	sbs->charger_present = (status >> 15) & 0x1;
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic ssize_t acpi_battery_alarm_show(struct device *dev,
4428c2ecf20Sopenharmony_ci					struct device_attribute *attr,
4438c2ecf20Sopenharmony_ci					char *buf)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
4468c2ecf20Sopenharmony_ci	acpi_battery_get_alarm(battery);
4478c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", battery->alarm_capacity *
4488c2ecf20Sopenharmony_ci				acpi_battery_scale(battery) * 1000);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic ssize_t acpi_battery_alarm_store(struct device *dev,
4528c2ecf20Sopenharmony_ci					struct device_attribute *attr,
4538c2ecf20Sopenharmony_ci					const char *buf, size_t count)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	unsigned long x;
4568c2ecf20Sopenharmony_ci	struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
4578c2ecf20Sopenharmony_ci	if (sscanf(buf, "%lu\n", &x) == 1)
4588c2ecf20Sopenharmony_ci		battery->alarm_capacity = x /
4598c2ecf20Sopenharmony_ci			(1000 * acpi_battery_scale(battery));
4608c2ecf20Sopenharmony_ci	if (battery->present)
4618c2ecf20Sopenharmony_ci		acpi_battery_set_alarm(battery);
4628c2ecf20Sopenharmony_ci	return count;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic const struct device_attribute alarm_attr = {
4668c2ecf20Sopenharmony_ci	.attr = {.name = "alarm", .mode = 0644},
4678c2ecf20Sopenharmony_ci	.show = acpi_battery_alarm_show,
4688c2ecf20Sopenharmony_ci	.store = acpi_battery_alarm_store,
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------
4728c2ecf20Sopenharmony_ci                                 Driver Interface
4738c2ecf20Sopenharmony_ci   -------------------------------------------------------------------------- */
4748c2ecf20Sopenharmony_cistatic int acpi_battery_read(struct acpi_battery *battery)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	int result = 0, saved_present = battery->present;
4778c2ecf20Sopenharmony_ci	u16 state;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (battery->sbs->manager_present) {
4808c2ecf20Sopenharmony_ci		result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
4818c2ecf20Sopenharmony_ci				ACPI_SBS_MANAGER, 0x01, (u8 *)&state);
4828c2ecf20Sopenharmony_ci		if (!result)
4838c2ecf20Sopenharmony_ci			battery->present = state & (1 << battery->id);
4848c2ecf20Sopenharmony_ci		state &= 0x0fff;
4858c2ecf20Sopenharmony_ci		state |= 1 << (battery->id + 12);
4868c2ecf20Sopenharmony_ci		acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD,
4878c2ecf20Sopenharmony_ci				  ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2);
4888c2ecf20Sopenharmony_ci	} else if (battery->id == 0)
4898c2ecf20Sopenharmony_ci		battery->present = 1;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (result || !battery->present)
4928c2ecf20Sopenharmony_ci		return result;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (saved_present != battery->present) {
4958c2ecf20Sopenharmony_ci		battery->update_time = 0;
4968c2ecf20Sopenharmony_ci		result = acpi_battery_get_info(battery);
4978c2ecf20Sopenharmony_ci		if (result) {
4988c2ecf20Sopenharmony_ci			battery->present = 0;
4998c2ecf20Sopenharmony_ci			return result;
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci	result = acpi_battery_get_state(battery);
5038c2ecf20Sopenharmony_ci	if (result)
5048c2ecf20Sopenharmony_ci		battery->present = 0;
5058c2ecf20Sopenharmony_ci	return result;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/* Smart Battery */
5098c2ecf20Sopenharmony_cistatic int acpi_battery_add(struct acpi_sbs *sbs, int id)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	struct acpi_battery *battery = &sbs->battery[id];
5128c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = { .drv_data = battery, };
5138c2ecf20Sopenharmony_ci	int result;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	battery->id = id;
5168c2ecf20Sopenharmony_ci	battery->sbs = sbs;
5178c2ecf20Sopenharmony_ci	result = acpi_battery_read(battery);
5188c2ecf20Sopenharmony_ci	if (result)
5198c2ecf20Sopenharmony_ci		return result;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	sprintf(battery->name, ACPI_BATTERY_DIR_NAME, id);
5228c2ecf20Sopenharmony_ci	battery->bat_desc.name = battery->name;
5238c2ecf20Sopenharmony_ci	battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
5248c2ecf20Sopenharmony_ci	if (!acpi_battery_mode(battery)) {
5258c2ecf20Sopenharmony_ci		battery->bat_desc.properties = sbs_charge_battery_props;
5268c2ecf20Sopenharmony_ci		battery->bat_desc.num_properties =
5278c2ecf20Sopenharmony_ci		    ARRAY_SIZE(sbs_charge_battery_props);
5288c2ecf20Sopenharmony_ci	} else {
5298c2ecf20Sopenharmony_ci		battery->bat_desc.properties = sbs_energy_battery_props;
5308c2ecf20Sopenharmony_ci		battery->bat_desc.num_properties =
5318c2ecf20Sopenharmony_ci		    ARRAY_SIZE(sbs_energy_battery_props);
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci	battery->bat_desc.get_property = acpi_sbs_battery_get_property;
5348c2ecf20Sopenharmony_ci	battery->bat = power_supply_register(&sbs->device->dev,
5358c2ecf20Sopenharmony_ci					&battery->bat_desc, &psy_cfg);
5368c2ecf20Sopenharmony_ci	if (IS_ERR(battery->bat)) {
5378c2ecf20Sopenharmony_ci		result = PTR_ERR(battery->bat);
5388c2ecf20Sopenharmony_ci		battery->bat = NULL;
5398c2ecf20Sopenharmony_ci		goto end;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	result = device_create_file(&battery->bat->dev, &alarm_attr);
5438c2ecf20Sopenharmony_ci	if (result)
5448c2ecf20Sopenharmony_ci		goto end;
5458c2ecf20Sopenharmony_ci	battery->have_sysfs_alarm = 1;
5468c2ecf20Sopenharmony_ci      end:
5478c2ecf20Sopenharmony_ci	printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
5488c2ecf20Sopenharmony_ci	       ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
5498c2ecf20Sopenharmony_ci	       battery->name, battery->present ? "present" : "absent");
5508c2ecf20Sopenharmony_ci	return result;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic void acpi_battery_remove(struct acpi_sbs *sbs, int id)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct acpi_battery *battery = &sbs->battery[id];
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (battery->bat) {
5588c2ecf20Sopenharmony_ci		if (battery->have_sysfs_alarm)
5598c2ecf20Sopenharmony_ci			device_remove_file(&battery->bat->dev, &alarm_attr);
5608c2ecf20Sopenharmony_ci		power_supply_unregister(battery->bat);
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int acpi_charger_add(struct acpi_sbs *sbs)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	int result;
5678c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = { .drv_data = sbs, };
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	result = acpi_ac_get_present(sbs);
5708c2ecf20Sopenharmony_ci	if (result)
5718c2ecf20Sopenharmony_ci		goto end;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	sbs->charger_exists = 1;
5748c2ecf20Sopenharmony_ci	sbs->charger = power_supply_register(&sbs->device->dev,
5758c2ecf20Sopenharmony_ci					&acpi_sbs_charger_desc, &psy_cfg);
5768c2ecf20Sopenharmony_ci	if (IS_ERR(sbs->charger)) {
5778c2ecf20Sopenharmony_ci		result = PTR_ERR(sbs->charger);
5788c2ecf20Sopenharmony_ci		sbs->charger = NULL;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
5818c2ecf20Sopenharmony_ci	       ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
5828c2ecf20Sopenharmony_ci	       ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line");
5838c2ecf20Sopenharmony_ci      end:
5848c2ecf20Sopenharmony_ci	return result;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic void acpi_charger_remove(struct acpi_sbs *sbs)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	if (sbs->charger)
5908c2ecf20Sopenharmony_ci		power_supply_unregister(sbs->charger);
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic void acpi_sbs_callback(void *context)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	int id;
5968c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs = context;
5978c2ecf20Sopenharmony_ci	struct acpi_battery *bat;
5988c2ecf20Sopenharmony_ci	u8 saved_charger_state = sbs->charger_present;
5998c2ecf20Sopenharmony_ci	u8 saved_battery_state;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (sbs->charger_exists) {
6028c2ecf20Sopenharmony_ci		acpi_ac_get_present(sbs);
6038c2ecf20Sopenharmony_ci		if (sbs->charger_present != saved_charger_state)
6048c2ecf20Sopenharmony_ci			kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE);
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (sbs->manager_present) {
6088c2ecf20Sopenharmony_ci		for (id = 0; id < MAX_SBS_BAT; ++id) {
6098c2ecf20Sopenharmony_ci			if (!(sbs->batteries_supported & (1 << id)))
6108c2ecf20Sopenharmony_ci				continue;
6118c2ecf20Sopenharmony_ci			bat = &sbs->battery[id];
6128c2ecf20Sopenharmony_ci			saved_battery_state = bat->present;
6138c2ecf20Sopenharmony_ci			acpi_battery_read(bat);
6148c2ecf20Sopenharmony_ci			if (saved_battery_state == bat->present)
6158c2ecf20Sopenharmony_ci				continue;
6168c2ecf20Sopenharmony_ci			kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE);
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic int acpi_sbs_add(struct acpi_device *device)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs;
6248c2ecf20Sopenharmony_ci	int result = 0;
6258c2ecf20Sopenharmony_ci	int id;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
6288c2ecf20Sopenharmony_ci	if (!sbs) {
6298c2ecf20Sopenharmony_ci		result = -ENOMEM;
6308c2ecf20Sopenharmony_ci		goto end;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	mutex_init(&sbs->lock);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	sbs->hc = acpi_driver_data(device->parent);
6368c2ecf20Sopenharmony_ci	sbs->device = device;
6378c2ecf20Sopenharmony_ci	strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
6388c2ecf20Sopenharmony_ci	strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
6398c2ecf20Sopenharmony_ci	device->driver_data = sbs;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	result = acpi_charger_add(sbs);
6428c2ecf20Sopenharmony_ci	if (result && result != -ENODEV)
6438c2ecf20Sopenharmony_ci		goto end;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	result = 0;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (!x86_apple_machine) {
6488c2ecf20Sopenharmony_ci		result = acpi_manager_get_info(sbs);
6498c2ecf20Sopenharmony_ci		if (!result) {
6508c2ecf20Sopenharmony_ci			sbs->manager_present = 1;
6518c2ecf20Sopenharmony_ci			for (id = 0; id < MAX_SBS_BAT; ++id)
6528c2ecf20Sopenharmony_ci				if ((sbs->batteries_supported & (1 << id)))
6538c2ecf20Sopenharmony_ci					acpi_battery_add(sbs, id);
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	if (!sbs->manager_present)
6588c2ecf20Sopenharmony_ci		acpi_battery_add(sbs, 0);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs);
6618c2ecf20Sopenharmony_ci      end:
6628c2ecf20Sopenharmony_ci	if (result)
6638c2ecf20Sopenharmony_ci		acpi_sbs_remove(device);
6648c2ecf20Sopenharmony_ci	return result;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic int acpi_sbs_remove(struct acpi_device *device)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs;
6708c2ecf20Sopenharmony_ci	int id;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (!device)
6738c2ecf20Sopenharmony_ci		return -EINVAL;
6748c2ecf20Sopenharmony_ci	sbs = acpi_driver_data(device);
6758c2ecf20Sopenharmony_ci	if (!sbs)
6768c2ecf20Sopenharmony_ci		return -EINVAL;
6778c2ecf20Sopenharmony_ci	mutex_lock(&sbs->lock);
6788c2ecf20Sopenharmony_ci	acpi_smbus_unregister_callback(sbs->hc);
6798c2ecf20Sopenharmony_ci	for (id = 0; id < MAX_SBS_BAT; ++id)
6808c2ecf20Sopenharmony_ci		acpi_battery_remove(sbs, id);
6818c2ecf20Sopenharmony_ci	acpi_charger_remove(sbs);
6828c2ecf20Sopenharmony_ci	mutex_unlock(&sbs->lock);
6838c2ecf20Sopenharmony_ci	mutex_destroy(&sbs->lock);
6848c2ecf20Sopenharmony_ci	kfree(sbs);
6858c2ecf20Sopenharmony_ci	return 0;
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6898c2ecf20Sopenharmony_cistatic int acpi_sbs_resume(struct device *dev)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	struct acpi_sbs *sbs;
6928c2ecf20Sopenharmony_ci	if (!dev)
6938c2ecf20Sopenharmony_ci		return -EINVAL;
6948c2ecf20Sopenharmony_ci	sbs = to_acpi_device(dev)->driver_data;
6958c2ecf20Sopenharmony_ci	acpi_sbs_callback(sbs);
6968c2ecf20Sopenharmony_ci	return 0;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci#else
6998c2ecf20Sopenharmony_ci#define acpi_sbs_resume NULL
7008c2ecf20Sopenharmony_ci#endif
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_sbs_driver = {
7058c2ecf20Sopenharmony_ci	.name = "sbs",
7068c2ecf20Sopenharmony_ci	.class = ACPI_SBS_CLASS,
7078c2ecf20Sopenharmony_ci	.ids = sbs_device_ids,
7088c2ecf20Sopenharmony_ci	.ops = {
7098c2ecf20Sopenharmony_ci		.add = acpi_sbs_add,
7108c2ecf20Sopenharmony_ci		.remove = acpi_sbs_remove,
7118c2ecf20Sopenharmony_ci		},
7128c2ecf20Sopenharmony_ci	.drv.pm = &acpi_sbs_pm,
7138c2ecf20Sopenharmony_ci};
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic int __init acpi_sbs_init(void)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	int result = 0;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (acpi_disabled)
7208c2ecf20Sopenharmony_ci		return -ENODEV;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	result = acpi_bus_register_driver(&acpi_sbs_driver);
7238c2ecf20Sopenharmony_ci	if (result < 0)
7248c2ecf20Sopenharmony_ci		return -ENODEV;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	return 0;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_cistatic void __exit acpi_sbs_exit(void)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&acpi_sbs_driver);
7328c2ecf20Sopenharmony_ci	return;
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cimodule_init(acpi_sbs_init);
7368c2ecf20Sopenharmony_cimodule_exit(acpi_sbs_exit);
737