1 /* $NetBSD: acpi_i2c.c,v 1.22 2025/09/18 02:51:04 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2017, 2021 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.22 2025/09/18 02:51:04 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 #include <dev/i2c/i2cvar.h> 45 46 #include <sys/kmem.h> 47 48 #define _COMPONENT ACPI_BUS_COMPONENT 49 ACPI_MODULE_NAME ("acpi_i2c") 50 51 struct acpi_i2c_address_space_context { 52 ACPI_CONNECTION_INFO conn_info; /* must be first */ 53 i2c_tag_t tag; 54 }; 55 56 static const struct device_compatible_entry hid_compat_data[] = { 57 { .compat = "PNP0C50" }, 58 DEVICE_COMPAT_EOL 59 }; 60 61 #if NIIC > 0 62 struct acpi_i2c_context { 63 uint16_t i2c_addr; 64 struct acpi_devnode *res_src; 65 }; 66 #endif 67 68 static struct acpi_devnode * 69 acpi_i2c_resource_find_source(ACPI_RESOURCE_SOURCE *rs) 70 { 71 ACPI_STATUS rv; 72 ACPI_HANDLE hdl; 73 struct acpi_devnode *ad; 74 75 if (rs->StringPtr == NULL) { 76 return NULL; 77 } 78 79 rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl); 80 if (ACPI_FAILURE(rv)) { 81 printf("%s: couldn't lookup '%s': %s\n", __func__, 82 rs->StringPtr, AcpiFormatException(rv)); 83 return NULL; 84 } 85 86 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) { 87 if (ad->ad_handle == hdl) { 88 return ad; 89 } 90 } 91 92 printf("%s: no acpi devnode matching resource source '%s'\n", 93 __func__, rs->StringPtr); 94 return NULL; 95 } 96 97 static ACPI_STATUS 98 acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context) 99 { 100 struct acpi_i2c_context *i2cc = context; 101 102 switch (res->Type) { 103 case ACPI_RESOURCE_TYPE_END_TAG: 104 break; 105 case ACPI_RESOURCE_TYPE_SERIAL_BUS: 106 switch (res->Data.I2cSerialBus.Type) { 107 case ACPI_RESOURCE_SERIAL_TYPE_I2C: 108 i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress; 109 i2cc->res_src = acpi_i2c_resource_find_source( 110 &res->Data.I2cSerialBus.ResourceSource); 111 break; 112 } 113 break; 114 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 115 break; 116 default: 117 break; 118 } 119 return_ACPI_STATUS(AE_OK); 120 } 121 122 static void 123 acpi_enter_i2c_device(struct acpi_devnode *ad, prop_array_t array) 124 { 125 prop_dictionary_t dev; 126 struct acpi_i2c_context i2cc; 127 ACPI_STATUS rv; 128 char *clist; 129 size_t clist_size; 130 devhandle_t child_devhandle; 131 132 memset(&i2cc, 0, sizeof(i2cc)); 133 rv = AcpiWalkResources(ad->ad_handle, "_CRS", 134 acpi_i2c_resource_parse_callback, &i2cc); 135 if (ACPI_FAILURE(rv)) { 136 return; 137 } 138 if (i2cc.i2c_addr == 0) 139 return; 140 dev = prop_dictionary_create(); 141 if (dev == NULL) { 142 aprint_error("ignoring device %s (no memory)\n", 143 ad->ad_name); 144 return; 145 } 146 clist = acpi_pack_compat_list(ad, &clist_size); 147 if (clist == NULL) { 148 prop_object_release(dev); 149 aprint_error("ignoring device %s (no _HID or _CID)\n", 150 ad->ad_name); 151 return; 152 } 153 prop_dictionary_set_string(dev, "name", ad->ad_name); 154 prop_dictionary_set_uint32(dev, "addr", i2cc.i2c_addr); 155 child_devhandle = devhandle_from_acpi(devhandle_invalid(), 156 ad->ad_handle); 157 prop_dictionary_set_data(dev, "devhandle", &child_devhandle, 158 sizeof(child_devhandle)); 159 prop_dictionary_set_data(dev, "compatible", clist, clist_size); 160 kmem_free(clist, clist_size); 161 162 prop_array_add(array, dev); 163 prop_object_release(dev); 164 } 165 166 static void 167 acpi_enter_i2chid_devs(device_t dev, struct acpi_devnode *devnode, 168 prop_array_t array) 169 { 170 struct acpi_devnode *ad; 171 172 KASSERT(dev != NULL); 173 174 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) { 175 struct acpi_attach_args aa = { 176 .aa_node = ad 177 }; 178 struct acpi_i2c_context i2cc; 179 ACPI_STATUS rv; 180 181 if (!acpi_device_present(ad->ad_handle)) 182 continue; 183 if (ad->ad_device != NULL) 184 continue; 185 if (acpi_compatible_match(&aa, hid_compat_data) == 0) 186 continue; 187 188 memset(&i2cc, 0, sizeof(i2cc)); 189 rv = AcpiWalkResources(ad->ad_handle, "_CRS", 190 acpi_i2c_resource_parse_callback, &i2cc); 191 if (ACPI_SUCCESS(rv) && 192 i2cc.i2c_addr != 0 && 193 i2cc.res_src == devnode) { 194 aprint_debug_dev(dev, "claiming %s\n", ad->ad_name); 195 ad->ad_device = dev; 196 acpi_claim_childdevs(dev, ad, NULL); 197 acpi_enter_i2c_device(ad, array); 198 } 199 } 200 } 201 202 prop_array_t 203 acpi_copy_i2c_devs(device_t dev) 204 { 205 struct acpi_devnode *ad; 206 ACPI_HANDLE *hdl = devhandle_to_acpi(device_handle(dev)); 207 struct acpi_devnode *devnode = acpi_match_node(hdl); 208 209 if (devnode == NULL) { 210 aprint_error_dev(dev, "%s: no devnode matching handle\n", 211 __func__); 212 return NULL; 213 } 214 215 prop_array_t array = prop_array_create(); 216 if (array == NULL) 217 return NULL; 218 219 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) { 220 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE) 221 continue; 222 if (!acpi_device_present(ad->ad_handle)) 223 continue; 224 acpi_enter_i2c_device(ad, array); 225 } 226 227 if (dev != NULL) { 228 acpi_claim_childdevs(dev, devnode, "_CRS"); 229 acpi_claim_childdevs(dev, devnode, "_ADR"); 230 acpi_enter_i2chid_devs(dev, devnode, array); 231 } 232 233 return array; 234 } 235 236 #if NIIC > 0 237 static ACPI_STATUS 238 acpi_i2c_gsb_init(ACPI_HANDLE region_hdl, UINT32 function, 239 void *handler_ctx, void **region_ctx) 240 { 241 if (function == ACPI_REGION_DEACTIVATE) { 242 *region_ctx = NULL; 243 } else { 244 *region_ctx = region_hdl; 245 } 246 return AE_OK; 247 } 248 249 static ACPI_STATUS 250 acpi_i2c_gsb_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 251 UINT32 bit_width, UINT64 *value, void *handler_ctx, 252 void *region_ctx) 253 { 254 ACPI_OPERAND_OBJECT *region_obj = region_ctx; 255 struct acpi_i2c_address_space_context *context = handler_ctx; 256 UINT8 *buf = ACPI_CAST_PTR(uint8_t, value); 257 ACPI_PHYSICAL_ADDRESS base_address; 258 ACPI_RESOURCE *res; 259 ACPI_STATUS rv; 260 ACPI_CONNECTION_INFO *conn_info = &context->conn_info; 261 i2c_tag_t tag = context->tag; 262 i2c_addr_t i2c_addr; 263 i2c_op_t op; 264 union { 265 uint8_t cmd8; 266 uint16_t cmd16; 267 uint32_t cmd32; 268 } cmd; 269 size_t buflen; 270 size_t cmdlen; 271 bool do_xfer = true; 272 273 if (region_obj->Region.Type != ACPI_TYPE_REGION) { 274 return AE_OK; 275 } 276 277 base_address = region_obj->Region.Address; 278 KASSERT(region_obj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS); 279 280 rv = AcpiBufferToResource(conn_info->Connection, conn_info->Length, 281 &res); 282 if (ACPI_FAILURE(rv)) { 283 return rv; 284 } 285 if (res->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS || 286 res->Data.CommonSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { 287 return AE_TYPE; 288 } 289 290 i2c_addr = res->Data.I2cSerialBus.SlaveAddress; 291 if ((function & ACPI_IO_MASK) != 0) { 292 op = I2C_OP_WRITE_WITH_STOP; 293 } else { 294 op = I2C_OP_READ_WITH_STOP; 295 } 296 297 #ifdef ACPI_I2C_DEBUG 298 UINT32 length; 299 rv = AcpiExGetProtocolBufferLength(function >> 16, &length); 300 if (ACPI_FAILURE(rv)) { 301 printf("%s AcpiExGetProtocolBufferLength failed: %s\n", 302 __func__, AcpiFormatException(rv)); 303 length = UINT32_MAX; 304 } 305 printf("%s %s: %s Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X", 306 __func__, AcpiUtGetRegionName(region_obj->Region.SpaceId), 307 (function & ACPI_IO_MASK) ? "Write" : "Read ", 308 (UINT32) (function >> 16), 309 (UINT32) address, (UINT32) base_address, 310 length, bit_width, buf[1]); 311 printf(" [AccessLength %.2X Connection %p]\n", 312 conn_info->AccessLength, conn_info->Connection); 313 #endif 314 315 switch ((UINT32)(function >> 16)) { 316 case AML_FIELD_ATTRIB_QUICK: 317 cmdlen = 0; 318 buflen = 0; 319 break; 320 case AML_FIELD_ATTRIB_SEND_RECEIVE: 321 cmdlen = 0; 322 buflen = 1; 323 break; 324 case AML_FIELD_ATTRIB_BYTE: 325 cmdlen = bit_width / NBBY; 326 buflen = 1; 327 break; 328 case AML_FIELD_ATTRIB_WORD: 329 cmdlen = bit_width / NBBY; 330 buflen = 2; 331 break; 332 case AML_FIELD_ATTRIB_BYTES: 333 cmdlen = bit_width / NBBY; 334 buflen = buf[1]; 335 break; 336 case AML_FIELD_ATTRIB_BLOCK: 337 cmdlen = bit_width / NBBY; 338 buflen = buf[1]; 339 op |= I2C_OPMASK_BLKMODE; 340 break; 341 case AML_FIELD_ATTRIB_RAW_BYTES: 342 case AML_FIELD_ATTRIB_RAW_PROCESS_BYTES: 343 case AML_FIELD_ATTRIB_PROCESS_CALL: 344 default: 345 cmdlen = 0; 346 do_xfer = false; 347 #ifdef ACPI_I2C_DEBUG 348 printf("field attrib 0x%x not supported\n", 349 (UINT32)(function >> 16)); 350 #endif 351 break; 352 } 353 354 switch (cmdlen) { 355 case 0: 356 case 1: 357 cmd.cmd8 = (uint8_t)(base_address + address); 358 break; 359 case 2: 360 cmd.cmd16 = (uint16_t)(base_address + address); 361 break; 362 case 4: 363 cmd.cmd32 = (uint32_t)(base_address + address); 364 break; 365 default: 366 do_xfer = false; 367 #ifdef ACPI_I2C_DEBUG 368 printf("cmdlen %zu not supported\n", cmdlen); 369 #endif 370 break; 371 } 372 373 if (!do_xfer) { 374 buf[0] = EINVAL; 375 } else { 376 const int flags = I2C_F_POLL; 377 iic_acquire_bus(tag, flags); 378 buf[0] = iic_exec(tag, op, i2c_addr, 379 &cmd, cmdlen, &buf[2], buflen, flags); 380 iic_release_bus(tag, flags); 381 if (buf[0] == 0) { 382 buf[1] = buflen; 383 } 384 #ifdef ACPI_I2C_DEBUG 385 printf("%s iic_exec op %u addr 0x%x len %zu/%zu returned %d\n", 386 __func__, op, res->Data.I2cSerialBus.SlaveAddress, cmdlen, 387 buflen, buf[0]); 388 #endif 389 } 390 391 ACPI_FREE(res); 392 393 return AE_OK; 394 } 395 #endif 396 397 void 398 acpi_i2c_register(device_t dev, i2c_tag_t tag) 399 { 400 #if NIIC > 0 401 ACPI_HANDLE hdl = devhandle_to_acpi(device_handle(dev)); 402 struct acpi_i2c_address_space_context *context; 403 ACPI_STATUS rv; 404 405 context = kmem_zalloc(sizeof(*context), KM_SLEEP); 406 context->tag = tag; 407 408 rv = AcpiInstallAddressSpaceHandler(hdl, 409 ACPI_ADR_SPACE_GSBUS, acpi_i2c_gsb_handler, acpi_i2c_gsb_init, 410 context); 411 if (ACPI_FAILURE(rv)) { 412 aprint_error_dev(dev, 413 "couldn't install address space handler: %s", 414 AcpiFormatException(rv)); 415 } 416 #endif 417 } 418