1 /* $NetBSD: acpi_i2c.c,v 1.19 2025/01/11 11:40:43 jmcneill 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.19 2025/01/11 11:40:43 jmcneill 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 131 memset(&i2cc, 0, sizeof(i2cc)); 132 rv = AcpiWalkResources(ad->ad_handle, "_CRS", 133 acpi_i2c_resource_parse_callback, &i2cc); 134 if (ACPI_FAILURE(rv)) { 135 return; 136 } 137 if (i2cc.i2c_addr == 0) 138 return; 139 dev = prop_dictionary_create(); 140 if (dev == NULL) { 141 aprint_error("ignoring device %s (no memory)\n", 142 ad->ad_name); 143 return; 144 } 145 clist = acpi_pack_compat_list(ad, &clist_size); 146 if (clist == NULL) { 147 prop_object_release(dev); 148 aprint_error("ignoring device %s (no _HID or _CID)\n", 149 ad->ad_name); 150 return; 151 } 152 prop_dictionary_set_string(dev, "name", ad->ad_name); 153 prop_dictionary_set_uint32(dev, "addr", i2cc.i2c_addr); 154 prop_dictionary_set_uint64(dev, "cookie", (uintptr_t)ad->ad_handle); 155 prop_dictionary_set_uint32(dev, "cookietype", I2C_COOKIE_ACPI); 156 prop_dictionary_set_data(dev, "compatible", clist, clist_size); 157 kmem_free(clist, clist_size); 158 159 prop_array_add(array, dev); 160 prop_object_release(dev); 161 } 162 163 static void 164 acpi_enter_i2chid_devs(device_t dev, struct acpi_devnode *devnode, 165 prop_array_t array) 166 { 167 struct acpi_devnode *ad; 168 169 KASSERT(dev != NULL); 170 171 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) { 172 struct acpi_attach_args aa = { 173 .aa_node = ad 174 }; 175 struct acpi_i2c_context i2cc; 176 ACPI_STATUS rv; 177 178 if (!acpi_device_present(ad->ad_handle)) 179 continue; 180 if (ad->ad_device != NULL) 181 continue; 182 if (acpi_compatible_match(&aa, hid_compat_data) == 0) 183 continue; 184 185 memset(&i2cc, 0, sizeof(i2cc)); 186 rv = AcpiWalkResources(ad->ad_handle, "_CRS", 187 acpi_i2c_resource_parse_callback, &i2cc); 188 if (ACPI_SUCCESS(rv) && 189 i2cc.i2c_addr != 0 && 190 i2cc.res_src == devnode) { 191 aprint_debug_dev(dev, "claiming %s\n", ad->ad_name); 192 ad->ad_device = dev; 193 acpi_claim_childdevs(dev, ad, NULL); 194 acpi_enter_i2c_device(ad, array); 195 } 196 } 197 } 198 199 prop_array_t 200 acpi_enter_i2c_devs(device_t dev, struct acpi_devnode *devnode) 201 { 202 struct acpi_devnode *ad; 203 prop_array_t array = prop_array_create(); 204 205 if (array == NULL) 206 return NULL; 207 208 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) { 209 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE) 210 continue; 211 if (!acpi_device_present(ad->ad_handle)) 212 continue; 213 acpi_enter_i2c_device(ad, array); 214 } 215 216 if (dev != NULL) { 217 acpi_claim_childdevs(dev, devnode, "_CRS"); 218 acpi_claim_childdevs(dev, devnode, "_ADR"); 219 acpi_enter_i2chid_devs(dev, devnode, array); 220 } 221 222 return array; 223 } 224 225 #if NIIC > 0 226 static ACPI_STATUS 227 acpi_i2c_gsb_init(ACPI_HANDLE region_hdl, UINT32 function, 228 void *handler_ctx, void **region_ctx) 229 { 230 if (function == ACPI_REGION_DEACTIVATE) { 231 *region_ctx = NULL; 232 } else { 233 *region_ctx = region_hdl; 234 } 235 return AE_OK; 236 } 237 238 static ACPI_STATUS 239 acpi_i2c_gsb_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 240 UINT32 bit_width, UINT64 *value, void *handler_ctx, 241 void *region_ctx) 242 { 243 ACPI_OPERAND_OBJECT *region_obj = region_ctx; 244 struct acpi_i2c_address_space_context *context = handler_ctx; 245 UINT8 *buf = ACPI_CAST_PTR(uint8_t, value); 246 ACPI_PHYSICAL_ADDRESS base_address; 247 ACPI_RESOURCE *res; 248 ACPI_STATUS rv; 249 ACPI_CONNECTION_INFO *conn_info = &context->conn_info; 250 i2c_tag_t tag = context->tag; 251 i2c_addr_t i2c_addr; 252 i2c_op_t op; 253 union { 254 uint8_t cmd8; 255 uint16_t cmd16; 256 uint32_t cmd32; 257 } cmd; 258 size_t buflen; 259 size_t cmdlen; 260 bool do_xfer = true; 261 262 if (region_obj->Region.Type != ACPI_TYPE_REGION) { 263 return AE_OK; 264 } 265 266 base_address = region_obj->Region.Address; 267 KASSERT(region_obj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS); 268 269 rv = AcpiBufferToResource(conn_info->Connection, conn_info->Length, 270 &res); 271 if (ACPI_FAILURE(rv)) { 272 return rv; 273 } 274 if (res->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS || 275 res->Data.CommonSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { 276 return AE_TYPE; 277 } 278 279 i2c_addr = res->Data.I2cSerialBus.SlaveAddress; 280 if ((function & ACPI_IO_MASK) != 0) { 281 op = I2C_OP_WRITE_WITH_STOP; 282 } else { 283 op = I2C_OP_READ_WITH_STOP; 284 } 285 286 #ifdef ACPI_I2C_DEBUG 287 UINT32 length; 288 rv = AcpiExGetProtocolBufferLength(function >> 16, &length); 289 if (ACPI_FAILURE(rv)) { 290 printf("%s AcpiExGetProtocolBufferLength failed: %s\n", 291 __func__, AcpiFormatException(rv)); 292 length = UINT32_MAX; 293 } 294 printf("%s %s: %s Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X", 295 __func__, AcpiUtGetRegionName(region_obj->Region.SpaceId), 296 (function & ACPI_IO_MASK) ? "Write" : "Read ", 297 (UINT32) (function >> 16), 298 (UINT32) address, (UINT32) base_address, 299 length, bit_width, buf[1]); 300 printf(" [AccessLength %.2X Connection %p]\n", 301 conn_info->AccessLength, conn_info->Connection); 302 #endif 303 304 switch ((UINT32)(function >> 16)) { 305 case AML_FIELD_ATTRIB_QUICK: 306 cmdlen = 0; 307 buflen = 0; 308 break; 309 case AML_FIELD_ATTRIB_SEND_RECEIVE: 310 cmdlen = 0; 311 buflen = 1; 312 break; 313 case AML_FIELD_ATTRIB_BYTE: 314 cmdlen = bit_width / NBBY; 315 buflen = 1; 316 break; 317 case AML_FIELD_ATTRIB_WORD: 318 cmdlen = bit_width / NBBY; 319 buflen = 2; 320 break; 321 case AML_FIELD_ATTRIB_BYTES: 322 cmdlen = bit_width / NBBY; 323 buflen = buf[1]; 324 break; 325 case AML_FIELD_ATTRIB_BLOCK: 326 cmdlen = bit_width / NBBY; 327 buflen = buf[1]; 328 op |= I2C_OPMASK_BLKMODE; 329 break; 330 case AML_FIELD_ATTRIB_RAW_BYTES: 331 case AML_FIELD_ATTRIB_RAW_PROCESS_BYTES: 332 case AML_FIELD_ATTRIB_PROCESS_CALL: 333 default: 334 cmdlen = 0; 335 do_xfer = false; 336 #ifdef ACPI_I2C_DEBUG 337 printf("field attrib 0x%x not supported\n", 338 (UINT32)(function >> 16)); 339 #endif 340 break; 341 } 342 343 switch (cmdlen) { 344 case 0: 345 case 1: 346 cmd.cmd8 = (uint8_t)(base_address + address); 347 break; 348 case 2: 349 cmd.cmd16 = (uint16_t)(base_address + address); 350 break; 351 case 4: 352 cmd.cmd32 = (uint32_t)(base_address + address); 353 break; 354 default: 355 do_xfer = false; 356 #ifdef ACPI_I2C_DEBUG 357 printf("cmdlen %zu not supported\n", cmdlen); 358 #endif 359 break; 360 } 361 362 if (!do_xfer) { 363 buf[0] = EINVAL; 364 } else { 365 const int flags = I2C_F_POLL; 366 iic_acquire_bus(tag, flags); 367 buf[0] = iic_exec(tag, op, i2c_addr, 368 &cmd, cmdlen, &buf[2], buflen, flags); 369 iic_release_bus(tag, flags); 370 if (buf[0] == 0) { 371 buf[1] = buflen; 372 } 373 #ifdef ACPI_I2C_DEBUG 374 printf("%s iic_exec op %u addr 0x%x len %zu/%zu returned %d\n", 375 __func__, op, res->Data.I2cSerialBus.SlaveAddress, cmdlen, 376 buflen, buf[0]); 377 #endif 378 } 379 380 ACPI_FREE(res); 381 382 return AE_OK; 383 } 384 #endif 385 386 ACPI_STATUS 387 acpi_i2c_register(struct acpi_devnode *devnode, device_t dev, i2c_tag_t tag) 388 { 389 #if NIIC > 0 390 struct acpi_i2c_address_space_context *context; 391 ACPI_STATUS rv; 392 393 context = kmem_zalloc(sizeof(*context), KM_SLEEP); 394 context->tag = tag; 395 396 rv = AcpiInstallAddressSpaceHandler(devnode->ad_handle, 397 ACPI_ADR_SPACE_GSBUS, acpi_i2c_gsb_handler, acpi_i2c_gsb_init, 398 context); 399 if (ACPI_FAILURE(rv)) { 400 aprint_error_dev(dev, 401 "couldn't install address space handler: %s", 402 AcpiFormatException(rv)); 403 } 404 405 return rv; 406 #else 407 return AE_NOT_CONFIGURED; 408 #endif 409 } 410