1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Linux I2C core ACPI support code |
4 | * |
5 | * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu <tianyu.lan@intel.com> |
6 | */ |
7 | |
8 | #include <linux/acpi.h> |
9 | #include <linux/device.h> |
10 | #include <linux/err.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/list.h> |
13 | #include <linux/module.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include "i2c-core.h" |
17 | |
18 | struct i2c_acpi_handler_data { |
19 | struct acpi_connection_info info; |
20 | struct i2c_adapter *adapter; |
21 | }; |
22 | |
23 | struct gsb_buffer { |
24 | u8 status; |
25 | u8 len; |
26 | union { |
27 | u16 wdata; |
28 | u8 bdata; |
29 | DECLARE_FLEX_ARRAY(u8, data); |
30 | }; |
31 | } __packed; |
32 | |
33 | struct i2c_acpi_lookup { |
34 | struct i2c_board_info *info; |
35 | acpi_handle adapter_handle; |
36 | acpi_handle device_handle; |
37 | acpi_handle search_handle; |
38 | int n; |
39 | int index; |
40 | u32 speed; |
41 | u32 min_speed; |
42 | u32 force_speed; |
43 | }; |
44 | |
45 | /** |
46 | * i2c_acpi_get_i2c_resource - Gets I2cSerialBus resource if type matches |
47 | * @ares: ACPI resource |
48 | * @i2c: Pointer to I2cSerialBus resource will be returned here |
49 | * |
50 | * Checks if the given ACPI resource is of type I2cSerialBus. |
51 | * In this case, returns a pointer to it to the caller. |
52 | * |
53 | * Returns true if resource type is of I2cSerialBus, otherwise false. |
54 | */ |
55 | bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, |
56 | struct acpi_resource_i2c_serialbus **i2c) |
57 | { |
58 | struct acpi_resource_i2c_serialbus *sb; |
59 | |
60 | if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) |
61 | return false; |
62 | |
63 | sb = &ares->data.i2c_serial_bus; |
64 | if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) |
65 | return false; |
66 | |
67 | *i2c = sb; |
68 | return true; |
69 | } |
70 | EXPORT_SYMBOL_GPL(i2c_acpi_get_i2c_resource); |
71 | |
72 | static int i2c_acpi_resource_count(struct acpi_resource *ares, void *data) |
73 | { |
74 | struct acpi_resource_i2c_serialbus *sb; |
75 | int *count = data; |
76 | |
77 | if (i2c_acpi_get_i2c_resource(ares, &sb)) |
78 | *count = *count + 1; |
79 | |
80 | return 1; |
81 | } |
82 | |
83 | /** |
84 | * i2c_acpi_client_count - Count the number of I2cSerialBus resources |
85 | * @adev: ACPI device |
86 | * |
87 | * Returns the number of I2cSerialBus resources in the ACPI-device's |
88 | * resource-list; or a negative error code. |
89 | */ |
90 | int i2c_acpi_client_count(struct acpi_device *adev) |
91 | { |
92 | int ret, count = 0; |
93 | LIST_HEAD(r); |
94 | |
95 | ret = acpi_dev_get_resources(adev, list: &r, preproc: i2c_acpi_resource_count, preproc_data: &count); |
96 | if (ret < 0) |
97 | return ret; |
98 | |
99 | acpi_dev_free_resource_list(list: &r); |
100 | return count; |
101 | } |
102 | EXPORT_SYMBOL_GPL(i2c_acpi_client_count); |
103 | |
104 | static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) |
105 | { |
106 | struct i2c_acpi_lookup *lookup = data; |
107 | struct i2c_board_info *info = lookup->info; |
108 | struct acpi_resource_i2c_serialbus *sb; |
109 | acpi_status status; |
110 | |
111 | if (info->addr || !i2c_acpi_get_i2c_resource(ares, &sb)) |
112 | return 1; |
113 | |
114 | if (lookup->index != -1 && lookup->n++ != lookup->index) |
115 | return 1; |
116 | |
117 | status = acpi_get_handle(parent: lookup->device_handle, |
118 | pathname: sb->resource_source.string_ptr, |
119 | ret_handle: &lookup->adapter_handle); |
120 | if (ACPI_FAILURE(status)) |
121 | return 1; |
122 | |
123 | info->addr = sb->slave_address; |
124 | lookup->speed = sb->connection_speed; |
125 | if (sb->access_mode == ACPI_I2C_10BIT_MODE) |
126 | info->flags |= I2C_CLIENT_TEN; |
127 | |
128 | return 1; |
129 | } |
130 | |
131 | static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = { |
132 | /* |
133 | * ACPI video acpi_devices, which are handled by the acpi-video driver |
134 | * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. |
135 | */ |
136 | { ACPI_VIDEO_HID, 0 }, |
137 | {} |
138 | }; |
139 | |
140 | struct i2c_acpi_irq_context { |
141 | int irq; |
142 | bool wake_capable; |
143 | }; |
144 | |
145 | static int i2c_acpi_do_lookup(struct acpi_device *adev, |
146 | struct i2c_acpi_lookup *lookup) |
147 | { |
148 | struct i2c_board_info *info = lookup->info; |
149 | struct list_head resource_list; |
150 | int ret; |
151 | |
152 | if (acpi_bus_get_status(device: adev)) |
153 | return -EINVAL; |
154 | |
155 | if (!acpi_dev_ready_for_enumeration(device: adev)) |
156 | return -ENODEV; |
157 | |
158 | if (acpi_match_device_ids(device: adev, ids: i2c_acpi_ignored_device_ids) == 0) |
159 | return -ENODEV; |
160 | |
161 | memset(info, 0, sizeof(*info)); |
162 | lookup->device_handle = acpi_device_handle(adev); |
163 | |
164 | /* Look up for I2cSerialBus resource */ |
165 | INIT_LIST_HEAD(list: &resource_list); |
166 | ret = acpi_dev_get_resources(adev, list: &resource_list, |
167 | preproc: i2c_acpi_fill_info, preproc_data: lookup); |
168 | acpi_dev_free_resource_list(list: &resource_list); |
169 | |
170 | if (ret < 0 || !info->addr) |
171 | return -EINVAL; |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | static int i2c_acpi_add_irq_resource(struct acpi_resource *ares, void *data) |
177 | { |
178 | struct i2c_acpi_irq_context *irq_ctx = data; |
179 | struct resource r; |
180 | |
181 | if (irq_ctx->irq > 0) |
182 | return 1; |
183 | |
184 | if (!acpi_dev_resource_interrupt(ares, index: 0, res: &r)) |
185 | return 1; |
186 | |
187 | irq_ctx->irq = i2c_dev_irq_from_resources(resources: &r, num_resources: 1); |
188 | irq_ctx->wake_capable = r.flags & IORESOURCE_IRQ_WAKECAPABLE; |
189 | |
190 | return 1; /* No need to add resource to the list */ |
191 | } |
192 | |
193 | /** |
194 | * i2c_acpi_get_irq - get device IRQ number from ACPI |
195 | * @client: Pointer to the I2C client device |
196 | * @wake_capable: Set to true if the IRQ is wake capable |
197 | * |
198 | * Find the IRQ number used by a specific client device. |
199 | * |
200 | * Return: The IRQ number or an error code. |
201 | */ |
202 | int i2c_acpi_get_irq(struct i2c_client *client, bool *wake_capable) |
203 | { |
204 | struct acpi_device *adev = ACPI_COMPANION(&client->dev); |
205 | struct list_head resource_list; |
206 | struct i2c_acpi_irq_context irq_ctx = { |
207 | .irq = -ENOENT, |
208 | }; |
209 | int ret; |
210 | |
211 | INIT_LIST_HEAD(list: &resource_list); |
212 | |
213 | ret = acpi_dev_get_resources(adev, list: &resource_list, |
214 | preproc: i2c_acpi_add_irq_resource, preproc_data: &irq_ctx); |
215 | if (ret < 0) |
216 | return ret; |
217 | |
218 | acpi_dev_free_resource_list(list: &resource_list); |
219 | |
220 | if (irq_ctx.irq == -ENOENT) |
221 | irq_ctx.irq = acpi_dev_gpio_irq_wake_get(adev, index: 0, wake_capable: &irq_ctx.wake_capable); |
222 | |
223 | if (irq_ctx.irq < 0) |
224 | return irq_ctx.irq; |
225 | |
226 | if (wake_capable) |
227 | *wake_capable = irq_ctx.wake_capable; |
228 | |
229 | return irq_ctx.irq; |
230 | } |
231 | |
232 | static int i2c_acpi_get_info(struct acpi_device *adev, |
233 | struct i2c_board_info *info, |
234 | struct i2c_adapter *adapter, |
235 | acpi_handle *adapter_handle) |
236 | { |
237 | struct i2c_acpi_lookup lookup; |
238 | int ret; |
239 | |
240 | memset(&lookup, 0, sizeof(lookup)); |
241 | lookup.info = info; |
242 | lookup.index = -1; |
243 | |
244 | if (acpi_device_enumerated(adev)) |
245 | return -EINVAL; |
246 | |
247 | ret = i2c_acpi_do_lookup(adev, lookup: &lookup); |
248 | if (ret) |
249 | return ret; |
250 | |
251 | if (adapter) { |
252 | /* The adapter must match the one in I2cSerialBus() connector */ |
253 | if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle) |
254 | return -ENODEV; |
255 | } else { |
256 | struct acpi_device *adapter_adev; |
257 | |
258 | /* The adapter must be present */ |
259 | adapter_adev = acpi_fetch_acpi_dev(handle: lookup.adapter_handle); |
260 | if (!adapter_adev) |
261 | return -ENODEV; |
262 | if (acpi_bus_get_status(device: adapter_adev) || |
263 | !adapter_adev->status.present) |
264 | return -ENODEV; |
265 | } |
266 | |
267 | info->fwnode = acpi_fwnode_handle(adev); |
268 | if (adapter_handle) |
269 | *adapter_handle = lookup.adapter_handle; |
270 | |
271 | acpi_set_modalias(adev, default_id: dev_name(dev: &adev->dev), modalias: info->type, |
272 | len: sizeof(info->type)); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | static void i2c_acpi_register_device(struct i2c_adapter *adapter, |
278 | struct acpi_device *adev, |
279 | struct i2c_board_info *info) |
280 | { |
281 | /* |
282 | * Skip registration on boards where the ACPI tables are |
283 | * known to contain bogus I2C devices. |
284 | */ |
285 | if (acpi_quirk_skip_i2c_client_enumeration(adev)) |
286 | return; |
287 | |
288 | adev->power.flags.ignore_parent = true; |
289 | acpi_device_set_enumerated(adev); |
290 | |
291 | if (IS_ERR(ptr: i2c_new_client_device(adap: adapter, info))) |
292 | adev->power.flags.ignore_parent = false; |
293 | } |
294 | |
295 | static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, |
296 | void *data, void **return_value) |
297 | { |
298 | struct i2c_adapter *adapter = data; |
299 | struct acpi_device *adev = acpi_fetch_acpi_dev(handle); |
300 | struct i2c_board_info info; |
301 | |
302 | if (!adev || i2c_acpi_get_info(adev, info: &info, adapter, NULL)) |
303 | return AE_OK; |
304 | |
305 | i2c_acpi_register_device(adapter, adev, info: &info); |
306 | |
307 | return AE_OK; |
308 | } |
309 | |
310 | #define I2C_ACPI_MAX_SCAN_DEPTH 32 |
311 | |
312 | /** |
313 | * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter |
314 | * @adap: pointer to adapter |
315 | * |
316 | * Enumerate all I2C slave devices behind this adapter by walking the ACPI |
317 | * namespace. When a device is found it will be added to the Linux device |
318 | * model and bound to the corresponding ACPI handle. |
319 | */ |
320 | void i2c_acpi_register_devices(struct i2c_adapter *adap) |
321 | { |
322 | struct acpi_device *adev; |
323 | acpi_status status; |
324 | |
325 | if (!has_acpi_companion(dev: &adap->dev)) |
326 | return; |
327 | |
328 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
329 | I2C_ACPI_MAX_SCAN_DEPTH, |
330 | descending_callback: i2c_acpi_add_device, NULL, |
331 | context: adap, NULL); |
332 | if (ACPI_FAILURE(status)) |
333 | dev_warn(&adap->dev, "failed to enumerate I2C slaves\n" ); |
334 | |
335 | if (!adap->dev.parent) |
336 | return; |
337 | |
338 | adev = ACPI_COMPANION(adap->dev.parent); |
339 | if (!adev) |
340 | return; |
341 | |
342 | acpi_dev_clear_dependencies(supplier: adev); |
343 | } |
344 | |
345 | static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { |
346 | /* |
347 | * These Silead touchscreen controllers only work at 400KHz, for |
348 | * some reason they do not work at 100KHz. On some devices the ACPI |
349 | * tables list another device at their bus as only being capable |
350 | * of 100KHz, testing has shown that these other devices work fine |
351 | * at 400KHz (as can be expected of any recent i2c hw) so we force |
352 | * the speed of the bus to 400 KHz if a Silead device is present. |
353 | */ |
354 | { "MSSL1680" , 0 }, |
355 | {} |
356 | }; |
357 | |
358 | static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, |
359 | void *data, void **return_value) |
360 | { |
361 | struct i2c_acpi_lookup *lookup = data; |
362 | struct acpi_device *adev = acpi_fetch_acpi_dev(handle); |
363 | |
364 | if (!adev || i2c_acpi_do_lookup(adev, lookup)) |
365 | return AE_OK; |
366 | |
367 | if (lookup->search_handle != lookup->adapter_handle) |
368 | return AE_OK; |
369 | |
370 | if (lookup->speed <= lookup->min_speed) |
371 | lookup->min_speed = lookup->speed; |
372 | |
373 | if (acpi_match_device_ids(device: adev, ids: i2c_acpi_force_400khz_device_ids) == 0) |
374 | lookup->force_speed = I2C_MAX_FAST_MODE_FREQ; |
375 | |
376 | return AE_OK; |
377 | } |
378 | |
379 | /** |
380 | * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI |
381 | * @dev: The device owning the bus |
382 | * |
383 | * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves |
384 | * devices connected to this bus and use the speed of slowest device. |
385 | * |
386 | * Returns the speed in Hz or zero |
387 | */ |
388 | u32 i2c_acpi_find_bus_speed(struct device *dev) |
389 | { |
390 | struct i2c_acpi_lookup lookup; |
391 | struct i2c_board_info dummy; |
392 | acpi_status status; |
393 | |
394 | if (!has_acpi_companion(dev)) |
395 | return 0; |
396 | |
397 | memset(&lookup, 0, sizeof(lookup)); |
398 | lookup.search_handle = ACPI_HANDLE(dev); |
399 | lookup.min_speed = UINT_MAX; |
400 | lookup.info = &dummy; |
401 | lookup.index = -1; |
402 | |
403 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
404 | I2C_ACPI_MAX_SCAN_DEPTH, |
405 | descending_callback: i2c_acpi_lookup_speed, NULL, |
406 | context: &lookup, NULL); |
407 | |
408 | if (ACPI_FAILURE(status)) { |
409 | dev_warn(dev, "unable to find I2C bus speed from ACPI\n" ); |
410 | return 0; |
411 | } |
412 | |
413 | if (lookup.force_speed) { |
414 | if (lookup.force_speed != lookup.min_speed) |
415 | dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n" , |
416 | lookup.min_speed, lookup.force_speed); |
417 | return lookup.force_speed; |
418 | } else if (lookup.min_speed != UINT_MAX) { |
419 | return lookup.min_speed; |
420 | } else { |
421 | return 0; |
422 | } |
423 | } |
424 | EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); |
425 | |
426 | struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) |
427 | { |
428 | struct i2c_adapter *adapter; |
429 | struct device *dev; |
430 | |
431 | dev = bus_find_device(bus: &i2c_bus_type, NULL, data: handle, match: device_match_acpi_handle); |
432 | if (!dev) |
433 | return NULL; |
434 | |
435 | adapter = i2c_verify_adapter(dev); |
436 | if (!adapter) |
437 | put_device(dev); |
438 | |
439 | return adapter; |
440 | } |
441 | EXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle); |
442 | |
443 | static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) |
444 | { |
445 | return i2c_find_device_by_fwnode(fwnode: acpi_fwnode_handle(adev)); |
446 | } |
447 | |
448 | static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, |
449 | void *arg) |
450 | { |
451 | struct acpi_device *adev = arg; |
452 | struct i2c_board_info info; |
453 | acpi_handle adapter_handle; |
454 | struct i2c_adapter *adapter; |
455 | struct i2c_client *client; |
456 | |
457 | switch (value) { |
458 | case ACPI_RECONFIG_DEVICE_ADD: |
459 | if (i2c_acpi_get_info(adev, info: &info, NULL, adapter_handle: &adapter_handle)) |
460 | break; |
461 | |
462 | adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); |
463 | if (!adapter) |
464 | break; |
465 | |
466 | i2c_acpi_register_device(adapter, adev, info: &info); |
467 | put_device(dev: &adapter->dev); |
468 | break; |
469 | case ACPI_RECONFIG_DEVICE_REMOVE: |
470 | if (!acpi_device_enumerated(adev)) |
471 | break; |
472 | |
473 | client = i2c_acpi_find_client_by_adev(adev); |
474 | if (!client) |
475 | break; |
476 | |
477 | i2c_unregister_device(client); |
478 | put_device(dev: &client->dev); |
479 | break; |
480 | } |
481 | |
482 | return NOTIFY_OK; |
483 | } |
484 | |
485 | struct notifier_block i2c_acpi_notifier = { |
486 | .notifier_call = i2c_acpi_notify, |
487 | }; |
488 | |
489 | /** |
490 | * i2c_acpi_new_device_by_fwnode - Create i2c-client for the Nth I2cSerialBus resource |
491 | * @fwnode: fwnode with the ACPI resources to get the client from |
492 | * @index: Index of ACPI resource to get |
493 | * @info: describes the I2C device; note this is modified (addr gets set) |
494 | * Context: can sleep |
495 | * |
496 | * By default the i2c subsys creates an i2c-client for the first I2cSerialBus |
497 | * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus |
498 | * resources, in that case this function can be used to create an i2c-client |
499 | * for other I2cSerialBus resources in the Current Resource Settings table. |
500 | * |
501 | * Also see i2c_new_client_device, which this function calls to create the |
502 | * i2c-client. |
503 | * |
504 | * Returns a pointer to the new i2c-client, or error pointer in case of failure. |
505 | * Specifically, -EPROBE_DEFER is returned if the adapter is not found. |
506 | */ |
507 | struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode, |
508 | int index, |
509 | struct i2c_board_info *info) |
510 | { |
511 | struct i2c_acpi_lookup lookup; |
512 | struct i2c_adapter *adapter; |
513 | struct acpi_device *adev; |
514 | LIST_HEAD(resource_list); |
515 | int ret; |
516 | |
517 | adev = to_acpi_device_node(fwnode); |
518 | if (!adev) |
519 | return ERR_PTR(error: -ENODEV); |
520 | |
521 | memset(&lookup, 0, sizeof(lookup)); |
522 | lookup.info = info; |
523 | lookup.device_handle = acpi_device_handle(adev); |
524 | lookup.index = index; |
525 | |
526 | ret = acpi_dev_get_resources(adev, list: &resource_list, |
527 | preproc: i2c_acpi_fill_info, preproc_data: &lookup); |
528 | if (ret < 0) |
529 | return ERR_PTR(error: ret); |
530 | |
531 | acpi_dev_free_resource_list(list: &resource_list); |
532 | |
533 | if (!info->addr) |
534 | return ERR_PTR(error: -EADDRNOTAVAIL); |
535 | |
536 | adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle); |
537 | if (!adapter) |
538 | return ERR_PTR(error: -EPROBE_DEFER); |
539 | |
540 | return i2c_new_client_device(adap: adapter, info); |
541 | } |
542 | EXPORT_SYMBOL_GPL(i2c_acpi_new_device_by_fwnode); |
543 | |
544 | bool i2c_acpi_waive_d0_probe(struct device *dev) |
545 | { |
546 | struct i2c_driver *driver = to_i2c_driver(dev->driver); |
547 | struct acpi_device *adev = ACPI_COMPANION(dev); |
548 | |
549 | return driver->flags & I2C_DRV_ACPI_WAIVE_D0_PROBE && |
550 | adev && adev->power.state_for_enumeration >= adev->power.state; |
551 | } |
552 | EXPORT_SYMBOL_GPL(i2c_acpi_waive_d0_probe); |
553 | |
554 | #ifdef CONFIG_ACPI_I2C_OPREGION |
555 | static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, |
556 | u8 cmd, u8 *data, u8 data_len) |
557 | { |
558 | |
559 | struct i2c_msg msgs[2]; |
560 | int ret; |
561 | u8 *buffer; |
562 | |
563 | buffer = kzalloc(size: data_len, GFP_KERNEL); |
564 | if (!buffer) |
565 | return AE_NO_MEMORY; |
566 | |
567 | msgs[0].addr = client->addr; |
568 | msgs[0].flags = client->flags; |
569 | msgs[0].len = 1; |
570 | msgs[0].buf = &cmd; |
571 | |
572 | msgs[1].addr = client->addr; |
573 | msgs[1].flags = client->flags | I2C_M_RD; |
574 | msgs[1].len = data_len; |
575 | msgs[1].buf = buffer; |
576 | |
577 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
578 | if (ret < 0) { |
579 | /* Getting a NACK is unfortunately normal with some DSTDs */ |
580 | if (ret == -EREMOTEIO) |
581 | dev_dbg(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n" , |
582 | data_len, client->addr, cmd, ret); |
583 | else |
584 | dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n" , |
585 | data_len, client->addr, cmd, ret); |
586 | /* 2 transfers must have completed successfully */ |
587 | } else if (ret == 2) { |
588 | memcpy(data, buffer, data_len); |
589 | ret = 0; |
590 | } else { |
591 | ret = -EIO; |
592 | } |
593 | |
594 | kfree(objp: buffer); |
595 | return ret; |
596 | } |
597 | |
598 | static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, |
599 | u8 cmd, u8 *data, u8 data_len) |
600 | { |
601 | |
602 | struct i2c_msg msgs[1]; |
603 | u8 *buffer; |
604 | int ret = AE_OK; |
605 | |
606 | buffer = kzalloc(size: data_len + 1, GFP_KERNEL); |
607 | if (!buffer) |
608 | return AE_NO_MEMORY; |
609 | |
610 | buffer[0] = cmd; |
611 | memcpy(buffer + 1, data, data_len); |
612 | |
613 | msgs[0].addr = client->addr; |
614 | msgs[0].flags = client->flags; |
615 | msgs[0].len = data_len + 1; |
616 | msgs[0].buf = buffer; |
617 | |
618 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
619 | |
620 | kfree(objp: buffer); |
621 | |
622 | if (ret < 0) { |
623 | dev_err(&client->adapter->dev, "i2c write failed: %d\n" , ret); |
624 | return ret; |
625 | } |
626 | |
627 | /* 1 transfer must have completed successfully */ |
628 | return (ret == 1) ? 0 : -EIO; |
629 | } |
630 | |
631 | static acpi_status |
632 | i2c_acpi_space_handler(u32 function, acpi_physical_address command, |
633 | u32 bits, u64 *value64, |
634 | void *handler_context, void *region_context) |
635 | { |
636 | struct gsb_buffer *gsb = (struct gsb_buffer *)value64; |
637 | struct i2c_acpi_handler_data *data = handler_context; |
638 | struct acpi_connection_info *info = &data->info; |
639 | struct acpi_resource_i2c_serialbus *sb; |
640 | struct i2c_adapter *adapter = data->adapter; |
641 | struct i2c_client *client; |
642 | struct acpi_resource *ares; |
643 | u32 accessor_type = function >> 16; |
644 | u8 action = function & ACPI_IO_MASK; |
645 | acpi_status ret; |
646 | int status; |
647 | |
648 | ret = acpi_buffer_to_resource(aml_buffer: info->connection, aml_buffer_length: info->length, resource_ptr: &ares); |
649 | if (ACPI_FAILURE(ret)) |
650 | return ret; |
651 | |
652 | client = kzalloc(size: sizeof(*client), GFP_KERNEL); |
653 | if (!client) { |
654 | ret = AE_NO_MEMORY; |
655 | goto err; |
656 | } |
657 | |
658 | if (!value64 || !i2c_acpi_get_i2c_resource(ares, &sb)) { |
659 | ret = AE_BAD_PARAMETER; |
660 | goto err; |
661 | } |
662 | |
663 | client->adapter = adapter; |
664 | client->addr = sb->slave_address; |
665 | |
666 | if (sb->access_mode == ACPI_I2C_10BIT_MODE) |
667 | client->flags |= I2C_CLIENT_TEN; |
668 | |
669 | switch (accessor_type) { |
670 | case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: |
671 | if (action == ACPI_READ) { |
672 | status = i2c_smbus_read_byte(client); |
673 | if (status >= 0) { |
674 | gsb->bdata = status; |
675 | status = 0; |
676 | } |
677 | } else { |
678 | status = i2c_smbus_write_byte(client, value: gsb->bdata); |
679 | } |
680 | break; |
681 | |
682 | case ACPI_GSB_ACCESS_ATTRIB_BYTE: |
683 | if (action == ACPI_READ) { |
684 | status = i2c_smbus_read_byte_data(client, command); |
685 | if (status >= 0) { |
686 | gsb->bdata = status; |
687 | status = 0; |
688 | } |
689 | } else { |
690 | status = i2c_smbus_write_byte_data(client, command, |
691 | value: gsb->bdata); |
692 | } |
693 | break; |
694 | |
695 | case ACPI_GSB_ACCESS_ATTRIB_WORD: |
696 | if (action == ACPI_READ) { |
697 | status = i2c_smbus_read_word_data(client, command); |
698 | if (status >= 0) { |
699 | gsb->wdata = status; |
700 | status = 0; |
701 | } |
702 | } else { |
703 | status = i2c_smbus_write_word_data(client, command, |
704 | value: gsb->wdata); |
705 | } |
706 | break; |
707 | |
708 | case ACPI_GSB_ACCESS_ATTRIB_BLOCK: |
709 | if (action == ACPI_READ) { |
710 | status = i2c_smbus_read_block_data(client, command, |
711 | values: gsb->data); |
712 | if (status >= 0) { |
713 | gsb->len = status; |
714 | status = 0; |
715 | } |
716 | } else { |
717 | status = i2c_smbus_write_block_data(client, command, |
718 | length: gsb->len, values: gsb->data); |
719 | } |
720 | break; |
721 | |
722 | case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: |
723 | if (action == ACPI_READ) { |
724 | status = acpi_gsb_i2c_read_bytes(client, cmd: command, |
725 | data: gsb->data, data_len: info->access_length); |
726 | } else { |
727 | status = acpi_gsb_i2c_write_bytes(client, cmd: command, |
728 | data: gsb->data, data_len: info->access_length); |
729 | } |
730 | break; |
731 | |
732 | default: |
733 | dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n" , |
734 | accessor_type, client->addr); |
735 | ret = AE_BAD_PARAMETER; |
736 | goto err; |
737 | } |
738 | |
739 | gsb->status = status; |
740 | |
741 | err: |
742 | kfree(objp: client); |
743 | ACPI_FREE(ares); |
744 | return ret; |
745 | } |
746 | |
747 | |
748 | int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) |
749 | { |
750 | acpi_handle handle; |
751 | struct i2c_acpi_handler_data *data; |
752 | acpi_status status; |
753 | |
754 | if (!adapter->dev.parent) |
755 | return -ENODEV; |
756 | |
757 | handle = ACPI_HANDLE(adapter->dev.parent); |
758 | |
759 | if (!handle) |
760 | return -ENODEV; |
761 | |
762 | data = kzalloc(size: sizeof(struct i2c_acpi_handler_data), |
763 | GFP_KERNEL); |
764 | if (!data) |
765 | return -ENOMEM; |
766 | |
767 | data->adapter = adapter; |
768 | status = acpi_bus_attach_private_data(handle, (void *)data); |
769 | if (ACPI_FAILURE(status)) { |
770 | kfree(objp: data); |
771 | return -ENOMEM; |
772 | } |
773 | |
774 | status = acpi_install_address_space_handler(device: handle, |
775 | ACPI_ADR_SPACE_GSBUS, |
776 | handler: &i2c_acpi_space_handler, |
777 | NULL, |
778 | context: data); |
779 | if (ACPI_FAILURE(status)) { |
780 | dev_err(&adapter->dev, "Error installing i2c space handler\n" ); |
781 | acpi_bus_detach_private_data(handle); |
782 | kfree(objp: data); |
783 | return -ENOMEM; |
784 | } |
785 | |
786 | return 0; |
787 | } |
788 | |
789 | void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) |
790 | { |
791 | acpi_handle handle; |
792 | struct i2c_acpi_handler_data *data; |
793 | acpi_status status; |
794 | |
795 | if (!adapter->dev.parent) |
796 | return; |
797 | |
798 | handle = ACPI_HANDLE(adapter->dev.parent); |
799 | |
800 | if (!handle) |
801 | return; |
802 | |
803 | acpi_remove_address_space_handler(device: handle, |
804 | ACPI_ADR_SPACE_GSBUS, |
805 | handler: &i2c_acpi_space_handler); |
806 | |
807 | status = acpi_bus_get_private_data(handle, (void **)&data); |
808 | if (ACPI_SUCCESS(status)) |
809 | kfree(objp: data); |
810 | |
811 | acpi_bus_detach_private_data(handle); |
812 | } |
813 | #endif /* CONFIG_ACPI_I2C_OPREGION */ |
814 | |