1 /* $NetBSD: smbus_acpi.c,v 1.14 2019/12/22 23:23:32 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.14 2019/12/22 23:23:32 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 char * const smbus_acpi_ids[] = { 116 "SMBUS01", /* SMBus CMI v1.0 */ 117 NULL 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 128 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 129 return 0; 130 131 if (acpi_match_hid(aa->aa_node->ad_devinfo, smbus_acpi_ids) == 0) 132 return 0; 133 134 return acpi_smbus_poll_alert(aa->aa_node->ad_handle, NULL); 135 } 136 137 static void 138 acpi_smbus_attach(device_t parent, device_t self, void *aux) 139 { 140 struct acpi_smbus_softc *sc = device_private(self); 141 struct acpi_attach_args *aa = aux; 142 struct i2cbus_attach_args iba; 143 144 aprint_naive("\n"); 145 146 sc->sc_devnode = aa->aa_node; 147 sc->sc_dv = self; 148 sc->sc_poll_alert = 2; 149 150 /* Attach I2C bus. */ 151 iic_tag_init(&sc->sc_i2c_tag); 152 sc->sc_i2c_tag.ic_cookie = sc; 153 sc->sc_i2c_tag.ic_exec = acpi_smbus_exec; 154 155 (void)acpi_smbus_poll_alert(aa->aa_node->ad_handle,&sc->sc_poll_alert); 156 157 /* If failed, fall-back to polling. */ 158 if (acpi_register_notify(sc->sc_devnode, 159 acpi_smbus_notify_handler) != true) 160 sc->sc_poll_alert = 2; 161 162 callout_init(&sc->sc_callout, 0); 163 callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self); 164 165 if (sc->sc_poll_alert != 0) { 166 aprint_debug(": alert_poll %d sec", sc->sc_poll_alert); 167 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 168 } 169 170 aprint_normal("\n"); 171 172 (void)memset(&iba, 0, sizeof(iba)); 173 (void)pmf_device_register(self, NULL, NULL); 174 175 iba.iba_tag = &sc->sc_i2c_tag; 176 177 (void)config_found_ia(self, "i2cbus", &iba, iicbus_print); 178 } 179 180 static int 181 acpi_smbus_detach(device_t self, int flags) 182 { 183 struct acpi_smbus_softc *sc = device_private(self); 184 185 pmf_device_deregister(self); 186 acpi_deregister_notify(sc->sc_devnode); 187 188 callout_halt(&sc->sc_callout, NULL); 189 callout_destroy(&sc->sc_callout); 190 191 iic_tag_fini(&sc->sc_i2c_tag); 192 193 return 0; 194 } 195 196 static int 197 acpi_smbus_poll_alert(ACPI_HANDLE hdl, int *alert) 198 { 199 struct SMB_INFO *info; 200 ACPI_BUFFER smi_buf; 201 ACPI_OBJECT *e, *p; 202 ACPI_STATUS rv; 203 204 /* 205 * Retrieve polling interval for SMBus Alerts. 206 */ 207 rv = acpi_eval_struct(hdl, "_SBI", &smi_buf); 208 209 if (ACPI_FAILURE(rv)) 210 return 0; 211 212 p = smi_buf.Pointer; 213 214 if (p->Type != ACPI_TYPE_PACKAGE) { 215 rv = AE_TYPE; 216 goto out; 217 } 218 219 if (p->Package.Count == 0) { 220 rv = AE_LIMIT; 221 goto out; 222 } 223 224 e = p->Package.Elements; 225 226 if (e[0].Type != ACPI_TYPE_INTEGER) { 227 rv = AE_TYPE; 228 goto out; 229 } 230 231 /* Verify CMI version. */ 232 if (e[0].Integer.Value != 0x10) { 233 rv = AE_SUPPORT; 234 goto out; 235 } 236 237 if (alert != NULL) { 238 239 if (p->Package.Count < 2) 240 goto out; 241 242 if (e[1].Type != ACPI_TYPE_BUFFER) 243 goto out; 244 245 info = (struct SMB_INFO *)(e[1].Buffer.Pointer); 246 *alert = info->poll_int; 247 } 248 249 out: 250 if (smi_buf.Pointer != NULL) 251 ACPI_FREE(smi_buf.Pointer); 252 253 return (ACPI_FAILURE(rv)) ? 0 : 1; 254 } 255 256 static int 257 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 258 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 259 { 260 struct acpi_smbus_softc *sc = cookie; 261 const uint8_t *c = cmdbuf; 262 uint8_t *b = buf, *xb; 263 const char *path; 264 ACPI_OBJECT_LIST args; 265 ACPI_OBJECT arg[5]; 266 ACPI_OBJECT *p, *e; 267 ACPI_BUFFER smbuf; 268 ACPI_STATUS rv; 269 int i, r, xlen; 270 271 /* 272 * arg[0] : protocol 273 * arg[1] : slave address 274 * arg[2] : command 275 * arg[3] : data length 276 * arg[4] : data 277 */ 278 for (i = r = 0; i < __arraycount(arg); i++) 279 arg[i].Type = ACPI_TYPE_INTEGER; 280 281 args.Pointer = arg; 282 283 smbuf.Pointer = NULL; 284 smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 285 286 arg[1].Integer.Value = addr; 287 288 if (I2C_OP_READ_P(op)) { 289 290 path = "_SBR"; 291 args.Count = 3; 292 293 switch (len) { 294 295 case 0: 296 arg[0].Integer.Value = (cmdlen != 0) ? 297 ACPI_SMBUS_RCV_BYTE : ACPI_SMBUS_RD_QUICK; 298 299 arg[2].Integer.Value = 0; 300 break; 301 302 case 1: 303 arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE; 304 arg[2].Integer.Value = *c; 305 break; 306 307 case 2: 308 arg[0].Integer.Value = ACPI_SMBUS_RD_WORD; 309 arg[2].Integer.Value = *c; 310 break; 311 312 default: 313 arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK; 314 arg[2].Integer.Value = *c; 315 break; 316 } 317 318 } else { 319 320 path = "_SBW"; 321 args.Count = 5; 322 323 arg[3].Integer.Value = len; 324 325 switch (len) { 326 327 case 0: 328 if (cmdlen == 0) { 329 arg[2].Integer.Value = 0; 330 arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK; 331 } else { 332 arg[2].Integer.Value = *c; 333 arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE; 334 } 335 336 arg[4].Integer.Value = 0; 337 break; 338 339 case 1: 340 arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE; 341 arg[2].Integer.Value = *c; 342 arg[4].Integer.Value = *b; 343 break; 344 345 case 2: 346 arg[0].Integer.Value = ACPI_SMBUS_WR_WORD; 347 arg[2].Integer.Value = *c; 348 arg[4].Integer.Value = *b++; 349 arg[4].Integer.Value += (*b--) << 8; 350 break; 351 352 default: 353 arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK; 354 arg[2].Integer.Value = *c; 355 arg[4].Type = ACPI_TYPE_BUFFER; 356 arg[4].Buffer.Pointer = buf; 357 arg[4].Buffer.Length = (len < 32) ? len : 32; 358 break; 359 } 360 } 361 362 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, path, &args,&smbuf); 363 364 if (ACPI_FAILURE(rv)) 365 goto out; 366 367 p = smbuf.Pointer; 368 369 if (p->Type != ACPI_TYPE_PACKAGE) { 370 rv = AE_TYPE; 371 goto out; 372 } 373 374 if (p->Package.Count < 1) { 375 rv = AE_LIMIT; 376 goto out; 377 } 378 379 e = p->Package.Elements; 380 381 if (e->Type != ACPI_TYPE_INTEGER) { 382 rv = AE_TYPE; 383 goto out; 384 } 385 386 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, 387 "return status: %"PRIu64"\n", e[0].Integer.Value)); 388 389 if (e[0].Integer.Value != 0) { 390 rv = AE_BAD_VALUE; 391 goto out; 392 } 393 394 /* 395 * For read operations, copy data to user buffer. 396 */ 397 if (I2C_OP_READ_P(op)) { 398 399 if (p->Package.Count < 3) { 400 rv = AE_LIMIT; 401 goto out; 402 } 403 404 if (e[1].Type != ACPI_TYPE_INTEGER) { 405 rv = AE_TYPE; 406 goto out; 407 } 408 409 xlen = e[1].Integer.Value; 410 411 if (xlen > len) 412 xlen = len; 413 414 switch (e[2].Type) { 415 416 case ACPI_TYPE_BUFFER: 417 418 if (xlen == 0) { 419 rv = AE_LIMIT; 420 goto out; 421 } 422 423 xb = e[2].Buffer.Pointer; 424 425 if (xb == NULL) { 426 rv = AE_NULL_OBJECT; 427 goto out; 428 } 429 430 (void)memcpy(b, xb, xlen); 431 break; 432 433 case ACPI_TYPE_INTEGER: 434 435 if (xlen > 0) 436 *b++ = e[2].Integer.Value & 0xff; 437 438 if (xlen > 1) 439 *b = e[2].Integer.Value >> 8; 440 441 break; 442 443 default: 444 rv = AE_TYPE; 445 goto out; 446 } 447 } 448 449 out: 450 if (smbuf.Pointer != NULL) 451 ACPI_FREE(smbuf.Pointer); 452 453 if (ACPI_SUCCESS(rv)) 454 return 0; 455 456 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to " 457 "evaluate %s: %s\n", path, AcpiFormatException(rv))); 458 459 return 1; 460 } 461 462 /* 463 * Whether triggered by periodic polling or a Notify(), 464 * retrieve all pending SMBus device alerts. 465 */ 466 static void 467 acpi_smbus_alerts(struct acpi_smbus_softc *sc) 468 { 469 const ACPI_HANDLE hdl = sc->sc_devnode->ad_handle; 470 ACPI_OBJECT *e, *p; 471 ACPI_BUFFER alert; 472 ACPI_STATUS rv; 473 int status = 0; 474 uint8_t addr; 475 476 do { 477 rv = acpi_eval_struct(hdl, "_SBA", &alert); 478 479 if (ACPI_FAILURE(rv)) { 480 status = 1; 481 goto done; 482 } 483 484 p = alert.Pointer; 485 486 if (p->Type == ACPI_TYPE_PACKAGE && p->Package.Count >= 2) { 487 488 status = 1; 489 490 e = p->Package.Elements; 491 492 if (e[0].Type == ACPI_TYPE_INTEGER) 493 status = e[0].Integer.Value; 494 495 if (status == 0 && e[1].Type == ACPI_TYPE_INTEGER) { 496 addr = e[1].Integer.Value; 497 498 aprint_debug_dev(sc->sc_dv, 499 "alert for 0x%x\n", addr); 500 501 (void)iic_smbus_intr(&sc->sc_i2c_tag); 502 } 503 } 504 done: 505 if (alert.Pointer != NULL) 506 ACPI_FREE(alert.Pointer); 507 508 } while (status == 0); 509 } 510 511 static void 512 acpi_smbus_tick(void *opaque) 513 { 514 device_t dv = opaque; 515 struct acpi_smbus_softc *sc = device_private(dv); 516 517 acpi_smbus_alerts(sc); 518 519 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 520 } 521 522 static void 523 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 524 { 525 device_t dv = opaque; 526 struct acpi_smbus_softc *sc = device_private(dv); 527 528 aprint_debug_dev(dv, "received notify message 0x%x\n", notify); 529 530 acpi_smbus_alerts(sc); 531 } 532