1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Supports for the power IC on the Surface 3 tablet. |
4 | * |
5 | * (C) Copyright 2016-2018 Red Hat, Inc |
6 | * (C) Copyright 2016-2018 Benjamin Tissoires <benjamin.tissoires@gmail.com> |
7 | * (C) Copyright 2016 Stephen Just <stephenjust@gmail.com> |
8 | * |
9 | * This driver has been reverse-engineered by parsing the DSDT of the Surface 3 |
10 | * and looking at the registers of the chips. |
11 | * |
12 | * The DSDT allowed to find out that: |
13 | * - the driver is required for the ACPI BAT0 device to communicate to the chip |
14 | * through an operation region. |
15 | * - the various defines for the operation region functions to communicate with |
16 | * this driver |
17 | * - the DSM 3f99e367-6220-4955-8b0f-06ef2ae79412 allows to trigger ACPI |
18 | * events to BAT0 (the code is all available in the DSDT). |
19 | * |
20 | * Further findings regarding the 2 chips declared in the MSHW0011 are: |
21 | * - there are 2 chips declared: |
22 | * . 0x22 seems to control the ADP1 line status (and probably the charger) |
23 | * . 0x55 controls the battery directly |
24 | * - the battery chip uses a SMBus protocol (using plain SMBus allows non |
25 | * destructive commands): |
26 | * . the commands/registers used are in the range 0x00..0x7F |
27 | * . if bit 8 (0x80) is set in the SMBus command, the returned value is the |
28 | * same as when it is not set. There is a high chance this bit is the |
29 | * read/write |
30 | * . the various registers semantic as been deduced by observing the register |
31 | * dumps. |
32 | */ |
33 | |
34 | #include <linux/acpi.h> |
35 | #include <linux/bits.h> |
36 | #include <linux/freezer.h> |
37 | #include <linux/i2c.h> |
38 | #include <linux/kernel.h> |
39 | #include <linux/kthread.h> |
40 | #include <linux/slab.h> |
41 | #include <linux/types.h> |
42 | #include <linux/uuid.h> |
43 | #include <asm/unaligned.h> |
44 | |
45 | #define SURFACE_3_POLL_INTERVAL (2 * HZ) |
46 | #define SURFACE_3_STRLEN 10 |
47 | |
48 | struct mshw0011_data { |
49 | struct i2c_client *adp1; |
50 | struct i2c_client *bat0; |
51 | unsigned short notify_mask; |
52 | struct task_struct *poll_task; |
53 | bool kthread_running; |
54 | |
55 | bool charging; |
56 | bool bat_charging; |
57 | u8 trip_point; |
58 | s32 full_capacity; |
59 | }; |
60 | |
61 | struct mshw0011_handler_data { |
62 | struct acpi_connection_info info; |
63 | struct i2c_client *client; |
64 | }; |
65 | |
66 | struct bix { |
67 | u32 revision; |
68 | u32 power_unit; |
69 | u32 design_capacity; |
70 | u32 last_full_charg_capacity; |
71 | u32 battery_technology; |
72 | u32 design_voltage; |
73 | u32 design_capacity_of_warning; |
74 | u32 design_capacity_of_low; |
75 | u32 cycle_count; |
76 | u32 measurement_accuracy; |
77 | u32 max_sampling_time; |
78 | u32 min_sampling_time; |
79 | u32 max_average_interval; |
80 | u32 min_average_interval; |
81 | u32 battery_capacity_granularity_1; |
82 | u32 battery_capacity_granularity_2; |
83 | char model[SURFACE_3_STRLEN]; |
84 | char serial[SURFACE_3_STRLEN]; |
85 | char type[SURFACE_3_STRLEN]; |
86 | char OEM[SURFACE_3_STRLEN]; |
87 | } __packed; |
88 | |
89 | struct bst { |
90 | u32 battery_state; |
91 | s32 battery_present_rate; |
92 | u32 battery_remaining_capacity; |
93 | u32 battery_present_voltage; |
94 | } __packed; |
95 | |
96 | struct gsb_command { |
97 | u8 arg0; |
98 | u8 arg1; |
99 | u8 arg2; |
100 | } __packed; |
101 | |
102 | struct gsb_buffer { |
103 | u8 status; |
104 | u8 len; |
105 | u8 ret; |
106 | union { |
107 | struct gsb_command cmd; |
108 | struct bst bst; |
109 | struct bix bix; |
110 | } __packed; |
111 | } __packed; |
112 | |
113 | #define ACPI_BATTERY_STATE_DISCHARGING BIT(0) |
114 | #define ACPI_BATTERY_STATE_CHARGING BIT(1) |
115 | #define ACPI_BATTERY_STATE_CRITICAL BIT(2) |
116 | |
117 | #define MSHW0011_CMD_DEST_BAT0 0x01 |
118 | #define MSHW0011_CMD_DEST_ADP1 0x03 |
119 | |
120 | #define MSHW0011_CMD_BAT0_STA 0x01 |
121 | #define MSHW0011_CMD_BAT0_BIX 0x02 |
122 | #define MSHW0011_CMD_BAT0_BCT 0x03 |
123 | #define MSHW0011_CMD_BAT0_BTM 0x04 |
124 | #define MSHW0011_CMD_BAT0_BST 0x05 |
125 | #define MSHW0011_CMD_BAT0_BTP 0x06 |
126 | #define MSHW0011_CMD_ADP1_PSR 0x07 |
127 | #define MSHW0011_CMD_BAT0_PSOC 0x09 |
128 | #define MSHW0011_CMD_BAT0_PMAX 0x0a |
129 | #define MSHW0011_CMD_BAT0_PSRC 0x0b |
130 | #define MSHW0011_CMD_BAT0_CHGI 0x0c |
131 | #define MSHW0011_CMD_BAT0_ARTG 0x0d |
132 | |
133 | #define MSHW0011_NOTIFY_GET_VERSION 0x00 |
134 | #define MSHW0011_NOTIFY_ADP1 0x01 |
135 | #define MSHW0011_NOTIFY_BAT0_BST 0x02 |
136 | #define MSHW0011_NOTIFY_BAT0_BIX 0x05 |
137 | |
138 | #define MSHW0011_ADP1_REG_PSR 0x04 |
139 | |
140 | #define MSHW0011_BAT0_REG_CAPACITY 0x0c |
141 | #define MSHW0011_BAT0_REG_FULL_CHG_CAPACITY 0x0e |
142 | #define MSHW0011_BAT0_REG_DESIGN_CAPACITY 0x40 |
143 | #define MSHW0011_BAT0_REG_VOLTAGE 0x08 |
144 | #define MSHW0011_BAT0_REG_RATE 0x14 |
145 | #define MSHW0011_BAT0_REG_OEM 0x45 |
146 | #define MSHW0011_BAT0_REG_TYPE 0x4e |
147 | #define MSHW0011_BAT0_REG_SERIAL_NO 0x56 |
148 | #define MSHW0011_BAT0_REG_CYCLE_CNT 0x6e |
149 | |
150 | #define MSHW0011_EV_2_5_MASK GENMASK(8, 0) |
151 | |
152 | /* 3f99e367-6220-4955-8b0f-06ef2ae79412 */ |
153 | static const guid_t mshw0011_guid = |
154 | GUID_INIT(0x3F99E367, 0x6220, 0x4955, 0x8B, 0x0F, 0x06, 0xEF, |
155 | 0x2A, 0xE7, 0x94, 0x12); |
156 | |
157 | static int |
158 | mshw0011_notify(struct mshw0011_data *cdata, u8 arg1, u8 arg2, |
159 | unsigned int *ret_value) |
160 | { |
161 | union acpi_object *obj; |
162 | acpi_handle handle; |
163 | unsigned int i; |
164 | |
165 | handle = ACPI_HANDLE(&cdata->adp1->dev); |
166 | if (!handle) |
167 | return -ENODEV; |
168 | |
169 | obj = acpi_evaluate_dsm_typed(handle, guid: &mshw0011_guid, rev: arg1, func: arg2, NULL, |
170 | ACPI_TYPE_BUFFER); |
171 | if (!obj) { |
172 | dev_err(&cdata->adp1->dev, "device _DSM execution failed\n" ); |
173 | return -ENODEV; |
174 | } |
175 | |
176 | *ret_value = 0; |
177 | for (i = 0; i < obj->buffer.length; i++) |
178 | *ret_value |= obj->buffer.pointer[i] << (i * 8); |
179 | |
180 | ACPI_FREE(obj); |
181 | return 0; |
182 | } |
183 | |
184 | static const struct bix default_bix = { |
185 | .revision = 0x00, |
186 | .power_unit = 0x01, |
187 | .design_capacity = 0x1dca, |
188 | .last_full_charg_capacity = 0x1dca, |
189 | .battery_technology = 0x01, |
190 | .design_voltage = 0x10df, |
191 | .design_capacity_of_warning = 0x8f, |
192 | .design_capacity_of_low = 0x47, |
193 | .cycle_count = 0xffffffff, |
194 | .measurement_accuracy = 0x00015f90, |
195 | .max_sampling_time = 0x03e8, |
196 | .min_sampling_time = 0x03e8, |
197 | .max_average_interval = 0x03e8, |
198 | .min_average_interval = 0x03e8, |
199 | .battery_capacity_granularity_1 = 0x45, |
200 | .battery_capacity_granularity_2 = 0x11, |
201 | .model = "P11G8M" , |
202 | .serial = "" , |
203 | .type = "LION" , |
204 | .OEM = "" , |
205 | }; |
206 | |
207 | static int mshw0011_bix(struct mshw0011_data *cdata, struct bix *bix) |
208 | { |
209 | struct i2c_client *client = cdata->bat0; |
210 | char buf[SURFACE_3_STRLEN]; |
211 | int ret; |
212 | |
213 | *bix = default_bix; |
214 | |
215 | /* get design capacity */ |
216 | ret = i2c_smbus_read_word_data(client, |
217 | MSHW0011_BAT0_REG_DESIGN_CAPACITY); |
218 | if (ret < 0) { |
219 | dev_err(&client->dev, "Error reading design capacity: %d\n" , |
220 | ret); |
221 | return ret; |
222 | } |
223 | bix->design_capacity = ret; |
224 | |
225 | /* get last full charge capacity */ |
226 | ret = i2c_smbus_read_word_data(client, |
227 | MSHW0011_BAT0_REG_FULL_CHG_CAPACITY); |
228 | if (ret < 0) { |
229 | dev_err(&client->dev, |
230 | "Error reading last full charge capacity: %d\n" , ret); |
231 | return ret; |
232 | } |
233 | bix->last_full_charg_capacity = ret; |
234 | |
235 | /* |
236 | * Get serial number, on some devices (with unofficial replacement |
237 | * battery?) reading any of the serial number range addresses gets |
238 | * nacked in this case just leave the serial number empty. |
239 | */ |
240 | ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_SERIAL_NO, |
241 | length: sizeof(buf), values: buf); |
242 | if (ret == -EREMOTEIO) { |
243 | /* no serial number available */ |
244 | } else if (ret != sizeof(buf)) { |
245 | dev_err(&client->dev, "Error reading serial no: %d\n" , ret); |
246 | return ret; |
247 | } else { |
248 | snprintf(buf: bix->serial, ARRAY_SIZE(bix->serial), fmt: "%3pE%6pE" , buf + 7, buf); |
249 | } |
250 | |
251 | /* get cycle count */ |
252 | ret = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CYCLE_CNT); |
253 | if (ret < 0) { |
254 | dev_err(&client->dev, "Error reading cycle count: %d\n" , ret); |
255 | return ret; |
256 | } |
257 | bix->cycle_count = ret; |
258 | |
259 | /* get OEM name */ |
260 | ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_OEM, |
261 | length: 4, values: buf); |
262 | if (ret != 4) { |
263 | dev_err(&client->dev, "Error reading cycle count: %d\n" , ret); |
264 | return ret; |
265 | } |
266 | snprintf(buf: bix->OEM, ARRAY_SIZE(bix->OEM), fmt: "%3pE" , buf); |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static int mshw0011_bst(struct mshw0011_data *cdata, struct bst *bst) |
272 | { |
273 | struct i2c_client *client = cdata->bat0; |
274 | int rate, capacity, voltage, state; |
275 | s16 tmp; |
276 | |
277 | rate = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_RATE); |
278 | if (rate < 0) |
279 | return rate; |
280 | |
281 | capacity = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CAPACITY); |
282 | if (capacity < 0) |
283 | return capacity; |
284 | |
285 | voltage = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_VOLTAGE); |
286 | if (voltage < 0) |
287 | return voltage; |
288 | |
289 | tmp = rate; |
290 | bst->battery_present_rate = abs((s32)tmp); |
291 | |
292 | state = 0; |
293 | if ((s32) tmp > 0) |
294 | state |= ACPI_BATTERY_STATE_CHARGING; |
295 | else if ((s32) tmp < 0) |
296 | state |= ACPI_BATTERY_STATE_DISCHARGING; |
297 | bst->battery_state = state; |
298 | |
299 | bst->battery_remaining_capacity = capacity; |
300 | bst->battery_present_voltage = voltage; |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | static int mshw0011_adp_psr(struct mshw0011_data *cdata) |
306 | { |
307 | return i2c_smbus_read_byte_data(client: cdata->adp1, MSHW0011_ADP1_REG_PSR); |
308 | } |
309 | |
310 | static int mshw0011_isr(struct mshw0011_data *cdata) |
311 | { |
312 | struct bst bst; |
313 | struct bix bix; |
314 | int ret; |
315 | bool status, bat_status; |
316 | |
317 | ret = mshw0011_adp_psr(cdata); |
318 | if (ret < 0) |
319 | return ret; |
320 | |
321 | status = ret; |
322 | if (status != cdata->charging) |
323 | mshw0011_notify(cdata, arg1: cdata->notify_mask, |
324 | MSHW0011_NOTIFY_ADP1, ret_value: &ret); |
325 | |
326 | cdata->charging = status; |
327 | |
328 | ret = mshw0011_bst(cdata, bst: &bst); |
329 | if (ret < 0) |
330 | return ret; |
331 | |
332 | bat_status = bst.battery_state; |
333 | if (bat_status != cdata->bat_charging) |
334 | mshw0011_notify(cdata, arg1: cdata->notify_mask, |
335 | MSHW0011_NOTIFY_BAT0_BST, ret_value: &ret); |
336 | |
337 | cdata->bat_charging = bat_status; |
338 | |
339 | ret = mshw0011_bix(cdata, bix: &bix); |
340 | if (ret < 0) |
341 | return ret; |
342 | |
343 | if (bix.last_full_charg_capacity != cdata->full_capacity) |
344 | mshw0011_notify(cdata, arg1: cdata->notify_mask, |
345 | MSHW0011_NOTIFY_BAT0_BIX, ret_value: &ret); |
346 | |
347 | cdata->full_capacity = bix.last_full_charg_capacity; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static int mshw0011_poll_task(void *data) |
353 | { |
354 | struct mshw0011_data *cdata = data; |
355 | int ret = 0; |
356 | |
357 | cdata->kthread_running = true; |
358 | |
359 | set_freezable(); |
360 | |
361 | while (!kthread_should_stop()) { |
362 | schedule_timeout_interruptible(SURFACE_3_POLL_INTERVAL); |
363 | try_to_freeze(); |
364 | ret = mshw0011_isr(cdata: data); |
365 | if (ret) |
366 | break; |
367 | } |
368 | |
369 | cdata->kthread_running = false; |
370 | return ret; |
371 | } |
372 | |
373 | static acpi_status |
374 | mshw0011_space_handler(u32 function, acpi_physical_address command, |
375 | u32 bits, u64 *value64, |
376 | void *handler_context, void *region_context) |
377 | { |
378 | struct gsb_buffer *gsb = (struct gsb_buffer *)value64; |
379 | struct mshw0011_handler_data *data = handler_context; |
380 | struct acpi_connection_info *info = &data->info; |
381 | struct acpi_resource_i2c_serialbus *sb; |
382 | struct i2c_client *client = data->client; |
383 | struct mshw0011_data *cdata = i2c_get_clientdata(client); |
384 | struct acpi_resource *ares; |
385 | u32 accessor_type = function >> 16; |
386 | acpi_status ret; |
387 | int status = 1; |
388 | |
389 | ret = acpi_buffer_to_resource(aml_buffer: info->connection, aml_buffer_length: info->length, resource_ptr: &ares); |
390 | if (ACPI_FAILURE(ret)) |
391 | return ret; |
392 | |
393 | if (!value64 || !i2c_acpi_get_i2c_resource(ares, i2c: &sb)) { |
394 | ret = AE_BAD_PARAMETER; |
395 | goto err; |
396 | } |
397 | |
398 | if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { |
399 | ret = AE_BAD_PARAMETER; |
400 | goto err; |
401 | } |
402 | |
403 | if (gsb->cmd.arg0 == MSHW0011_CMD_DEST_ADP1 && |
404 | gsb->cmd.arg1 == MSHW0011_CMD_ADP1_PSR) { |
405 | status = mshw0011_adp_psr(cdata); |
406 | if (status >= 0) { |
407 | ret = AE_OK; |
408 | goto out; |
409 | } else { |
410 | ret = AE_ERROR; |
411 | goto err; |
412 | } |
413 | } |
414 | |
415 | if (gsb->cmd.arg0 != MSHW0011_CMD_DEST_BAT0) { |
416 | ret = AE_BAD_PARAMETER; |
417 | goto err; |
418 | } |
419 | |
420 | switch (gsb->cmd.arg1) { |
421 | case MSHW0011_CMD_BAT0_STA: |
422 | break; |
423 | case MSHW0011_CMD_BAT0_BIX: |
424 | ret = mshw0011_bix(cdata, bix: &gsb->bix); |
425 | break; |
426 | case MSHW0011_CMD_BAT0_BTP: |
427 | cdata->trip_point = gsb->cmd.arg2; |
428 | break; |
429 | case MSHW0011_CMD_BAT0_BST: |
430 | ret = mshw0011_bst(cdata, bst: &gsb->bst); |
431 | break; |
432 | default: |
433 | dev_info(&cdata->bat0->dev, "command(0x%02x) is not supported.\n" , gsb->cmd.arg1); |
434 | ret = AE_BAD_PARAMETER; |
435 | goto err; |
436 | } |
437 | |
438 | out: |
439 | gsb->ret = status; |
440 | gsb->status = 0; |
441 | |
442 | err: |
443 | ACPI_FREE(ares); |
444 | return ret; |
445 | } |
446 | |
447 | static int mshw0011_install_space_handler(struct i2c_client *client) |
448 | { |
449 | struct acpi_device *adev; |
450 | struct mshw0011_handler_data *data; |
451 | acpi_status status; |
452 | |
453 | adev = ACPI_COMPANION(&client->dev); |
454 | if (!adev) |
455 | return -ENODEV; |
456 | |
457 | data = kzalloc(size: sizeof(struct mshw0011_handler_data), |
458 | GFP_KERNEL); |
459 | if (!data) |
460 | return -ENOMEM; |
461 | |
462 | data->client = client; |
463 | status = acpi_bus_attach_private_data(adev->handle, (void *)data); |
464 | if (ACPI_FAILURE(status)) { |
465 | kfree(objp: data); |
466 | return -ENOMEM; |
467 | } |
468 | |
469 | status = acpi_install_address_space_handler(device: adev->handle, |
470 | ACPI_ADR_SPACE_GSBUS, |
471 | handler: &mshw0011_space_handler, |
472 | NULL, |
473 | context: data); |
474 | if (ACPI_FAILURE(status)) { |
475 | dev_err(&client->dev, "Error installing i2c space handler\n" ); |
476 | acpi_bus_detach_private_data(adev->handle); |
477 | kfree(objp: data); |
478 | return -ENOMEM; |
479 | } |
480 | |
481 | acpi_dev_clear_dependencies(supplier: adev); |
482 | return 0; |
483 | } |
484 | |
485 | static void mshw0011_remove_space_handler(struct i2c_client *client) |
486 | { |
487 | struct mshw0011_handler_data *data; |
488 | acpi_handle handle; |
489 | acpi_status status; |
490 | |
491 | handle = ACPI_HANDLE(&client->dev); |
492 | if (!handle) |
493 | return; |
494 | |
495 | acpi_remove_address_space_handler(device: handle, |
496 | ACPI_ADR_SPACE_GSBUS, |
497 | handler: &mshw0011_space_handler); |
498 | |
499 | status = acpi_bus_get_private_data(handle, (void **)&data); |
500 | if (ACPI_SUCCESS(status)) |
501 | kfree(objp: data); |
502 | |
503 | acpi_bus_detach_private_data(handle); |
504 | } |
505 | |
506 | static int mshw0011_probe(struct i2c_client *client) |
507 | { |
508 | struct i2c_board_info board_info; |
509 | struct device *dev = &client->dev; |
510 | struct i2c_client *bat0; |
511 | struct mshw0011_data *data; |
512 | int error, mask; |
513 | |
514 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
515 | if (!data) |
516 | return -ENOMEM; |
517 | |
518 | data->adp1 = client; |
519 | i2c_set_clientdata(client, data); |
520 | |
521 | memset(&board_info, 0, sizeof(board_info)); |
522 | strscpy(board_info.type, "MSHW0011-bat0" , I2C_NAME_SIZE); |
523 | |
524 | bat0 = i2c_acpi_new_device(dev, index: 1, info: &board_info); |
525 | if (IS_ERR(ptr: bat0)) |
526 | return PTR_ERR(ptr: bat0); |
527 | |
528 | data->bat0 = bat0; |
529 | i2c_set_clientdata(client: bat0, data); |
530 | |
531 | error = mshw0011_notify(cdata: data, arg1: 1, MSHW0011_NOTIFY_GET_VERSION, ret_value: &mask); |
532 | if (error) |
533 | goto out_err; |
534 | |
535 | data->notify_mask = mask == MSHW0011_EV_2_5_MASK; |
536 | |
537 | data->poll_task = kthread_run(mshw0011_poll_task, data, "mshw0011_adp" ); |
538 | if (IS_ERR(ptr: data->poll_task)) { |
539 | error = PTR_ERR(ptr: data->poll_task); |
540 | dev_err(&client->dev, "Unable to run kthread err %d\n" , error); |
541 | goto out_err; |
542 | } |
543 | |
544 | error = mshw0011_install_space_handler(client); |
545 | if (error) |
546 | goto out_err; |
547 | |
548 | return 0; |
549 | |
550 | out_err: |
551 | if (data->kthread_running) |
552 | kthread_stop(k: data->poll_task); |
553 | i2c_unregister_device(client: data->bat0); |
554 | return error; |
555 | } |
556 | |
557 | static void mshw0011_remove(struct i2c_client *client) |
558 | { |
559 | struct mshw0011_data *cdata = i2c_get_clientdata(client); |
560 | |
561 | mshw0011_remove_space_handler(client); |
562 | |
563 | if (cdata->kthread_running) |
564 | kthread_stop(k: cdata->poll_task); |
565 | |
566 | i2c_unregister_device(client: cdata->bat0); |
567 | } |
568 | |
569 | static const struct acpi_device_id mshw0011_acpi_match[] = { |
570 | { "MSHW0011" , 0 }, |
571 | { } |
572 | }; |
573 | MODULE_DEVICE_TABLE(acpi, mshw0011_acpi_match); |
574 | |
575 | static struct i2c_driver mshw0011_driver = { |
576 | .probe = mshw0011_probe, |
577 | .remove = mshw0011_remove, |
578 | .driver = { |
579 | .name = "mshw0011" , |
580 | .acpi_match_table = mshw0011_acpi_match, |
581 | }, |
582 | }; |
583 | module_i2c_driver(mshw0011_driver); |
584 | |
585 | MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>" ); |
586 | MODULE_DESCRIPTION("mshw0011 driver" ); |
587 | MODULE_LICENSE("GPL v2" ); |
588 | |