1 /* $NetBSD: smbus_acpi.c,v 1.9 2010/03/05 14:00:17 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.9 2010/03/05 14:00:17 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_acquire_bus(void *, int); 84 static void acpi_smbus_release_bus(void *, int); 85 static int acpi_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *, 86 size_t, void *, size_t, int); 87 static void acpi_smbus_alerts(struct acpi_smbus_softc *); 88 static void acpi_smbus_tick(void *); 89 static void acpi_smbus_notify_handler(ACPI_HANDLE, uint32_t, void *); 90 91 struct SMB_UDID { 92 uint8_t dev_cap; 93 uint8_t vers_rev; 94 uint16_t vendor; 95 uint16_t device; 96 uint16_t interface; 97 uint16_t subsys_vendor; 98 uint16_t subsys_device; 99 uint8_t reserved[4]; 100 }; 101 102 struct SMB_DEVICE { 103 uint8_t slave_addr; 104 uint8_t reserved; 105 struct SMB_UDID dev_id; 106 }; 107 108 struct SMB_INFO { 109 uint8_t struct_ver; 110 uint8_t spec_ver; 111 uint8_t hw_cap; 112 uint8_t poll_int; 113 uint8_t dev_count; 114 struct SMB_DEVICE device[1]; 115 }; 116 117 static const char * const smbus_acpi_ids[] = { 118 "SMBUS01", /* SMBus CMI v1.0 */ 119 NULL 120 }; 121 122 static const char * const pcibus_acpi_ids[] = { 123 "PNP0A03", 124 "PNP0A08", 125 NULL 126 }; 127 128 CFATTACH_DECL_NEW(acpismbus, sizeof(struct acpi_smbus_softc), 129 acpi_smbus_match, acpi_smbus_attach, acpi_smbus_detach, NULL); 130 131 /* 132 * acpi_smbus_match: autoconf(9) match routine 133 */ 134 static int 135 acpi_smbus_match(device_t parent, cfdata_t match, void *aux) 136 { 137 struct acpi_attach_args *aa = aux; 138 int r = 0; 139 ACPI_STATUS rv; 140 ACPI_BUFFER smi_buf; 141 ACPI_OBJECT *e, *p; 142 143 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 144 return 0; 145 146 if (acpi_match_hid(aa->aa_node->ad_devinfo, smbus_acpi_ids) == 0) 147 return 0; 148 149 /* Ensure that device's CMI version is supported */ 150 rv = acpi_eval_struct(aa->aa_node->ad_handle, "_SBI", &smi_buf); 151 if (ACPI_FAILURE(rv)) 152 goto done; 153 154 p = smi_buf.Pointer; 155 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE && 156 p->Package.Count >= 1) { 157 e = p->Package.Elements; 158 if (e[0].Type == ACPI_TYPE_INTEGER && 159 e[0].Integer.Value == 0x10) 160 r = 1; 161 } 162 done: 163 if (smi_buf.Pointer != NULL) 164 ACPI_FREE(smi_buf.Pointer); 165 166 return r; 167 } 168 169 /* 170 * acpitz_attach: autoconf(9) attach routine 171 */ 172 static void 173 acpi_smbus_attach(device_t parent, device_t self, void *aux) 174 { 175 struct acpi_smbus_softc *sc = device_private(self); 176 struct acpi_attach_args *aa = aux; 177 struct i2cbus_attach_args iba; 178 ACPI_STATUS rv; 179 ACPI_HANDLE native_dev, native_bus; 180 ACPI_DEVICE_INFO *native_dev_info, *native_bus_info; 181 ACPI_BUFFER smi_buf; 182 ACPI_OBJECT *e, *p; 183 struct SMB_INFO *info; 184 int pci_bus, pci_dev, pci_func; 185 186 aprint_naive("\n"); 187 188 sc->sc_devnode = aa->aa_node; 189 sc->sc_dv = self; 190 sc->sc_poll_alert = 2; 191 192 /* Attach I2C bus */ 193 mutex_init(&sc->sc_i2c_mutex, MUTEX_DEFAULT, IPL_NONE); 194 sc->sc_i2c_tag.ic_cookie = sc; 195 sc->sc_i2c_tag.ic_acquire_bus = acpi_smbus_acquire_bus; 196 sc->sc_i2c_tag.ic_release_bus = acpi_smbus_release_bus; 197 sc->sc_i2c_tag.ic_exec = acpi_smbus_exec; 198 199 /* Retrieve polling interval for SMBus Alerts */ 200 rv = acpi_eval_struct(aa->aa_node->ad_handle, "_SBI", &smi_buf); 201 if (ACPI_SUCCESS(rv)) { 202 p = smi_buf.Pointer; 203 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE && 204 p->Package.Count >= 2) { 205 e = p->Package.Elements; 206 if (e[1].Type == ACPI_TYPE_BUFFER) { 207 info = (struct SMB_INFO *)(e[1].Buffer.Pointer); 208 sc->sc_poll_alert = info->poll_int; 209 } 210 } 211 } 212 if (smi_buf.Pointer != NULL) 213 ACPI_FREE(smi_buf.Pointer); 214 215 /* Install notify handler if possible */ 216 rv = AcpiInstallNotifyHandler(sc->sc_devnode->ad_handle, 217 ACPI_DEVICE_NOTIFY, acpi_smbus_notify_handler, self); 218 if (ACPI_FAILURE(rv)) { 219 aprint_error(": unable to install DEVICE NOTIFY handler: %s\n", 220 AcpiFormatException(rv)); 221 sc->sc_poll_alert = 2; /* If failed, fall-back to polling */ 222 } 223 224 callout_init(&sc->sc_callout, 0); 225 callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self); 226 227 if (!pmf_device_register(self, NULL, NULL)) 228 aprint_error(": couldn't establish power handler\n"); 229 230 if (sc->sc_poll_alert != 0) { 231 aprint_debug(" alert_poll %d sec", sc->sc_poll_alert); 232 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 233 } 234 aprint_normal("\n"); 235 236 /* 237 * Retrieve and display native controller info 238 */ 239 rv = AcpiGetParent(sc->sc_devnode->ad_handle, &native_dev); 240 241 native_bus_info = native_dev_info = NULL; 242 243 if (ACPI_SUCCESS(rv)) 244 rv = AcpiGetParent(native_dev, &native_bus); 245 246 if (ACPI_SUCCESS(rv)) 247 rv = AcpiGetObjectInfo(native_bus, &native_bus_info); 248 249 if (ACPI_SUCCESS(rv) && 250 acpi_match_hid(native_bus_info, pcibus_acpi_ids) != 0) { 251 252 rv = AcpiGetObjectInfo(native_dev, &native_dev_info); 253 254 if (ACPI_SUCCESS(rv)) { 255 pci_bus = native_bus_info->Address; 256 pci_dev = ACPI_ADR_PCI_DEV(native_dev_info->Address); 257 pci_func = ACPI_ADR_PCI_FUNC(native_dev_info->Address); 258 aprint_debug_dev(self, "Native i2c host controller" 259 " is on PCI bus %d dev %d func %d\n", 260 pci_bus, pci_dev, pci_func); 261 /* 262 * XXX We really need a mechanism to prevent the 263 * XXX native controller from attaching 264 */ 265 } 266 } 267 268 if (native_bus_info != NULL) 269 ACPI_FREE(native_bus_info); 270 271 if (native_dev_info != NULL) 272 ACPI_FREE(native_dev_info); 273 274 memset(&iba, 0, sizeof(iba)); 275 iba.iba_tag = &sc->sc_i2c_tag; 276 config_found_ia(self, "i2cbus", &iba, iicbus_print); 277 } 278 279 static int 280 acpi_smbus_detach(device_t self, int flags) 281 { 282 struct acpi_smbus_softc *sc = device_private(self); 283 ACPI_STATUS rv; 284 285 rv = AcpiRemoveNotifyHandler(sc->sc_devnode->ad_handle, 286 ACPI_DEVICE_NOTIFY, acpi_smbus_notify_handler); 287 288 if (ACPI_FAILURE(rv)) 289 return EBUSY; 290 291 pmf_device_deregister(self); 292 293 callout_halt(&sc->sc_callout, NULL); 294 callout_destroy(&sc->sc_callout); 295 296 mutex_destroy(&sc->sc_i2c_mutex); 297 298 return 0; 299 } 300 301 static int 302 acpi_smbus_acquire_bus(void *cookie, int flags) 303 { 304 struct acpi_smbus_softc *sc = cookie; 305 306 mutex_enter(&sc->sc_i2c_mutex); 307 return 0; 308 } 309 310 static void 311 acpi_smbus_release_bus(void *cookie, int flags) 312 { 313 struct acpi_smbus_softc *sc = cookie; 314 315 mutex_exit(&sc->sc_i2c_mutex); 316 } 317 static int 318 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 319 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 320 { 321 struct acpi_smbus_softc *sc = cookie; 322 const uint8_t *c = cmdbuf; 323 uint8_t *b = buf, *xb; 324 int xlen; 325 int r = 0; 326 ACPI_BUFFER smbuf; 327 ACPI_STATUS rv; 328 ACPI_OBJECT_LIST args; 329 ACPI_OBJECT arg[5]; 330 ACPI_OBJECT *p, *e; 331 332 smbuf.Pointer = NULL; 333 smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 334 args.Pointer = arg; 335 arg[0].Type = ACPI_TYPE_INTEGER; /* Protocol */ 336 arg[1].Type = ACPI_TYPE_INTEGER; /* Slave Addr */ 337 arg[1].Integer.Value = addr; 338 arg[2].Type = ACPI_TYPE_INTEGER; /* Command */ 339 if (I2C_OP_READ_P(op)) { 340 args.Count = 3; 341 if (len == 0) { 342 arg[2].Integer.Value = 0; 343 if (cmdlen == 0) 344 arg[0].Integer.Value = ACPI_SMBUS_RD_QUICK; 345 else 346 arg[0].Integer.Value = ACPI_SMBUS_RCV_BYTE; 347 } else 348 arg[2].Integer.Value = *c; 349 if (len == 1) 350 arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE; 351 else if (len == 2) 352 arg[0].Integer.Value = ACPI_SMBUS_RD_WORD; 353 else if (len > 2) 354 arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK; 355 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, "_SBR", 356 &args, &smbuf); 357 } else { 358 args.Count = 5; 359 arg[3].Type = ACPI_TYPE_INTEGER; /* Data Len */ 360 arg[3].Integer.Value = len; 361 arg[4].Type = ACPI_TYPE_INTEGER; /* Data */ 362 if (len == 0) { 363 arg[4].Integer.Value = 0; 364 if (cmdlen == 0) { 365 arg[2].Integer.Value = 0; 366 arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK; 367 } else { 368 arg[2].Integer.Value = *c; 369 arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE; 370 } 371 } else 372 arg[2].Integer.Value = *c; 373 if (len == 1) { 374 arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE; 375 arg[4].Integer.Value = *b; 376 } else if (len == 2) { 377 arg[0].Integer.Value = ACPI_SMBUS_WR_WORD; 378 arg[4].Integer.Value = *b++; 379 arg[4].Integer.Value += (*b--) << 8; 380 } else if (len > 2) { 381 arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK; 382 arg[4].Type = ACPI_TYPE_BUFFER; 383 arg[4].Buffer.Pointer = buf; 384 arg[4].Buffer.Length = (len < 32?len:32); 385 } 386 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, "_SBW", 387 &args, &smbuf); 388 } 389 if (ACPI_FAILURE(rv)) 390 r = 1; 391 else { 392 p = smbuf.Pointer; 393 if (p == NULL || p->Type != ACPI_TYPE_PACKAGE || 394 p->Package.Count < 1) 395 r = 1; 396 else { 397 e = p->Package.Elements; 398 if (e->Type == ACPI_TYPE_INTEGER) 399 r = e[0].Integer.Value; 400 else 401 r = 1; 402 } 403 if (r != 0) 404 r = 1; 405 406 /* For read operations, copy data to user buffer */ 407 if (r == 0 && I2C_OP_READ_P(op)) { 408 if (p->Package.Count >= 3 && 409 e[1].Type == ACPI_TYPE_INTEGER) { 410 xlen = e[1].Integer.Value; 411 if (xlen > len) 412 xlen = len; 413 if (xlen != 0 && 414 e[2].Type == ACPI_TYPE_BUFFER) { 415 xb = e[2].Buffer.Pointer; 416 if (xb != NULL) 417 memcpy(b, xb, xlen); 418 else 419 r = 1; 420 } else if (e[2].Type == ACPI_TYPE_INTEGER) { 421 if (xlen > 0) 422 *b++ = e[2].Integer.Value & 423 0xff; 424 if (xlen > 1) 425 *b = (e[2].Integer.Value >> 8); 426 } else 427 r = 1; 428 } else 429 r = 1; 430 } 431 } 432 if (smbuf.Pointer) 433 ACPI_FREE(smbuf.Pointer); 434 435 return r; 436 } 437 438 /* 439 * acpi_smbus_alerts 440 * 441 * Whether triggered by periodic polling or an AcpiNotify, retrieve 442 * all pending SMBus device alerts 443 */ 444 static void 445 acpi_smbus_alerts(struct acpi_smbus_softc *sc) 446 { 447 int status = 0; 448 uint8_t slave_addr; 449 ACPI_STATUS rv; 450 ACPI_BUFFER alert; 451 ACPI_OBJECT *e, *p; 452 453 do { 454 rv = acpi_eval_struct(sc->sc_devnode->ad_handle, "_SBA", 455 &alert); 456 if (ACPI_FAILURE(rv)) { 457 status = 1; 458 goto done; 459 } 460 461 p = alert.Pointer; 462 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE && 463 p->Package.Count >= 2) { 464 e = p->Package.Elements; 465 if (e[0].Type == ACPI_TYPE_INTEGER) 466 status = e[0].Integer.Value; 467 else 468 status = 1; 469 if (status == 0x0 && e[1].Type == ACPI_TYPE_INTEGER) { 470 slave_addr = e[1].Integer.Value; 471 aprint_debug_dev(sc->sc_dv, "Alert for 0x%x\n", 472 slave_addr); 473 (void)iic_smbus_intr(&sc->sc_i2c_tag); 474 } 475 } 476 done: 477 if (alert.Pointer != NULL) 478 ACPI_FREE(alert.Pointer); 479 } while (status == 0); 480 } 481 482 static void 483 acpi_smbus_tick(void *opaque) 484 { 485 device_t dv = opaque; 486 struct acpi_smbus_softc *sc = device_private(dv); 487 488 acpi_smbus_alerts(sc); 489 490 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 491 } 492 493 static void 494 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 495 { 496 device_t dv = opaque; 497 struct acpi_smbus_softc *sc = device_private(dv); 498 499 aprint_debug_dev(dv, "received notify message 0x%x\n", notify); 500 acpi_smbus_alerts(sc); 501 } 502