18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Supports for the power IC on the Surface 3 tablet.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright 2016-2018 Red Hat, Inc
68c2ecf20Sopenharmony_ci * (C) Copyright 2016-2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
78c2ecf20Sopenharmony_ci * (C) Copyright 2016 Stephen Just <stephenjust@gmail.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This driver has been reverse-engineered by parsing the DSDT of the Surface 3
108c2ecf20Sopenharmony_ci * and looking at the registers of the chips.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * The DSDT allowed to find out that:
138c2ecf20Sopenharmony_ci * - the driver is required for the ACPI BAT0 device to communicate to the chip
148c2ecf20Sopenharmony_ci *   through an operation region.
158c2ecf20Sopenharmony_ci * - the various defines for the operation region functions to communicate with
168c2ecf20Sopenharmony_ci *   this driver
178c2ecf20Sopenharmony_ci * - the DSM 3f99e367-6220-4955-8b0f-06ef2ae79412 allows to trigger ACPI
188c2ecf20Sopenharmony_ci *   events to BAT0 (the code is all available in the DSDT).
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Further findings regarding the 2 chips declared in the MSHW0011 are:
218c2ecf20Sopenharmony_ci * - there are 2 chips declared:
228c2ecf20Sopenharmony_ci *   . 0x22 seems to control the ADP1 line status (and probably the charger)
238c2ecf20Sopenharmony_ci *   . 0x55 controls the battery directly
248c2ecf20Sopenharmony_ci * - the battery chip uses a SMBus protocol (using plain SMBus allows non
258c2ecf20Sopenharmony_ci *   destructive commands):
268c2ecf20Sopenharmony_ci *   . the commands/registers used are in the range 0x00..0x7F
278c2ecf20Sopenharmony_ci *   . if bit 8 (0x80) is set in the SMBus command, the returned value is the
288c2ecf20Sopenharmony_ci *     same as when it is not set. There is a high chance this bit is the
298c2ecf20Sopenharmony_ci *     read/write
308c2ecf20Sopenharmony_ci *   . the various registers semantic as been deduced by observing the register
318c2ecf20Sopenharmony_ci *     dumps.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/acpi.h>
358c2ecf20Sopenharmony_ci#include <linux/bits.h>
368c2ecf20Sopenharmony_ci#include <linux/freezer.h>
378c2ecf20Sopenharmony_ci#include <linux/i2c.h>
388c2ecf20Sopenharmony_ci#include <linux/kernel.h>
398c2ecf20Sopenharmony_ci#include <linux/kthread.h>
408c2ecf20Sopenharmony_ci#include <linux/slab.h>
418c2ecf20Sopenharmony_ci#include <linux/types.h>
428c2ecf20Sopenharmony_ci#include <linux/uuid.h>
438c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define SURFACE_3_POLL_INTERVAL		(2 * HZ)
468c2ecf20Sopenharmony_ci#define SURFACE_3_STRLEN		10
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct mshw0011_data {
498c2ecf20Sopenharmony_ci	struct i2c_client	*adp1;
508c2ecf20Sopenharmony_ci	struct i2c_client	*bat0;
518c2ecf20Sopenharmony_ci	unsigned short		notify_mask;
528c2ecf20Sopenharmony_ci	struct task_struct	*poll_task;
538c2ecf20Sopenharmony_ci	bool			kthread_running;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	bool			charging;
568c2ecf20Sopenharmony_ci	bool			bat_charging;
578c2ecf20Sopenharmony_ci	u8			trip_point;
588c2ecf20Sopenharmony_ci	s32			full_capacity;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistruct mshw0011_handler_data {
628c2ecf20Sopenharmony_ci	struct acpi_connection_info	info;
638c2ecf20Sopenharmony_ci	struct i2c_client		*client;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistruct bix {
678c2ecf20Sopenharmony_ci	u32	revision;
688c2ecf20Sopenharmony_ci	u32	power_unit;
698c2ecf20Sopenharmony_ci	u32	design_capacity;
708c2ecf20Sopenharmony_ci	u32	last_full_charg_capacity;
718c2ecf20Sopenharmony_ci	u32	battery_technology;
728c2ecf20Sopenharmony_ci	u32	design_voltage;
738c2ecf20Sopenharmony_ci	u32	design_capacity_of_warning;
748c2ecf20Sopenharmony_ci	u32	design_capacity_of_low;
758c2ecf20Sopenharmony_ci	u32	cycle_count;
768c2ecf20Sopenharmony_ci	u32	measurement_accuracy;
778c2ecf20Sopenharmony_ci	u32	max_sampling_time;
788c2ecf20Sopenharmony_ci	u32	min_sampling_time;
798c2ecf20Sopenharmony_ci	u32	max_average_interval;
808c2ecf20Sopenharmony_ci	u32	min_average_interval;
818c2ecf20Sopenharmony_ci	u32	battery_capacity_granularity_1;
828c2ecf20Sopenharmony_ci	u32	battery_capacity_granularity_2;
838c2ecf20Sopenharmony_ci	char	model[SURFACE_3_STRLEN];
848c2ecf20Sopenharmony_ci	char	serial[SURFACE_3_STRLEN];
858c2ecf20Sopenharmony_ci	char	type[SURFACE_3_STRLEN];
868c2ecf20Sopenharmony_ci	char	OEM[SURFACE_3_STRLEN];
878c2ecf20Sopenharmony_ci} __packed;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct bst {
908c2ecf20Sopenharmony_ci	u32	battery_state;
918c2ecf20Sopenharmony_ci	s32	battery_present_rate;
928c2ecf20Sopenharmony_ci	u32	battery_remaining_capacity;
938c2ecf20Sopenharmony_ci	u32	battery_present_voltage;
948c2ecf20Sopenharmony_ci} __packed;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct gsb_command {
978c2ecf20Sopenharmony_ci	u8	arg0;
988c2ecf20Sopenharmony_ci	u8	arg1;
998c2ecf20Sopenharmony_ci	u8	arg2;
1008c2ecf20Sopenharmony_ci} __packed;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct gsb_buffer {
1038c2ecf20Sopenharmony_ci	u8	status;
1048c2ecf20Sopenharmony_ci	u8	len;
1058c2ecf20Sopenharmony_ci	u8	ret;
1068c2ecf20Sopenharmony_ci	union {
1078c2ecf20Sopenharmony_ci		struct gsb_command	cmd;
1088c2ecf20Sopenharmony_ci		struct bst		bst;
1098c2ecf20Sopenharmony_ci		struct bix		bix;
1108c2ecf20Sopenharmony_ci	} __packed;
1118c2ecf20Sopenharmony_ci} __packed;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define ACPI_BATTERY_STATE_DISCHARGING	BIT(0)
1148c2ecf20Sopenharmony_ci#define ACPI_BATTERY_STATE_CHARGING	BIT(1)
1158c2ecf20Sopenharmony_ci#define ACPI_BATTERY_STATE_CRITICAL	BIT(2)
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#define MSHW0011_CMD_DEST_BAT0		0x01
1188c2ecf20Sopenharmony_ci#define MSHW0011_CMD_DEST_ADP1		0x03
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_STA		0x01
1218c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_BIX		0x02
1228c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_BCT		0x03
1238c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_BTM		0x04
1248c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_BST		0x05
1258c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_BTP		0x06
1268c2ecf20Sopenharmony_ci#define MSHW0011_CMD_ADP1_PSR		0x07
1278c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_PSOC		0x09
1288c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_PMAX		0x0a
1298c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_PSRC		0x0b
1308c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_CHGI		0x0c
1318c2ecf20Sopenharmony_ci#define MSHW0011_CMD_BAT0_ARTG		0x0d
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#define MSHW0011_NOTIFY_GET_VERSION	0x00
1348c2ecf20Sopenharmony_ci#define MSHW0011_NOTIFY_ADP1		0x01
1358c2ecf20Sopenharmony_ci#define MSHW0011_NOTIFY_BAT0_BST	0x02
1368c2ecf20Sopenharmony_ci#define MSHW0011_NOTIFY_BAT0_BIX	0x05
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci#define MSHW0011_ADP1_REG_PSR		0x04
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_CAPACITY		0x0c
1418c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_FULL_CHG_CAPACITY	0x0e
1428c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_DESIGN_CAPACITY	0x40
1438c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_VOLTAGE	0x08
1448c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_RATE		0x14
1458c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_OEM		0x45
1468c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_TYPE		0x4e
1478c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_SERIAL_NO	0x56
1488c2ecf20Sopenharmony_ci#define MSHW0011_BAT0_REG_CYCLE_CNT	0x6e
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#define MSHW0011_EV_2_5_MASK		GENMASK(8, 0)
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* 3f99e367-6220-4955-8b0f-06ef2ae79412 */
1538c2ecf20Sopenharmony_cistatic const guid_t mshw0011_guid =
1548c2ecf20Sopenharmony_ci	GUID_INIT(0x3F99E367, 0x6220, 0x4955, 0x8B, 0x0F, 0x06, 0xEF,
1558c2ecf20Sopenharmony_ci		  0x2A, 0xE7, 0x94, 0x12);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int
1588c2ecf20Sopenharmony_cimshw0011_notify(struct mshw0011_data *cdata, u8 arg1, u8 arg2,
1598c2ecf20Sopenharmony_ci		unsigned int *ret_value)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	union acpi_object *obj;
1628c2ecf20Sopenharmony_ci	struct acpi_device *adev;
1638c2ecf20Sopenharmony_ci	acpi_handle handle;
1648c2ecf20Sopenharmony_ci	unsigned int i;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	handle = ACPI_HANDLE(&cdata->adp1->dev);
1678c2ecf20Sopenharmony_ci	if (!handle || acpi_bus_get_device(handle, &adev))
1688c2ecf20Sopenharmony_ci		return -ENODEV;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	obj = acpi_evaluate_dsm_typed(handle, &mshw0011_guid, arg1, arg2, NULL,
1718c2ecf20Sopenharmony_ci				      ACPI_TYPE_BUFFER);
1728c2ecf20Sopenharmony_ci	if (!obj) {
1738c2ecf20Sopenharmony_ci		dev_err(&cdata->adp1->dev, "device _DSM execution failed\n");
1748c2ecf20Sopenharmony_ci		return -ENODEV;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	*ret_value = 0;
1788c2ecf20Sopenharmony_ci	for (i = 0; i < obj->buffer.length; i++)
1798c2ecf20Sopenharmony_ci		*ret_value |= obj->buffer.pointer[i] << (i * 8);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	ACPI_FREE(obj);
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic const struct bix default_bix = {
1868c2ecf20Sopenharmony_ci	.revision = 0x00,
1878c2ecf20Sopenharmony_ci	.power_unit = 0x01,
1888c2ecf20Sopenharmony_ci	.design_capacity = 0x1dca,
1898c2ecf20Sopenharmony_ci	.last_full_charg_capacity = 0x1dca,
1908c2ecf20Sopenharmony_ci	.battery_technology = 0x01,
1918c2ecf20Sopenharmony_ci	.design_voltage = 0x10df,
1928c2ecf20Sopenharmony_ci	.design_capacity_of_warning = 0x8f,
1938c2ecf20Sopenharmony_ci	.design_capacity_of_low = 0x47,
1948c2ecf20Sopenharmony_ci	.cycle_count = 0xffffffff,
1958c2ecf20Sopenharmony_ci	.measurement_accuracy = 0x00015f90,
1968c2ecf20Sopenharmony_ci	.max_sampling_time = 0x03e8,
1978c2ecf20Sopenharmony_ci	.min_sampling_time = 0x03e8,
1988c2ecf20Sopenharmony_ci	.max_average_interval = 0x03e8,
1998c2ecf20Sopenharmony_ci	.min_average_interval = 0x03e8,
2008c2ecf20Sopenharmony_ci	.battery_capacity_granularity_1 = 0x45,
2018c2ecf20Sopenharmony_ci	.battery_capacity_granularity_2 = 0x11,
2028c2ecf20Sopenharmony_ci	.model = "P11G8M",
2038c2ecf20Sopenharmony_ci	.serial = "",
2048c2ecf20Sopenharmony_ci	.type = "LION",
2058c2ecf20Sopenharmony_ci	.OEM = "",
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int mshw0011_bix(struct mshw0011_data *cdata, struct bix *bix)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct i2c_client *client = cdata->bat0;
2118c2ecf20Sopenharmony_ci	char buf[SURFACE_3_STRLEN];
2128c2ecf20Sopenharmony_ci	int ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	*bix = default_bix;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* get design capacity */
2178c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_word_data(client,
2188c2ecf20Sopenharmony_ci				       MSHW0011_BAT0_REG_DESIGN_CAPACITY);
2198c2ecf20Sopenharmony_ci	if (ret < 0) {
2208c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Error reading design capacity: %d\n",
2218c2ecf20Sopenharmony_ci			ret);
2228c2ecf20Sopenharmony_ci		return ret;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	bix->design_capacity = ret;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* get last full charge capacity */
2278c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_word_data(client,
2288c2ecf20Sopenharmony_ci				       MSHW0011_BAT0_REG_FULL_CHG_CAPACITY);
2298c2ecf20Sopenharmony_ci	if (ret < 0) {
2308c2ecf20Sopenharmony_ci		dev_err(&client->dev,
2318c2ecf20Sopenharmony_ci			"Error reading last full charge capacity: %d\n", ret);
2328c2ecf20Sopenharmony_ci		return ret;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	bix->last_full_charg_capacity = ret;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/*
2378c2ecf20Sopenharmony_ci	 * Get serial number, on some devices (with unofficial replacement
2388c2ecf20Sopenharmony_ci	 * battery?) reading any of the serial number range addresses gets
2398c2ecf20Sopenharmony_ci	 * nacked in this case just leave the serial number empty.
2408c2ecf20Sopenharmony_ci	 */
2418c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_SERIAL_NO,
2428c2ecf20Sopenharmony_ci					    sizeof(buf), buf);
2438c2ecf20Sopenharmony_ci	if (ret == -EREMOTEIO) {
2448c2ecf20Sopenharmony_ci		/* no serial number available */
2458c2ecf20Sopenharmony_ci	} else if (ret != sizeof(buf)) {
2468c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Error reading serial no: %d\n", ret);
2478c2ecf20Sopenharmony_ci		return ret;
2488c2ecf20Sopenharmony_ci	} else {
2498c2ecf20Sopenharmony_ci		snprintf(bix->serial, ARRAY_SIZE(bix->serial), "%3pE%6pE", buf + 7, buf);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* get cycle count */
2538c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CYCLE_CNT);
2548c2ecf20Sopenharmony_ci	if (ret < 0) {
2558c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Error reading cycle count: %d\n", ret);
2568c2ecf20Sopenharmony_ci		return ret;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci	bix->cycle_count = ret;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* get OEM name */
2618c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_OEM,
2628c2ecf20Sopenharmony_ci					    4, buf);
2638c2ecf20Sopenharmony_ci	if (ret != 4) {
2648c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Error reading cycle count: %d\n", ret);
2658c2ecf20Sopenharmony_ci		return ret;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	snprintf(bix->OEM, ARRAY_SIZE(bix->OEM), "%3pE", buf);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int mshw0011_bst(struct mshw0011_data *cdata, struct bst *bst)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct i2c_client *client = cdata->bat0;
2758c2ecf20Sopenharmony_ci	int rate, capacity, voltage, state;
2768c2ecf20Sopenharmony_ci	s16 tmp;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	rate = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_RATE);
2798c2ecf20Sopenharmony_ci	if (rate < 0)
2808c2ecf20Sopenharmony_ci		return rate;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	capacity = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CAPACITY);
2838c2ecf20Sopenharmony_ci	if (capacity < 0)
2848c2ecf20Sopenharmony_ci		return capacity;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	voltage = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_VOLTAGE);
2878c2ecf20Sopenharmony_ci	if (voltage < 0)
2888c2ecf20Sopenharmony_ci		return voltage;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	tmp = rate;
2918c2ecf20Sopenharmony_ci	bst->battery_present_rate = abs((s32)tmp);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	state = 0;
2948c2ecf20Sopenharmony_ci	if ((s32) tmp > 0)
2958c2ecf20Sopenharmony_ci		state |= ACPI_BATTERY_STATE_CHARGING;
2968c2ecf20Sopenharmony_ci	else if ((s32) tmp < 0)
2978c2ecf20Sopenharmony_ci		state |= ACPI_BATTERY_STATE_DISCHARGING;
2988c2ecf20Sopenharmony_ci	bst->battery_state = state;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	bst->battery_remaining_capacity = capacity;
3018c2ecf20Sopenharmony_ci	bst->battery_present_voltage = voltage;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int mshw0011_adp_psr(struct mshw0011_data *cdata)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	return i2c_smbus_read_byte_data(cdata->adp1, MSHW0011_ADP1_REG_PSR);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int mshw0011_isr(struct mshw0011_data *cdata)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct bst bst;
3148c2ecf20Sopenharmony_ci	struct bix bix;
3158c2ecf20Sopenharmony_ci	int ret;
3168c2ecf20Sopenharmony_ci	bool status, bat_status;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	ret = mshw0011_adp_psr(cdata);
3198c2ecf20Sopenharmony_ci	if (ret < 0)
3208c2ecf20Sopenharmony_ci		return ret;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	status = ret;
3238c2ecf20Sopenharmony_ci	if (status != cdata->charging)
3248c2ecf20Sopenharmony_ci		mshw0011_notify(cdata, cdata->notify_mask,
3258c2ecf20Sopenharmony_ci				MSHW0011_NOTIFY_ADP1, &ret);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	cdata->charging = status;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ret = mshw0011_bst(cdata, &bst);
3308c2ecf20Sopenharmony_ci	if (ret < 0)
3318c2ecf20Sopenharmony_ci		return ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	bat_status = bst.battery_state;
3348c2ecf20Sopenharmony_ci	if (bat_status != cdata->bat_charging)
3358c2ecf20Sopenharmony_ci		mshw0011_notify(cdata, cdata->notify_mask,
3368c2ecf20Sopenharmony_ci				MSHW0011_NOTIFY_BAT0_BST, &ret);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	cdata->bat_charging = bat_status;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ret = mshw0011_bix(cdata, &bix);
3418c2ecf20Sopenharmony_ci	if (ret < 0)
3428c2ecf20Sopenharmony_ci		return ret;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (bix.last_full_charg_capacity != cdata->full_capacity)
3458c2ecf20Sopenharmony_ci		mshw0011_notify(cdata, cdata->notify_mask,
3468c2ecf20Sopenharmony_ci				MSHW0011_NOTIFY_BAT0_BIX, &ret);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	cdata->full_capacity = bix.last_full_charg_capacity;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int mshw0011_poll_task(void *data)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct mshw0011_data *cdata = data;
3568c2ecf20Sopenharmony_ci	int ret = 0;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	cdata->kthread_running = true;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	set_freezable();
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
3638c2ecf20Sopenharmony_ci		schedule_timeout_interruptible(SURFACE_3_POLL_INTERVAL);
3648c2ecf20Sopenharmony_ci		try_to_freeze();
3658c2ecf20Sopenharmony_ci		ret = mshw0011_isr(data);
3668c2ecf20Sopenharmony_ci		if (ret)
3678c2ecf20Sopenharmony_ci			break;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	cdata->kthread_running = false;
3718c2ecf20Sopenharmony_ci	return ret;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic acpi_status
3758c2ecf20Sopenharmony_cimshw0011_space_handler(u32 function, acpi_physical_address command,
3768c2ecf20Sopenharmony_ci			u32 bits, u64 *value64,
3778c2ecf20Sopenharmony_ci			void *handler_context, void *region_context)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct gsb_buffer *gsb = (struct gsb_buffer *)value64;
3808c2ecf20Sopenharmony_ci	struct mshw0011_handler_data *data = handler_context;
3818c2ecf20Sopenharmony_ci	struct acpi_connection_info *info = &data->info;
3828c2ecf20Sopenharmony_ci	struct acpi_resource_i2c_serialbus *sb;
3838c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3848c2ecf20Sopenharmony_ci	struct mshw0011_data *cdata = i2c_get_clientdata(client);
3858c2ecf20Sopenharmony_ci	struct acpi_resource *ares;
3868c2ecf20Sopenharmony_ci	u32 accessor_type = function >> 16;
3878c2ecf20Sopenharmony_ci	acpi_status ret;
3888c2ecf20Sopenharmony_ci	int status = 1;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
3918c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(ret))
3928c2ecf20Sopenharmony_ci		return ret;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
3958c2ecf20Sopenharmony_ci		ret = AE_BAD_PARAMETER;
3968c2ecf20Sopenharmony_ci		goto err;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	sb = &ares->data.i2c_serial_bus;
4008c2ecf20Sopenharmony_ci	if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
4018c2ecf20Sopenharmony_ci		ret = AE_BAD_PARAMETER;
4028c2ecf20Sopenharmony_ci		goto err;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) {
4068c2ecf20Sopenharmony_ci		ret = AE_BAD_PARAMETER;
4078c2ecf20Sopenharmony_ci		goto err;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (gsb->cmd.arg0 == MSHW0011_CMD_DEST_ADP1 &&
4118c2ecf20Sopenharmony_ci	    gsb->cmd.arg1 == MSHW0011_CMD_ADP1_PSR) {
4128c2ecf20Sopenharmony_ci		status = mshw0011_adp_psr(cdata);
4138c2ecf20Sopenharmony_ci		if (status >= 0) {
4148c2ecf20Sopenharmony_ci			ret = AE_OK;
4158c2ecf20Sopenharmony_ci			goto out;
4168c2ecf20Sopenharmony_ci		} else {
4178c2ecf20Sopenharmony_ci			ret = AE_ERROR;
4188c2ecf20Sopenharmony_ci			goto err;
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (gsb->cmd.arg0 != MSHW0011_CMD_DEST_BAT0) {
4238c2ecf20Sopenharmony_ci		ret = AE_BAD_PARAMETER;
4248c2ecf20Sopenharmony_ci		goto err;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	switch (gsb->cmd.arg1) {
4288c2ecf20Sopenharmony_ci	case MSHW0011_CMD_BAT0_STA:
4298c2ecf20Sopenharmony_ci		break;
4308c2ecf20Sopenharmony_ci	case MSHW0011_CMD_BAT0_BIX:
4318c2ecf20Sopenharmony_ci		ret = mshw0011_bix(cdata, &gsb->bix);
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	case MSHW0011_CMD_BAT0_BTP:
4348c2ecf20Sopenharmony_ci		cdata->trip_point = gsb->cmd.arg2;
4358c2ecf20Sopenharmony_ci		break;
4368c2ecf20Sopenharmony_ci	case MSHW0011_CMD_BAT0_BST:
4378c2ecf20Sopenharmony_ci		ret = mshw0011_bst(cdata, &gsb->bst);
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	default:
4408c2ecf20Sopenharmony_ci		dev_info(&cdata->bat0->dev, "command(0x%02x) is not supported.\n", gsb->cmd.arg1);
4418c2ecf20Sopenharmony_ci		ret = AE_BAD_PARAMETER;
4428c2ecf20Sopenharmony_ci		goto err;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci out:
4468c2ecf20Sopenharmony_ci	gsb->ret = status;
4478c2ecf20Sopenharmony_ci	gsb->status = 0;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci err:
4508c2ecf20Sopenharmony_ci	ACPI_FREE(ares);
4518c2ecf20Sopenharmony_ci	return ret;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int mshw0011_install_space_handler(struct i2c_client *client)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	acpi_handle handle;
4578c2ecf20Sopenharmony_ci	struct mshw0011_handler_data *data;
4588c2ecf20Sopenharmony_ci	acpi_status status;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	handle = ACPI_HANDLE(&client->dev);
4618c2ecf20Sopenharmony_ci	if (!handle)
4628c2ecf20Sopenharmony_ci		return -ENODEV;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(struct mshw0011_handler_data),
4658c2ecf20Sopenharmony_ci			    GFP_KERNEL);
4668c2ecf20Sopenharmony_ci	if (!data)
4678c2ecf20Sopenharmony_ci		return -ENOMEM;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	data->client = client;
4708c2ecf20Sopenharmony_ci	status = acpi_bus_attach_private_data(handle, (void *)data);
4718c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
4728c2ecf20Sopenharmony_ci		kfree(data);
4738c2ecf20Sopenharmony_ci		return -ENOMEM;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	status = acpi_install_address_space_handler(handle,
4778c2ecf20Sopenharmony_ci				ACPI_ADR_SPACE_GSBUS,
4788c2ecf20Sopenharmony_ci				&mshw0011_space_handler,
4798c2ecf20Sopenharmony_ci				NULL,
4808c2ecf20Sopenharmony_ci				data);
4818c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
4828c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Error installing i2c space handler\n");
4838c2ecf20Sopenharmony_ci		acpi_bus_detach_private_data(handle);
4848c2ecf20Sopenharmony_ci		kfree(data);
4858c2ecf20Sopenharmony_ci		return -ENOMEM;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	acpi_walk_dep_device_list(handle);
4898c2ecf20Sopenharmony_ci	return 0;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic void mshw0011_remove_space_handler(struct i2c_client *client)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct mshw0011_handler_data *data;
4958c2ecf20Sopenharmony_ci	acpi_handle handle;
4968c2ecf20Sopenharmony_ci	acpi_status status;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	handle = ACPI_HANDLE(&client->dev);
4998c2ecf20Sopenharmony_ci	if (!handle)
5008c2ecf20Sopenharmony_ci		return;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	acpi_remove_address_space_handler(handle,
5038c2ecf20Sopenharmony_ci				ACPI_ADR_SPACE_GSBUS,
5048c2ecf20Sopenharmony_ci				&mshw0011_space_handler);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	status = acpi_bus_get_private_data(handle, (void **)&data);
5078c2ecf20Sopenharmony_ci	if (ACPI_SUCCESS(status))
5088c2ecf20Sopenharmony_ci		kfree(data);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	acpi_bus_detach_private_data(handle);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic int mshw0011_probe(struct i2c_client *client)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct i2c_board_info board_info;
5168c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
5178c2ecf20Sopenharmony_ci	struct i2c_client *bat0;
5188c2ecf20Sopenharmony_ci	struct mshw0011_data *data;
5198c2ecf20Sopenharmony_ci	int error, mask;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
5228c2ecf20Sopenharmony_ci	if (!data)
5238c2ecf20Sopenharmony_ci		return -ENOMEM;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	data->adp1 = client;
5268c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, data);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	memset(&board_info, 0, sizeof(board_info));
5298c2ecf20Sopenharmony_ci	strlcpy(board_info.type, "MSHW0011-bat0", I2C_NAME_SIZE);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	bat0 = i2c_acpi_new_device(dev, 1, &board_info);
5328c2ecf20Sopenharmony_ci	if (IS_ERR(bat0))
5338c2ecf20Sopenharmony_ci		return PTR_ERR(bat0);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	data->bat0 = bat0;
5368c2ecf20Sopenharmony_ci	i2c_set_clientdata(bat0, data);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	error = mshw0011_notify(data, 1, MSHW0011_NOTIFY_GET_VERSION, &mask);
5398c2ecf20Sopenharmony_ci	if (error)
5408c2ecf20Sopenharmony_ci		goto out_err;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	data->notify_mask = mask == MSHW0011_EV_2_5_MASK;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	data->poll_task = kthread_run(mshw0011_poll_task, data, "mshw0011_adp");
5458c2ecf20Sopenharmony_ci	if (IS_ERR(data->poll_task)) {
5468c2ecf20Sopenharmony_ci		error = PTR_ERR(data->poll_task);
5478c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Unable to run kthread err %d\n", error);
5488c2ecf20Sopenharmony_ci		goto out_err;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	error = mshw0011_install_space_handler(client);
5528c2ecf20Sopenharmony_ci	if (error)
5538c2ecf20Sopenharmony_ci		goto out_err;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ciout_err:
5588c2ecf20Sopenharmony_ci	if (data->kthread_running)
5598c2ecf20Sopenharmony_ci		kthread_stop(data->poll_task);
5608c2ecf20Sopenharmony_ci	i2c_unregister_device(data->bat0);
5618c2ecf20Sopenharmony_ci	return error;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int mshw0011_remove(struct i2c_client *client)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct mshw0011_data *cdata = i2c_get_clientdata(client);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	mshw0011_remove_space_handler(client);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (cdata->kthread_running)
5718c2ecf20Sopenharmony_ci		kthread_stop(cdata->poll_task);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	i2c_unregister_device(cdata->bat0);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return 0;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic const struct acpi_device_id mshw0011_acpi_match[] = {
5798c2ecf20Sopenharmony_ci	{ "MSHW0011", 0 },
5808c2ecf20Sopenharmony_ci	{ }
5818c2ecf20Sopenharmony_ci};
5828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mshw0011_acpi_match);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic struct i2c_driver mshw0011_driver = {
5858c2ecf20Sopenharmony_ci	.probe_new = mshw0011_probe,
5868c2ecf20Sopenharmony_ci	.remove = mshw0011_remove,
5878c2ecf20Sopenharmony_ci	.driver = {
5888c2ecf20Sopenharmony_ci		.name = "mshw0011",
5898c2ecf20Sopenharmony_ci		.acpi_match_table = mshw0011_acpi_match,
5908c2ecf20Sopenharmony_ci	},
5918c2ecf20Sopenharmony_ci};
5928c2ecf20Sopenharmony_cimodule_i2c_driver(mshw0011_driver);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
5958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("mshw0011 driver");
5968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
597