1 /* $NetBSD: smbus_acpi.c,v 1.15 2021/01/29 15:49:55 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Paul Goyette 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 /* 33 * ACPI SMBus Controller driver 34 * 35 * See http://smbus.org/specs/smbus_cmi10.pdf for specifications 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: smbus_acpi.c,v 1.15 2021/01/29 15:49:55 thorpej Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/device.h> 43 #include <sys/callout.h> 44 #include <sys/kernel.h> 45 #include <sys/mutex.h> 46 #include <sys/systm.h> 47 48 #include <dev/acpi/acpireg.h> 49 #include <dev/acpi/acpivar.h> 50 51 #include <dev/i2c/i2cvar.h> 52 53 #define _COMPONENT ACPI_BUS_COMPONENT 54 ACPI_MODULE_NAME ("smbus_acpi") 55 56 /* 57 * ACPI SMBus CMI protocol codes. 58 */ 59 #define ACPI_SMBUS_RD_QUICK 0x03 60 #define ACPI_SMBUS_RCV_BYTE 0x05 61 #define ACPI_SMBUS_RD_BYTE 0x07 62 #define ACPI_SMBUS_RD_WORD 0x09 63 #define ACPI_SMBUS_RD_BLOCK 0x0B 64 #define ACPI_SMBUS_WR_QUICK 0x02 65 #define ACPI_SMBUS_SND_BYTE 0x04 66 #define ACPI_SMBUS_WR_BYTE 0x06 67 #define ACPI_SMBUS_WR_WORD 0x08 68 #define ACPI_SMBUS_WR_BLOCK 0x0A 69 #define ACPI_SMBUS_PROCESS_CALL 0x0C 70 71 struct acpi_smbus_softc { 72 struct acpi_devnode *sc_devnode; 73 struct callout sc_callout; 74 struct i2c_controller sc_i2c_tag; 75 device_t sc_dv; 76 int sc_poll_alert; 77 }; 78 79 static int acpi_smbus_match(device_t, cfdata_t, void *); 80 static void acpi_smbus_attach(device_t, device_t, void *); 81 static int acpi_smbus_detach(device_t, int); 82 static int acpi_smbus_poll_alert(ACPI_HANDLE, int *); 83 static int acpi_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *, 84 size_t, void *, size_t, int); 85 static void acpi_smbus_alerts(struct acpi_smbus_softc *); 86 static void acpi_smbus_tick(void *); 87 static void acpi_smbus_notify_handler(ACPI_HANDLE, uint32_t, void *); 88 89 struct SMB_UDID { 90 uint8_t dev_cap; 91 uint8_t vers_rev; 92 uint16_t vendor; 93 uint16_t device; 94 uint16_t interface; 95 uint16_t subsys_vendor; 96 uint16_t subsys_device; 97 uint8_t reserved[4]; 98 }; 99 100 struct SMB_DEVICE { 101 uint8_t slave_addr; 102 uint8_t reserved; 103 struct SMB_UDID dev_id; 104 }; 105 106 struct SMB_INFO { 107 uint8_t struct_ver; 108 uint8_t spec_ver; 109 uint8_t hw_cap; 110 uint8_t poll_int; 111 uint8_t dev_count; 112 struct SMB_DEVICE device[1]; 113 }; 114 115 static const struct device_compatible_entry compat_data[] = { 116 { .compat = "SMBUS01" }, /* SMBus CMI v1.0 */ 117 DEVICE_COMPAT_EOL 118 }; 119 120 CFATTACH_DECL_NEW(acpismbus, sizeof(struct acpi_smbus_softc), 121 acpi_smbus_match, acpi_smbus_attach, acpi_smbus_detach, NULL); 122 123 static int 124 acpi_smbus_match(device_t parent, cfdata_t match, void *aux) 125 { 126 struct acpi_attach_args *aa = aux; 127 int ret; 128 129 ret = acpi_compatible_match(aa, compat_data); 130 if (ret == 0) 131 return 0; 132 133 return acpi_smbus_poll_alert(aa->aa_node->ad_handle, NULL) ? ret : 0; 134 } 135 136 static void 137 acpi_smbus_attach(device_t parent, device_t self, void *aux) 138 { 139 struct acpi_smbus_softc *sc = device_private(self); 140 struct acpi_attach_args *aa = aux; 141 struct i2cbus_attach_args iba; 142 143 aprint_naive("\n"); 144 145 sc->sc_devnode = aa->aa_node; 146 sc->sc_dv = self; 147 sc->sc_poll_alert = 2; 148 149 /* Attach I2C bus. */ 150 iic_tag_init(&sc->sc_i2c_tag); 151 sc->sc_i2c_tag.ic_cookie = sc; 152 sc->sc_i2c_tag.ic_exec = acpi_smbus_exec; 153 154 (void)acpi_smbus_poll_alert(aa->aa_node->ad_handle,&sc->sc_poll_alert); 155 156 /* If failed, fall-back to polling. */ 157 if (acpi_register_notify(sc->sc_devnode, 158 acpi_smbus_notify_handler) != true) 159 sc->sc_poll_alert = 2; 160 161 callout_init(&sc->sc_callout, 0); 162 callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self); 163 164 if (sc->sc_poll_alert != 0) { 165 aprint_debug(": alert_poll %d sec", sc->sc_poll_alert); 166 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 167 } 168 169 aprint_normal("\n"); 170 171 (void)memset(&iba, 0, sizeof(iba)); 172 (void)pmf_device_register(self, NULL, NULL); 173 174 iba.iba_tag = &sc->sc_i2c_tag; 175 176 (void)config_found_ia(self, "i2cbus", &iba, iicbus_print); 177 } 178 179 static int 180 acpi_smbus_detach(device_t self, int flags) 181 { 182 struct acpi_smbus_softc *sc = device_private(self); 183 184 pmf_device_deregister(self); 185 acpi_deregister_notify(sc->sc_devnode); 186 187 callout_halt(&sc->sc_callout, NULL); 188 callout_destroy(&sc->sc_callout); 189 190 iic_tag_fini(&sc->sc_i2c_tag); 191 192 return 0; 193 } 194 195 static int 196 acpi_smbus_poll_alert(ACPI_HANDLE hdl, int *alert) 197 { 198 struct SMB_INFO *info; 199 ACPI_BUFFER smi_buf; 200 ACPI_OBJECT *e, *p; 201 ACPI_STATUS rv; 202 203 /* 204 * Retrieve polling interval for SMBus Alerts. 205 */ 206 rv = acpi_eval_struct(hdl, "_SBI", &smi_buf); 207 208 if (ACPI_FAILURE(rv)) 209 return 0; 210 211 p = smi_buf.Pointer; 212 213 if (p->Type != ACPI_TYPE_PACKAGE) { 214 rv = AE_TYPE; 215 goto out; 216 } 217 218 if (p->Package.Count == 0) { 219 rv = AE_LIMIT; 220 goto out; 221 } 222 223 e = p->Package.Elements; 224 225 if (e[0].Type != ACPI_TYPE_INTEGER) { 226 rv = AE_TYPE; 227 goto out; 228 } 229 230 /* Verify CMI version. */ 231 if (e[0].Integer.Value != 0x10) { 232 rv = AE_SUPPORT; 233 goto out; 234 } 235 236 if (alert != NULL) { 237 238 if (p->Package.Count < 2) 239 goto out; 240 241 if (e[1].Type != ACPI_TYPE_BUFFER) 242 goto out; 243 244 info = (struct SMB_INFO *)(e[1].Buffer.Pointer); 245 *alert = info->poll_int; 246 } 247 248 out: 249 if (smi_buf.Pointer != NULL) 250 ACPI_FREE(smi_buf.Pointer); 251 252 return (ACPI_FAILURE(rv)) ? 0 : 1; 253 } 254 255 static int 256 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 257 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 258 { 259 struct acpi_smbus_softc *sc = cookie; 260 const uint8_t *c = cmdbuf; 261 uint8_t *b = buf, *xb; 262 const char *path; 263 ACPI_OBJECT_LIST args; 264 ACPI_OBJECT arg[5]; 265 ACPI_OBJECT *p, *e; 266 ACPI_BUFFER smbuf; 267 ACPI_STATUS rv; 268 int i, r, xlen; 269 270 /* 271 * arg[0] : protocol 272 * arg[1] : slave address 273 * arg[2] : command 274 * arg[3] : data length 275 * arg[4] : data 276 */ 277 for (i = r = 0; i < __arraycount(arg); i++) 278 arg[i].Type = ACPI_TYPE_INTEGER; 279 280 args.Pointer = arg; 281 282 smbuf.Pointer = NULL; 283 smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 284 285 arg[1].Integer.Value = addr; 286 287 if (I2C_OP_READ_P(op)) { 288 289 path = "_SBR"; 290 args.Count = 3; 291 292 switch (len) { 293 294 case 0: 295 arg[0].Integer.Value = (cmdlen != 0) ? 296 ACPI_SMBUS_RCV_BYTE : ACPI_SMBUS_RD_QUICK; 297 298 arg[2].Integer.Value = 0; 299 break; 300 301 case 1: 302 arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE; 303 arg[2].Integer.Value = *c; 304 break; 305 306 case 2: 307 arg[0].Integer.Value = ACPI_SMBUS_RD_WORD; 308 arg[2].Integer.Value = *c; 309 break; 310 311 default: 312 arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK; 313 arg[2].Integer.Value = *c; 314 break; 315 } 316 317 } else { 318 319 path = "_SBW"; 320 args.Count = 5; 321 322 arg[3].Integer.Value = len; 323 324 switch (len) { 325 326 case 0: 327 if (cmdlen == 0) { 328 arg[2].Integer.Value = 0; 329 arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK; 330 } else { 331 arg[2].Integer.Value = *c; 332 arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE; 333 } 334 335 arg[4].Integer.Value = 0; 336 break; 337 338 case 1: 339 arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE; 340 arg[2].Integer.Value = *c; 341 arg[4].Integer.Value = *b; 342 break; 343 344 case 2: 345 arg[0].Integer.Value = ACPI_SMBUS_WR_WORD; 346 arg[2].Integer.Value = *c; 347 arg[4].Integer.Value = *b++; 348 arg[4].Integer.Value += (*b--) << 8; 349 break; 350 351 default: 352 arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK; 353 arg[2].Integer.Value = *c; 354 arg[4].Type = ACPI_TYPE_BUFFER; 355 arg[4].Buffer.Pointer = buf; 356 arg[4].Buffer.Length = (len < 32) ? len : 32; 357 break; 358 } 359 } 360 361 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, path, &args,&smbuf); 362 363 if (ACPI_FAILURE(rv)) 364 goto out; 365 366 p = smbuf.Pointer; 367 368 if (p->Type != ACPI_TYPE_PACKAGE) { 369 rv = AE_TYPE; 370 goto out; 371 } 372 373 if (p->Package.Count < 1) { 374 rv = AE_LIMIT; 375 goto out; 376 } 377 378 e = p->Package.Elements; 379 380 if (e->Type != ACPI_TYPE_INTEGER) { 381 rv = AE_TYPE; 382 goto out; 383 } 384 385 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, 386 "return status: %"PRIu64"\n", e[0].Integer.Value)); 387 388 if (e[0].Integer.Value != 0) { 389 rv = AE_BAD_VALUE; 390 goto out; 391 } 392 393 /* 394 * For read operations, copy data to user buffer. 395 */ 396 if (I2C_OP_READ_P(op)) { 397 398 if (p->Package.Count < 3) { 399 rv = AE_LIMIT; 400 goto out; 401 } 402 403 if (e[1].Type != ACPI_TYPE_INTEGER) { 404 rv = AE_TYPE; 405 goto out; 406 } 407 408 xlen = e[1].Integer.Value; 409 410 if (xlen > len) 411 xlen = len; 412 413 switch (e[2].Type) { 414 415 case ACPI_TYPE_BUFFER: 416 417 if (xlen == 0) { 418 rv = AE_LIMIT; 419 goto out; 420 } 421 422 xb = e[2].Buffer.Pointer; 423 424 if (xb == NULL) { 425 rv = AE_NULL_OBJECT; 426 goto out; 427 } 428 429 (void)memcpy(b, xb, xlen); 430 break; 431 432 case ACPI_TYPE_INTEGER: 433 434 if (xlen > 0) 435 *b++ = e[2].Integer.Value & 0xff; 436 437 if (xlen > 1) 438 *b = e[2].Integer.Value >> 8; 439 440 break; 441 442 default: 443 rv = AE_TYPE; 444 goto out; 445 } 446 } 447 448 out: 449 if (smbuf.Pointer != NULL) 450 ACPI_FREE(smbuf.Pointer); 451 452 if (ACPI_SUCCESS(rv)) 453 return 0; 454 455 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to " 456 "evaluate %s: %s\n", path, AcpiFormatException(rv))); 457 458 return 1; 459 } 460 461 /* 462 * Whether triggered by periodic polling or a Notify(), 463 * retrieve all pending SMBus device alerts. 464 */ 465 static void 466 acpi_smbus_alerts(struct acpi_smbus_softc *sc) 467 { 468 const ACPI_HANDLE hdl = sc->sc_devnode->ad_handle; 469 ACPI_OBJECT *e, *p; 470 ACPI_BUFFER alert; 471 ACPI_STATUS rv; 472 int status = 0; 473 uint8_t addr; 474 475 do { 476 rv = acpi_eval_struct(hdl, "_SBA", &alert); 477 478 if (ACPI_FAILURE(rv)) { 479 status = 1; 480 goto done; 481 } 482 483 p = alert.Pointer; 484 485 if (p->Type == ACPI_TYPE_PACKAGE && p->Package.Count >= 2) { 486 487 status = 1; 488 489 e = p->Package.Elements; 490 491 if (e[0].Type == ACPI_TYPE_INTEGER) 492 status = e[0].Integer.Value; 493 494 if (status == 0 && e[1].Type == ACPI_TYPE_INTEGER) { 495 addr = e[1].Integer.Value; 496 497 aprint_debug_dev(sc->sc_dv, 498 "alert for 0x%x\n", addr); 499 500 (void)iic_smbus_intr(&sc->sc_i2c_tag); 501 } 502 } 503 done: 504 if (alert.Pointer != NULL) 505 ACPI_FREE(alert.Pointer); 506 507 } while (status == 0); 508 } 509 510 static void 511 acpi_smbus_tick(void *opaque) 512 { 513 device_t dv = opaque; 514 struct acpi_smbus_softc *sc = device_private(dv); 515 516 acpi_smbus_alerts(sc); 517 518 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 519 } 520 521 static void 522 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 523 { 524 device_t dv = opaque; 525 struct acpi_smbus_softc *sc = device_private(dv); 526 527 aprint_debug_dev(dv, "received notify message 0x%x\n", notify); 528 529 acpi_smbus_alerts(sc); 530 } 531