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