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