1 /* $NetBSD: smbus_acpi.c,v 1.10 2010/04/15 07:02:24 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.10 2010/04/15 07:02:24 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 /* If failed, fall-back to polling. */ 216 if (acpi_register_notify(sc->sc_devnode, 217 acpi_smbus_notify_handler) != true) 218 sc->sc_poll_alert = 2; 219 220 callout_init(&sc->sc_callout, 0); 221 callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self); 222 223 if (!pmf_device_register(self, NULL, NULL)) 224 aprint_error(": couldn't establish power handler\n"); 225 226 if (sc->sc_poll_alert != 0) { 227 aprint_debug(" alert_poll %d sec", sc->sc_poll_alert); 228 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 229 } 230 aprint_normal("\n"); 231 232 /* 233 * Retrieve and display native controller info 234 */ 235 rv = AcpiGetParent(sc->sc_devnode->ad_handle, &native_dev); 236 237 native_bus_info = native_dev_info = NULL; 238 239 if (ACPI_SUCCESS(rv)) 240 rv = AcpiGetParent(native_dev, &native_bus); 241 242 if (ACPI_SUCCESS(rv)) 243 rv = AcpiGetObjectInfo(native_bus, &native_bus_info); 244 245 if (ACPI_SUCCESS(rv) && 246 acpi_match_hid(native_bus_info, pcibus_acpi_ids) != 0) { 247 248 rv = AcpiGetObjectInfo(native_dev, &native_dev_info); 249 250 if (ACPI_SUCCESS(rv)) { 251 pci_bus = native_bus_info->Address; 252 pci_dev = ACPI_ADR_PCI_DEV(native_dev_info->Address); 253 pci_func = ACPI_ADR_PCI_FUNC(native_dev_info->Address); 254 aprint_debug_dev(self, "Native i2c host controller" 255 " is on PCI bus %d dev %d func %d\n", 256 pci_bus, pci_dev, pci_func); 257 /* 258 * XXX We really need a mechanism to prevent the 259 * XXX native controller from attaching 260 */ 261 } 262 } 263 264 if (native_bus_info != NULL) 265 ACPI_FREE(native_bus_info); 266 267 if (native_dev_info != NULL) 268 ACPI_FREE(native_dev_info); 269 270 memset(&iba, 0, sizeof(iba)); 271 iba.iba_tag = &sc->sc_i2c_tag; 272 config_found_ia(self, "i2cbus", &iba, iicbus_print); 273 } 274 275 static int 276 acpi_smbus_detach(device_t self, int flags) 277 { 278 struct acpi_smbus_softc *sc = device_private(self); 279 280 pmf_device_deregister(self); 281 acpi_deregister_notify(sc->sc_devnode); 282 283 callout_halt(&sc->sc_callout, NULL); 284 callout_destroy(&sc->sc_callout); 285 286 mutex_destroy(&sc->sc_i2c_mutex); 287 288 return 0; 289 } 290 291 static int 292 acpi_smbus_acquire_bus(void *cookie, int flags) 293 { 294 struct acpi_smbus_softc *sc = cookie; 295 296 mutex_enter(&sc->sc_i2c_mutex); 297 return 0; 298 } 299 300 static void 301 acpi_smbus_release_bus(void *cookie, int flags) 302 { 303 struct acpi_smbus_softc *sc = cookie; 304 305 mutex_exit(&sc->sc_i2c_mutex); 306 } 307 static int 308 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 309 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 310 { 311 struct acpi_smbus_softc *sc = cookie; 312 const uint8_t *c = cmdbuf; 313 uint8_t *b = buf, *xb; 314 int xlen; 315 int r = 0; 316 ACPI_BUFFER smbuf; 317 ACPI_STATUS rv; 318 ACPI_OBJECT_LIST args; 319 ACPI_OBJECT arg[5]; 320 ACPI_OBJECT *p, *e; 321 322 smbuf.Pointer = NULL; 323 smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 324 args.Pointer = arg; 325 arg[0].Type = ACPI_TYPE_INTEGER; /* Protocol */ 326 arg[1].Type = ACPI_TYPE_INTEGER; /* Slave Addr */ 327 arg[1].Integer.Value = addr; 328 arg[2].Type = ACPI_TYPE_INTEGER; /* Command */ 329 if (I2C_OP_READ_P(op)) { 330 args.Count = 3; 331 if (len == 0) { 332 arg[2].Integer.Value = 0; 333 if (cmdlen == 0) 334 arg[0].Integer.Value = ACPI_SMBUS_RD_QUICK; 335 else 336 arg[0].Integer.Value = ACPI_SMBUS_RCV_BYTE; 337 } else 338 arg[2].Integer.Value = *c; 339 if (len == 1) 340 arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE; 341 else if (len == 2) 342 arg[0].Integer.Value = ACPI_SMBUS_RD_WORD; 343 else if (len > 2) 344 arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK; 345 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, "_SBR", 346 &args, &smbuf); 347 } else { 348 args.Count = 5; 349 arg[3].Type = ACPI_TYPE_INTEGER; /* Data Len */ 350 arg[3].Integer.Value = len; 351 arg[4].Type = ACPI_TYPE_INTEGER; /* Data */ 352 if (len == 0) { 353 arg[4].Integer.Value = 0; 354 if (cmdlen == 0) { 355 arg[2].Integer.Value = 0; 356 arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK; 357 } else { 358 arg[2].Integer.Value = *c; 359 arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE; 360 } 361 } else 362 arg[2].Integer.Value = *c; 363 if (len == 1) { 364 arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE; 365 arg[4].Integer.Value = *b; 366 } else if (len == 2) { 367 arg[0].Integer.Value = ACPI_SMBUS_WR_WORD; 368 arg[4].Integer.Value = *b++; 369 arg[4].Integer.Value += (*b--) << 8; 370 } else if (len > 2) { 371 arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK; 372 arg[4].Type = ACPI_TYPE_BUFFER; 373 arg[4].Buffer.Pointer = buf; 374 arg[4].Buffer.Length = (len < 32?len:32); 375 } 376 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, "_SBW", 377 &args, &smbuf); 378 } 379 if (ACPI_FAILURE(rv)) 380 r = 1; 381 else { 382 p = smbuf.Pointer; 383 if (p == NULL || p->Type != ACPI_TYPE_PACKAGE || 384 p->Package.Count < 1) 385 r = 1; 386 else { 387 e = p->Package.Elements; 388 if (e->Type == ACPI_TYPE_INTEGER) 389 r = e[0].Integer.Value; 390 else 391 r = 1; 392 } 393 if (r != 0) 394 r = 1; 395 396 /* For read operations, copy data to user buffer */ 397 if (r == 0 && I2C_OP_READ_P(op)) { 398 if (p->Package.Count >= 3 && 399 e[1].Type == ACPI_TYPE_INTEGER) { 400 xlen = e[1].Integer.Value; 401 if (xlen > len) 402 xlen = len; 403 if (xlen != 0 && 404 e[2].Type == ACPI_TYPE_BUFFER) { 405 xb = e[2].Buffer.Pointer; 406 if (xb != NULL) 407 memcpy(b, xb, xlen); 408 else 409 r = 1; 410 } else if (e[2].Type == ACPI_TYPE_INTEGER) { 411 if (xlen > 0) 412 *b++ = e[2].Integer.Value & 413 0xff; 414 if (xlen > 1) 415 *b = (e[2].Integer.Value >> 8); 416 } else 417 r = 1; 418 } else 419 r = 1; 420 } 421 } 422 if (smbuf.Pointer) 423 ACPI_FREE(smbuf.Pointer); 424 425 return r; 426 } 427 428 /* 429 * acpi_smbus_alerts 430 * 431 * Whether triggered by periodic polling or an AcpiNotify, retrieve 432 * all pending SMBus device alerts 433 */ 434 static void 435 acpi_smbus_alerts(struct acpi_smbus_softc *sc) 436 { 437 int status = 0; 438 uint8_t slave_addr; 439 ACPI_STATUS rv; 440 ACPI_BUFFER alert; 441 ACPI_OBJECT *e, *p; 442 443 do { 444 rv = acpi_eval_struct(sc->sc_devnode->ad_handle, "_SBA", 445 &alert); 446 if (ACPI_FAILURE(rv)) { 447 status = 1; 448 goto done; 449 } 450 451 p = alert.Pointer; 452 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE && 453 p->Package.Count >= 2) { 454 e = p->Package.Elements; 455 if (e[0].Type == ACPI_TYPE_INTEGER) 456 status = e[0].Integer.Value; 457 else 458 status = 1; 459 if (status == 0x0 && e[1].Type == ACPI_TYPE_INTEGER) { 460 slave_addr = e[1].Integer.Value; 461 aprint_debug_dev(sc->sc_dv, "Alert for 0x%x\n", 462 slave_addr); 463 (void)iic_smbus_intr(&sc->sc_i2c_tag); 464 } 465 } 466 done: 467 if (alert.Pointer != NULL) 468 ACPI_FREE(alert.Pointer); 469 } while (status == 0); 470 } 471 472 static void 473 acpi_smbus_tick(void *opaque) 474 { 475 device_t dv = opaque; 476 struct acpi_smbus_softc *sc = device_private(dv); 477 478 acpi_smbus_alerts(sc); 479 480 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz); 481 } 482 483 static void 484 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 485 { 486 device_t dv = opaque; 487 struct acpi_smbus_softc *sc = device_private(dv); 488 489 aprint_debug_dev(dv, "received notify message 0x%x\n", notify); 490 acpi_smbus_alerts(sc); 491 } 492