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