acpi_i2c.c revision 1.23 1 /* $NetBSD: acpi_i2c.c,v 1.23 2025/09/23 06:28:19 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2017, 2021, 2025 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Manuel Bouyer.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "iic.h"
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v 1.23 2025/09/23 06:28:19 thorpej Exp $");
36
37 #include <sys/device.h>
38
39 #include <dev/acpi/acpireg.h>
40 #include <dev/acpi/acpivar.h>
41 #include <dev/acpi/acpi_i2c.h>
42 #include <external/bsd/acpica/dist/include/acinterp.h>
43 #include <external/bsd/acpica/dist/include/amlcode.h>
44
45 #include <dev/i2c/i2cvar.h>
46 #include <dev/i2c/i2c_calls.h>
47 #include <dev/i2c/i2c_enum.h>
48
49 #include <sys/kmem.h>
50
51 #define _COMPONENT ACPI_BUS_COMPONENT
52 ACPI_MODULE_NAME ("acpi_i2c")
53
54 struct acpi_i2c_address_space_context {
55 ACPI_CONNECTION_INFO conn_info; /* must be first */
56 i2c_tag_t tag;
57 };
58
59 static const struct device_compatible_entry hid_compat_data[] = {
60 { .compat = "PNP0C50" },
61 DEVICE_COMPAT_EOL
62 };
63
64 #if NIIC > 0
65 struct acpi_i2c_context {
66 uint16_t i2c_addr;
67 struct acpi_devnode *res_src;
68 };
69 #endif
70
71 static struct acpi_devnode *
72 acpi_i2c_resource_find_source(ACPI_RESOURCE_SOURCE *rs)
73 {
74 ACPI_STATUS rv;
75 ACPI_HANDLE hdl;
76 struct acpi_devnode *ad;
77
78 if (rs->StringPtr == NULL) {
79 return NULL;
80 }
81
82 rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
83 if (ACPI_FAILURE(rv)) {
84 printf("%s: couldn't lookup '%s': %s\n", __func__,
85 rs->StringPtr, AcpiFormatException(rv));
86 return NULL;
87 }
88
89 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
90 if (ad->ad_handle == hdl) {
91 return ad;
92 }
93 }
94
95 printf("%s: no acpi devnode matching resource source '%s'\n",
96 __func__, rs->StringPtr);
97 return NULL;
98 }
99
100 static ACPI_STATUS
101 acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context)
102 {
103 struct acpi_i2c_context *i2cc = context;
104
105 switch (res->Type) {
106 case ACPI_RESOURCE_TYPE_END_TAG:
107 break;
108 case ACPI_RESOURCE_TYPE_SERIAL_BUS:
109 switch (res->Data.I2cSerialBus.Type) {
110 case ACPI_RESOURCE_SERIAL_TYPE_I2C:
111 i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
112 i2cc->res_src = acpi_i2c_resource_find_source(
113 &res->Data.I2cSerialBus.ResourceSource);
114 break;
115 }
116 break;
117 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
118 break;
119 default:
120 break;
121 }
122 return_ACPI_STATUS(AE_OK);
123 }
124
125 static bool
126 acpi_i2c_enumerate_device(device_t dev, struct acpi_devnode *ad,
127 struct i2c_enumerate_devices_args * const args)
128 {
129 struct acpi_i2c_context i2cc;
130 ACPI_STATUS rv;
131 char *clist;
132 size_t clist_size;
133 bool cbrv;
134
135 memset(&i2cc, 0, sizeof(i2cc));
136 rv = AcpiWalkResources(ad->ad_handle, "_CRS",
137 acpi_i2c_resource_parse_callback, &i2cc);
138 if (ACPI_FAILURE(rv)) {
139 aprint_error_dev(dev,
140 "ACPI: unable to get resources for %s: %s\n",
141 ad->ad_name, AcpiFormatException(rv));
142 return true; /* keep enumerating */
143 }
144 if (i2cc.i2c_addr == 0) {
145 return true; /* keep enumerating */
146 }
147
148 clist = acpi_pack_compat_list(ad, &clist_size);
149 if (clist == NULL) {
150 aprint_error_dev(dev,
151 "ACPI: ignoring device %s (no _HID or _CID)\n",
152 ad->ad_name);
153 return true; /* keep enumerating */
154 }
155
156 cbrv = i2c_enumerate_device(dev, args, ad->ad_name,
157 clist, clist_size, i2cc.i2c_addr,
158 devhandle_from_acpi(device_handle(dev), ad->ad_handle));
159
160 kmem_free(clist, clist_size);
161
162 return cbrv; /* callback decides if we keep enumerating */
163 }
164
165 static void
166 acpi_i2c_enumerate_hid_devs(device_t dev, struct acpi_devnode *devnode,
167 struct i2c_enumerate_devices_args * const args)
168 {
169 struct acpi_devnode *ad;
170
171 KASSERT(dev != NULL);
172
173 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
174 struct acpi_attach_args aa = {
175 .aa_node = ad
176 };
177 struct acpi_i2c_context i2cc;
178 ACPI_STATUS rv;
179
180 if (!acpi_device_present(ad->ad_handle))
181 continue;
182 /*
183 * We don't know why the caller is enumerating, so
184 * in addition to checking of the node hasn't been
185 * claimed, we check to see if it's been claimed by
186 * ourselves already and let those through as well.
187 */
188 if (ad->ad_device != NULL && ad->ad_device != dev)
189 continue;
190 if (acpi_compatible_match(&aa, hid_compat_data) == 0)
191 continue;
192
193 memset(&i2cc, 0, sizeof(i2cc));
194 rv = AcpiWalkResources(ad->ad_handle, "_CRS",
195 acpi_i2c_resource_parse_callback, &i2cc);
196 if (ACPI_SUCCESS(rv) &&
197 i2cc.i2c_addr != 0 &&
198 i2cc.res_src == devnode) {
199 aprint_debug_dev(dev, "claiming %s\n", ad->ad_name);
200 ad->ad_device = dev;
201 acpi_claim_childdevs(dev, ad, NULL);
202 if (!acpi_i2c_enumerate_device(dev, ad, args))
203 break;
204 }
205 }
206 }
207
208 static int
209 acpi_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v)
210 {
211 struct i2c_enumerate_devices_args *args = v;
212 ACPI_HANDLE *hdl = devhandle_to_acpi(device_handle(dev));
213 struct acpi_devnode *ad, *devnode = acpi_match_node(hdl);
214
215 KASSERT(dev != NULL);
216
217 if (devnode == NULL) {
218 aprint_error_dev(dev, "%s: no devnode matching handle\n",
219 __func__);
220 return 0;
221 }
222
223 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
224 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
225 continue;
226 if (!acpi_device_present(ad->ad_handle))
227 continue;
228 if (!acpi_i2c_enumerate_device(dev, ad, args))
229 break;
230 }
231
232 acpi_claim_childdevs(dev, devnode, "_CRS");
233 acpi_claim_childdevs(dev, devnode, "_ADR");
234
235 /*
236 * I2C HID devices might not be children of the controller in the
237 * ACPI device tree; go look for those separately.
238 */
239 acpi_i2c_enumerate_hid_devs(dev, devnode, args);
240
241 return 0;
242 }
243 ACPI_DEVICE_CALL_REGISTER(I2C_ENUMERATE_DEVICES_STR,
244 acpi_i2c_enumerate_devices)
245
246 #if NIIC > 0
247 static ACPI_STATUS
248 acpi_i2c_gsb_init(ACPI_HANDLE region_hdl, UINT32 function,
249 void *handler_ctx, void **region_ctx)
250 {
251 if (function == ACPI_REGION_DEACTIVATE) {
252 *region_ctx = NULL;
253 } else {
254 *region_ctx = region_hdl;
255 }
256 return AE_OK;
257 }
258
259 static ACPI_STATUS
260 acpi_i2c_gsb_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
261 UINT32 bit_width, UINT64 *value, void *handler_ctx,
262 void *region_ctx)
263 {
264 ACPI_OPERAND_OBJECT *region_obj = region_ctx;
265 struct acpi_i2c_address_space_context *context = handler_ctx;
266 UINT8 *buf = ACPI_CAST_PTR(uint8_t, value);
267 ACPI_PHYSICAL_ADDRESS base_address;
268 ACPI_RESOURCE *res;
269 ACPI_STATUS rv;
270 ACPI_CONNECTION_INFO *conn_info = &context->conn_info;
271 i2c_tag_t tag = context->tag;
272 i2c_addr_t i2c_addr;
273 i2c_op_t op;
274 union {
275 uint8_t cmd8;
276 uint16_t cmd16;
277 uint32_t cmd32;
278 } cmd;
279 size_t buflen;
280 size_t cmdlen;
281 bool do_xfer = true;
282
283 if (region_obj->Region.Type != ACPI_TYPE_REGION) {
284 return AE_OK;
285 }
286
287 base_address = region_obj->Region.Address;
288 KASSERT(region_obj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS);
289
290 rv = AcpiBufferToResource(conn_info->Connection, conn_info->Length,
291 &res);
292 if (ACPI_FAILURE(rv)) {
293 return rv;
294 }
295 if (res->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS ||
296 res->Data.CommonSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
297 return AE_TYPE;
298 }
299
300 i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
301 if ((function & ACPI_IO_MASK) != 0) {
302 op = I2C_OP_WRITE_WITH_STOP;
303 } else {
304 op = I2C_OP_READ_WITH_STOP;
305 }
306
307 #ifdef ACPI_I2C_DEBUG
308 UINT32 length;
309 rv = AcpiExGetProtocolBufferLength(function >> 16, &length);
310 if (ACPI_FAILURE(rv)) {
311 printf("%s AcpiExGetProtocolBufferLength failed: %s\n",
312 __func__, AcpiFormatException(rv));
313 length = UINT32_MAX;
314 }
315 printf("%s %s: %s Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X",
316 __func__, AcpiUtGetRegionName(region_obj->Region.SpaceId),
317 (function & ACPI_IO_MASK) ? "Write" : "Read ",
318 (UINT32) (function >> 16),
319 (UINT32) address, (UINT32) base_address,
320 length, bit_width, buf[1]);
321 printf(" [AccessLength %.2X Connection %p]\n",
322 conn_info->AccessLength, conn_info->Connection);
323 #endif
324
325 switch ((UINT32)(function >> 16)) {
326 case AML_FIELD_ATTRIB_QUICK:
327 cmdlen = 0;
328 buflen = 0;
329 break;
330 case AML_FIELD_ATTRIB_SEND_RECEIVE:
331 cmdlen = 0;
332 buflen = 1;
333 break;
334 case AML_FIELD_ATTRIB_BYTE:
335 cmdlen = bit_width / NBBY;
336 buflen = 1;
337 break;
338 case AML_FIELD_ATTRIB_WORD:
339 cmdlen = bit_width / NBBY;
340 buflen = 2;
341 break;
342 case AML_FIELD_ATTRIB_BYTES:
343 cmdlen = bit_width / NBBY;
344 buflen = buf[1];
345 break;
346 case AML_FIELD_ATTRIB_BLOCK:
347 cmdlen = bit_width / NBBY;
348 buflen = buf[1];
349 op |= I2C_OPMASK_BLKMODE;
350 break;
351 case AML_FIELD_ATTRIB_RAW_BYTES:
352 case AML_FIELD_ATTRIB_RAW_PROCESS_BYTES:
353 case AML_FIELD_ATTRIB_PROCESS_CALL:
354 default:
355 cmdlen = 0;
356 do_xfer = false;
357 #ifdef ACPI_I2C_DEBUG
358 printf("field attrib 0x%x not supported\n",
359 (UINT32)(function >> 16));
360 #endif
361 break;
362 }
363
364 switch (cmdlen) {
365 case 0:
366 case 1:
367 cmd.cmd8 = (uint8_t)(base_address + address);
368 break;
369 case 2:
370 cmd.cmd16 = (uint16_t)(base_address + address);
371 break;
372 case 4:
373 cmd.cmd32 = (uint32_t)(base_address + address);
374 break;
375 default:
376 do_xfer = false;
377 #ifdef ACPI_I2C_DEBUG
378 printf("cmdlen %zu not supported\n", cmdlen);
379 #endif
380 break;
381 }
382
383 if (!do_xfer) {
384 buf[0] = EINVAL;
385 } else {
386 const int flags = I2C_F_POLL;
387 iic_acquire_bus(tag, flags);
388 buf[0] = iic_exec(tag, op, i2c_addr,
389 &cmd, cmdlen, &buf[2], buflen, flags);
390 iic_release_bus(tag, flags);
391 if (buf[0] == 0) {
392 buf[1] = buflen;
393 }
394 #ifdef ACPI_I2C_DEBUG
395 printf("%s iic_exec op %u addr 0x%x len %zu/%zu returned %d\n",
396 __func__, op, res->Data.I2cSerialBus.SlaveAddress, cmdlen,
397 buflen, buf[0]);
398 #endif
399 }
400
401 ACPI_FREE(res);
402
403 return AE_OK;
404 }
405 #endif
406
407 void
408 acpi_i2c_register(device_t dev, i2c_tag_t tag)
409 {
410 #if NIIC > 0
411 ACPI_HANDLE hdl = devhandle_to_acpi(device_handle(dev));
412 struct acpi_i2c_address_space_context *context;
413 ACPI_STATUS rv;
414
415 context = kmem_zalloc(sizeof(*context), KM_SLEEP);
416 context->tag = tag;
417
418 rv = AcpiInstallAddressSpaceHandler(hdl,
419 ACPI_ADR_SPACE_GSBUS, acpi_i2c_gsb_handler, acpi_i2c_gsb_init,
420 context);
421 if (ACPI_FAILURE(rv)) {
422 aprint_error_dev(dev,
423 "couldn't install address space handler: %s",
424 AcpiFormatException(rv));
425 }
426 #endif
427 }
428