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