]>
Commit | Line | Data |
---|---|---|
b1f81b49 BH |
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> | |
dd4a9356 | 35 | #include <linux/bits.h> |
b1f81b49 BH |
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> | |
dd4a9356 | 41 | #include <linux/types.h> |
b1f81b49 BH |
42 | #include <linux/uuid.h> |
43 | #include <asm/unaligned.h> | |
44 | ||
fcbb1142 AS |
45 | #define SURFACE_3_POLL_INTERVAL (2 * HZ) |
46 | #define SURFACE_3_STRLEN 10 | |
b1f81b49 BH |
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 | ||
b1f81b49 BH |
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 | ||
3d8fc115 | 152 | /* 3f99e367-6220-4955-8b0f-06ef2ae79412 */ |
b1f81b49 | 153 | static const guid_t mshw0011_guid = |
3d8fc115 AS |
154 | GUID_INIT(0x3F99E367, 0x6220, 0x4955, 0x8B, 0x0F, 0x06, 0xEF, |
155 | 0x2A, 0xE7, 0x94, 0x12); | |
b1f81b49 BH |
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 | struct acpi_device *adev; | |
163 | acpi_handle handle; | |
164 | unsigned int i; | |
165 | ||
166 | handle = ACPI_HANDLE(&cdata->adp1->dev); | |
167 | if (!handle || acpi_bus_get_device(handle, &adev)) | |
168 | return -ENODEV; | |
169 | ||
170 | obj = acpi_evaluate_dsm_typed(handle, &mshw0011_guid, arg1, arg2, NULL, | |
171 | ACPI_TYPE_BUFFER); | |
172 | if (!obj) { | |
173 | dev_err(&cdata->adp1->dev, "device _DSM execution failed\n"); | |
174 | return -ENODEV; | |
175 | } | |
176 | ||
177 | *ret_value = 0; | |
178 | for (i = 0; i < obj->buffer.length; i++) | |
179 | *ret_value |= obj->buffer.pointer[i] << (i * 8); | |
180 | ||
181 | ACPI_FREE(obj); | |
182 | return 0; | |
183 | } | |
184 | ||
185 | static const struct bix default_bix = { | |
186 | .revision = 0x00, | |
187 | .power_unit = 0x01, | |
188 | .design_capacity = 0x1dca, | |
189 | .last_full_charg_capacity = 0x1dca, | |
190 | .battery_technology = 0x01, | |
191 | .design_voltage = 0x10df, | |
192 | .design_capacity_of_warning = 0x8f, | |
193 | .design_capacity_of_low = 0x47, | |
194 | .cycle_count = 0xffffffff, | |
195 | .measurement_accuracy = 0x00015f90, | |
196 | .max_sampling_time = 0x03e8, | |
197 | .min_sampling_time = 0x03e8, | |
198 | .max_average_interval = 0x03e8, | |
199 | .min_average_interval = 0x03e8, | |
200 | .battery_capacity_granularity_1 = 0x45, | |
201 | .battery_capacity_granularity_2 = 0x11, | |
202 | .model = "P11G8M", | |
203 | .serial = "", | |
204 | .type = "LION", | |
205 | .OEM = "", | |
206 | }; | |
207 | ||
208 | static int mshw0011_bix(struct mshw0011_data *cdata, struct bix *bix) | |
209 | { | |
210 | struct i2c_client *client = cdata->bat0; | |
211 | char buf[SURFACE_3_STRLEN]; | |
212 | int ret; | |
213 | ||
214 | *bix = default_bix; | |
215 | ||
216 | /* get design capacity */ | |
217 | ret = i2c_smbus_read_word_data(client, | |
218 | MSHW0011_BAT0_REG_DESIGN_CAPACITY); | |
219 | if (ret < 0) { | |
220 | dev_err(&client->dev, "Error reading design capacity: %d\n", | |
221 | ret); | |
222 | return ret; | |
223 | } | |
224 | bix->design_capacity = ret; | |
225 | ||
226 | /* get last full charge capacity */ | |
227 | ret = i2c_smbus_read_word_data(client, | |
228 | MSHW0011_BAT0_REG_FULL_CHG_CAPACITY); | |
229 | if (ret < 0) { | |
230 | dev_err(&client->dev, | |
231 | "Error reading last full charge capacity: %d\n", ret); | |
232 | return ret; | |
233 | } | |
234 | bix->last_full_charg_capacity = ret; | |
235 | ||
236 | /* get serial number */ | |
237 | ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_SERIAL_NO, | |
238 | sizeof(buf), buf); | |
239 | if (ret != sizeof(buf)) { | |
240 | dev_err(&client->dev, "Error reading serial no: %d\n", ret); | |
241 | return ret; | |
242 | } | |
243 | snprintf(bix->serial, ARRAY_SIZE(bix->serial), "%3pE%6pE", buf + 7, buf); | |
244 | ||
245 | /* get cycle count */ | |
246 | ret = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CYCLE_CNT); | |
247 | if (ret < 0) { | |
248 | dev_err(&client->dev, "Error reading cycle count: %d\n", ret); | |
249 | return ret; | |
250 | } | |
251 | bix->cycle_count = ret; | |
252 | ||
253 | /* get OEM name */ | |
254 | ret = i2c_smbus_read_i2c_block_data(client, MSHW0011_BAT0_REG_OEM, | |
255 | 4, buf); | |
256 | if (ret != 4) { | |
257 | dev_err(&client->dev, "Error reading cycle count: %d\n", ret); | |
258 | return ret; | |
259 | } | |
260 | snprintf(bix->OEM, ARRAY_SIZE(bix->OEM), "%3pE", buf); | |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
265 | static int mshw0011_bst(struct mshw0011_data *cdata, struct bst *bst) | |
266 | { | |
267 | struct i2c_client *client = cdata->bat0; | |
268 | int rate, capacity, voltage, state; | |
269 | s16 tmp; | |
270 | ||
271 | rate = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_RATE); | |
272 | if (rate < 0) | |
273 | return rate; | |
274 | ||
275 | capacity = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_CAPACITY); | |
276 | if (capacity < 0) | |
277 | return capacity; | |
278 | ||
279 | voltage = i2c_smbus_read_word_data(client, MSHW0011_BAT0_REG_VOLTAGE); | |
280 | if (voltage < 0) | |
281 | return voltage; | |
282 | ||
283 | tmp = rate; | |
284 | bst->battery_present_rate = abs((s32)tmp); | |
285 | ||
286 | state = 0; | |
287 | if ((s32) tmp > 0) | |
288 | state |= ACPI_BATTERY_STATE_CHARGING; | |
289 | else if ((s32) tmp < 0) | |
290 | state |= ACPI_BATTERY_STATE_DISCHARGING; | |
291 | bst->battery_state = state; | |
292 | ||
293 | bst->battery_remaining_capacity = capacity; | |
294 | bst->battery_present_voltage = voltage; | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | static int mshw0011_adp_psr(struct mshw0011_data *cdata) | |
300 | { | |
48978995 | 301 | return i2c_smbus_read_byte_data(cdata->adp1, MSHW0011_ADP1_REG_PSR); |
b1f81b49 BH |
302 | } |
303 | ||
304 | static int mshw0011_isr(struct mshw0011_data *cdata) | |
305 | { | |
306 | struct bst bst; | |
307 | struct bix bix; | |
308 | int ret; | |
309 | bool status, bat_status; | |
310 | ||
311 | ret = mshw0011_adp_psr(cdata); | |
312 | if (ret < 0) | |
313 | return ret; | |
314 | ||
315 | status = ret; | |
316 | if (status != cdata->charging) | |
317 | mshw0011_notify(cdata, cdata->notify_mask, | |
318 | MSHW0011_NOTIFY_ADP1, &ret); | |
319 | ||
320 | cdata->charging = status; | |
321 | ||
322 | ret = mshw0011_bst(cdata, &bst); | |
323 | if (ret < 0) | |
324 | return ret; | |
325 | ||
326 | bat_status = bst.battery_state; | |
327 | if (bat_status != cdata->bat_charging) | |
328 | mshw0011_notify(cdata, cdata->notify_mask, | |
329 | MSHW0011_NOTIFY_BAT0_BST, &ret); | |
330 | ||
331 | cdata->bat_charging = bat_status; | |
332 | ||
333 | ret = mshw0011_bix(cdata, &bix); | |
334 | if (ret < 0) | |
335 | return ret; | |
336 | ||
337 | if (bix.last_full_charg_capacity != cdata->full_capacity) | |
338 | mshw0011_notify(cdata, cdata->notify_mask, | |
339 | MSHW0011_NOTIFY_BAT0_BIX, &ret); | |
340 | ||
341 | cdata->full_capacity = bix.last_full_charg_capacity; | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | static int mshw0011_poll_task(void *data) | |
347 | { | |
348 | struct mshw0011_data *cdata = data; | |
349 | int ret = 0; | |
350 | ||
351 | cdata->kthread_running = true; | |
352 | ||
353 | set_freezable(); | |
354 | ||
355 | while (!kthread_should_stop()) { | |
fcbb1142 | 356 | schedule_timeout_interruptible(SURFACE_3_POLL_INTERVAL); |
b1f81b49 BH |
357 | try_to_freeze(); |
358 | ret = mshw0011_isr(data); | |
359 | if (ret) | |
360 | break; | |
361 | } | |
362 | ||
363 | cdata->kthread_running = false; | |
364 | return ret; | |
365 | } | |
366 | ||
367 | static acpi_status | |
368 | mshw0011_space_handler(u32 function, acpi_physical_address command, | |
369 | u32 bits, u64 *value64, | |
370 | void *handler_context, void *region_context) | |
371 | { | |
372 | struct gsb_buffer *gsb = (struct gsb_buffer *)value64; | |
373 | struct mshw0011_handler_data *data = handler_context; | |
374 | struct acpi_connection_info *info = &data->info; | |
375 | struct acpi_resource_i2c_serialbus *sb; | |
376 | struct i2c_client *client = data->client; | |
377 | struct mshw0011_data *cdata = i2c_get_clientdata(client); | |
378 | struct acpi_resource *ares; | |
379 | u32 accessor_type = function >> 16; | |
380 | acpi_status ret; | |
381 | int status = 1; | |
382 | ||
383 | ret = acpi_buffer_to_resource(info->connection, info->length, &ares); | |
384 | if (ACPI_FAILURE(ret)) | |
385 | return ret; | |
386 | ||
387 | if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { | |
388 | ret = AE_BAD_PARAMETER; | |
389 | goto err; | |
390 | } | |
391 | ||
392 | sb = &ares->data.i2c_serial_bus; | |
393 | if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { | |
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) { | |
d878bdfb AS |
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; | |
b1f81b49 | 412 | } |
b1f81b49 BH |
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, &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, &gsb->bst); | |
431 | break; | |
432 | default: | |
74bef188 | 433 | dev_info(&cdata->bat0->dev, "command(0x%02x) is not supported.\n", gsb->cmd.arg1); |
b1f81b49 BH |
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 | acpi_handle handle; | |
450 | struct mshw0011_handler_data *data; | |
451 | acpi_status status; | |
452 | ||
453 | handle = ACPI_HANDLE(&client->dev); | |
454 | if (!handle) | |
455 | return -ENODEV; | |
456 | ||
457 | data = kzalloc(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(handle, (void *)data); | |
464 | if (ACPI_FAILURE(status)) { | |
465 | kfree(data); | |
466 | return -ENOMEM; | |
467 | } | |
468 | ||
469 | status = acpi_install_address_space_handler(handle, | |
470 | ACPI_ADR_SPACE_GSBUS, | |
471 | &mshw0011_space_handler, | |
472 | NULL, | |
473 | data); | |
474 | if (ACPI_FAILURE(status)) { | |
475 | dev_err(&client->dev, "Error installing i2c space handler\n"); | |
476 | acpi_bus_detach_private_data(handle); | |
477 | kfree(data); | |
478 | return -ENOMEM; | |
479 | } | |
480 | ||
481 | acpi_walk_dep_device_list(handle); | |
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(handle, | |
496 | ACPI_ADR_SPACE_GSBUS, | |
497 | &mshw0011_space_handler); | |
498 | ||
499 | status = acpi_bus_get_private_data(handle, (void **)&data); | |
500 | if (ACPI_SUCCESS(status)) | |
501 | kfree(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, 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 | strlcpy(board_info.type, "MSHW0011-bat0", I2C_NAME_SIZE); | |
523 | ||
524 | bat0 = i2c_acpi_new_device(dev, 1, &board_info); | |
4dbccb87 DC |
525 | if (IS_ERR(bat0)) |
526 | return PTR_ERR(bat0); | |
b1f81b49 BH |
527 | |
528 | data->bat0 = bat0; | |
529 | i2c_set_clientdata(bat0, data); | |
530 | ||
531 | error = mshw0011_notify(data, 1, MSHW0011_NOTIFY_GET_VERSION, &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(data->poll_task)) { | |
539 | error = PTR_ERR(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(data->poll_task); | |
553 | i2c_unregister_device(data->bat0); | |
554 | return error; | |
555 | } | |
556 | ||
557 | static int 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(cdata->poll_task); | |
565 | ||
566 | i2c_unregister_device(cdata->bat0); | |
567 | ||
568 | return 0; | |
569 | } | |
570 | ||
571 | static const struct acpi_device_id mshw0011_acpi_match[] = { | |
572 | { "MSHW0011", 0 }, | |
573 | { } | |
574 | }; | |
575 | MODULE_DEVICE_TABLE(acpi, mshw0011_acpi_match); | |
576 | ||
577 | static struct i2c_driver mshw0011_driver = { | |
578 | .probe_new = mshw0011_probe, | |
579 | .remove = mshw0011_remove, | |
580 | .driver = { | |
581 | .name = "mshw0011", | |
f1f25fc8 | 582 | .acpi_match_table = mshw0011_acpi_match, |
b1f81b49 BH |
583 | }, |
584 | }; | |
585 | module_i2c_driver(mshw0011_driver); | |
586 | ||
587 | MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); | |
588 | MODULE_DESCRIPTION("mshw0011 driver"); | |
589 | MODULE_LICENSE("GPL v2"); |