1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Solaris x86 ACPI CA Embedded Controller operation region handler 27 */ 28 29 #include <sys/file.h> 30 #include <sys/errno.h> 31 #include <sys/conf.h> 32 #include <sys/modctl.h> 33 #include <sys/open.h> 34 #include <sys/stat.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/note.h> 38 #include <sys/atomic.h> 39 40 #include <sys/acpi/acpi.h> 41 #include <sys/acpica.h> 42 43 /* 44 * Internal prototypes 45 */ 46 static int ec_wait_ibf_clear(int sc_addr); 47 static int ec_wait_obf_set(int sc_addr); 48 49 /* 50 * EC status bits 51 */ 52 #define EC_IBF (0x02) 53 #define EC_OBF (0x01) 54 #define EC_SMI (0x40) 55 #define EC_SCI (0x20) 56 57 /* 58 * EC commands 59 */ 60 #define EC_RD (0x80) 61 #define EC_WR (0x81) 62 #define EC_BE (0x82) 63 #define EC_BD (0x83) 64 #define EC_QR (0x84) 65 66 #define IO_PORT_DES (0x47) 67 68 /* 69 * EC softstate 70 */ 71 struct ec_softstate { 72 uint8_t ec_ok; /* != 0 when EC handler is attached */ 73 uint16_t ec_base; /* base of EC I/O port - data */ 74 uint16_t ec_sc; /* EC status/command */ 75 ACPI_HANDLE ec_obj; /* handle to ACPI object for EC */ 76 kmutex_t ec_mutex; /* serialize access to EC */ 77 } ec; 78 79 /* I/O port range descriptor */ 80 typedef struct io_port_des { 81 uint8_t type; 82 uint8_t decode; 83 uint8_t min_base_lo; 84 uint8_t min_base_hi; 85 uint8_t max_base_lo; 86 uint8_t max_base_hi; 87 uint8_t align; 88 uint8_t len; 89 } io_port_des_t; 90 91 /* 92 * Patchable timeout values for EC input-buffer-full-clear 93 * and output-buffer-full-set. These are in 10uS units and 94 * default to 1 second. 95 */ 96 int ibf_clear_timeout = 100000; 97 int obf_set_timeout = 100000; 98 99 /* 100 * ACPI CA address space handler interface functions 101 */ 102 /*ARGSUSED*/ 103 static ACPI_STATUS 104 ec_setup(ACPI_HANDLE reg, UINT32 func, void *context, void **ret) 105 { 106 107 return (AE_OK); 108 } 109 110 /* 111 * Only called from ec_handler(), which validates ec_ok 112 */ 113 static int 114 ec_rd(int addr) 115 { 116 int cnt, rv; 117 uint8_t sc; 118 119 mutex_enter(&ec.ec_mutex); 120 sc = inb(ec.ec_sc); 121 122 #ifdef DEBUG 123 if (sc & EC_IBF) { 124 cmn_err(CE_NOTE, "!ec_rd: IBF already set"); 125 } 126 127 if (sc & EC_OBF) { 128 cmn_err(CE_NOTE, "!ec_rd: OBF already set"); 129 } 130 #endif 131 132 outb(ec.ec_sc, EC_RD); /* output a read command */ 133 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 134 cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting " 135 "for IBF to clear"); 136 mutex_exit(&ec.ec_mutex); 137 return (-1); 138 } 139 140 outb(ec.ec_base, addr); /* output addr */ 141 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 142 cmn_err(CE_NOTE, "!ec_rd:2: timed-out waiting " 143 "for IBF to clear"); 144 mutex_exit(&ec.ec_mutex); 145 return (-1); 146 } 147 if (ec_wait_obf_set(ec.ec_sc) < 0) { 148 cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting " 149 "for OBF to set"); 150 mutex_exit(&ec.ec_mutex); 151 return (-1); 152 } 153 154 rv = inb(ec.ec_base); 155 mutex_exit(&ec.ec_mutex); 156 return (rv); 157 } 158 159 /* 160 * Only called from ec_handler(), which validates ec_ok 161 */ 162 static int 163 ec_wr(int addr, uint8_t *val) 164 { 165 int cnt; 166 uint8_t sc; 167 168 mutex_enter(&ec.ec_mutex); 169 sc = inb(ec.ec_sc); 170 171 #ifdef DEBUG 172 if (sc & EC_IBF) { 173 cmn_err(CE_NOTE, "!ec_wr: IBF already set"); 174 } 175 176 if (sc & EC_OBF) { 177 cmn_err(CE_NOTE, "!ec_wr: OBF already set"); 178 } 179 #endif 180 181 outb(ec.ec_sc, EC_WR); /* output a write command */ 182 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 183 cmn_err(CE_NOTE, "!ec_wr:1: timed-out waiting " 184 "for IBF to clear"); 185 mutex_exit(&ec.ec_mutex); 186 return (-1); 187 } 188 189 outb(ec.ec_base, addr); /* output addr */ 190 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 191 cmn_err(CE_NOTE, "!ec_wr:2: timed-out waiting " 192 "for IBF to clear"); 193 mutex_exit(&ec.ec_mutex); 194 return (-1); 195 } 196 197 outb(ec.ec_base, *val); /* write data */ 198 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 199 cmn_err(CE_NOTE, "!ec_wr:3: timed-out waiting " 200 "for IBF to clear"); 201 mutex_exit(&ec.ec_mutex); 202 return (-1); 203 } 204 205 mutex_exit(&ec.ec_mutex); 206 return (0); 207 } 208 209 /* 210 * Only called from ec_gpe_callback(), which validates ec_ok 211 */ 212 static int 213 ec_query(void) 214 { 215 int cnt, rv; 216 uint8_t sc; 217 218 mutex_enter(&ec.ec_mutex); 219 outb(ec.ec_sc, EC_QR); /* output a query command */ 220 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 221 cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting " 222 "for IBF to clear"); 223 mutex_exit(&ec.ec_mutex); 224 return (-1); 225 } 226 227 if (ec_wait_obf_set(ec.ec_sc) < 0) { 228 cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting " 229 "for OBF to set"); 230 mutex_exit(&ec.ec_mutex); 231 return (-1); 232 } 233 234 rv = inb(ec.ec_base); 235 mutex_exit(&ec.ec_mutex); 236 return (rv); 237 } 238 239 static ACPI_STATUS 240 ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width, 241 ACPI_INTEGER *val, void *context, void *regcontext) 242 { 243 _NOTE(ARGUNUSED(context, regcontext)) 244 int tmp; 245 246 /* Guard against unexpected invocation */ 247 if (ec.ec_ok == 0) 248 return (AE_ERROR); 249 250 /* 251 * Add safety checks for BIOSes not strictly compliant 252 * with ACPI spec 253 */ 254 if (((width % 8) != 0) || (width > 8)) { 255 cmn_err(CE_NOTE, "!ec_handler: invalid width %d", width); 256 return (AE_ERROR); 257 } 258 259 switch (func) { 260 case ACPI_READ: 261 tmp = ec_rd(addr); 262 if (tmp < 0) 263 return (AE_ERROR); 264 *val = tmp; 265 break; 266 case ACPI_WRITE: 267 if (ec_wr(addr, (uint8_t *)val) < 0) 268 return (AE_ERROR); 269 break; 270 default: 271 return (AE_ERROR); 272 } 273 274 return (AE_OK); 275 } 276 277 278 static void 279 ec_gpe_callback(void *ctx) 280 { 281 _NOTE(ARGUNUSED(ctx)) 282 283 char query_str[5]; 284 int query; 285 286 if (!(inb(ec.ec_sc) & EC_SCI)) 287 return; 288 289 if (ec.ec_ok == 0) { 290 cmn_err(CE_WARN, "!ec_gpe_callback: EC handler unavailable"); 291 return; 292 } 293 294 query = ec_query(); 295 if (query >= 0) { 296 (void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query); 297 (void) AcpiEvaluateObject(ec.ec_obj, query_str, NULL, NULL); 298 } 299 300 } 301 302 static UINT32 303 ec_gpe_handler(void *ctx) 304 { 305 _NOTE(ARGUNUSED(ctx)) 306 307 AcpiOsExecute(OSL_GPE_HANDLER, ec_gpe_callback, NULL); 308 return (0); 309 } 310 311 /* 312 * Busy-wait for IBF to clear 313 * return < 0 for time out, 0 for no error 314 */ 315 static int 316 ec_wait_ibf_clear(int sc_addr) 317 { 318 int cnt; 319 320 cnt = ibf_clear_timeout; 321 while (inb(sc_addr) & EC_IBF) { 322 if (cnt-- <= 0) 323 return (-1); 324 drv_usecwait(10); 325 } 326 return (0); 327 } 328 329 /* 330 * Busy-wait for OBF to set 331 * return < 0 for time out, 0 for no error 332 */ 333 static int 334 ec_wait_obf_set(int sc_addr) 335 { 336 int cnt; 337 338 cnt = obf_set_timeout; 339 while (!(inb(sc_addr) & EC_OBF)) { 340 if (cnt-- <= 0) 341 return (-1); 342 drv_usecwait(10); 343 } 344 return (0); 345 } 346 347 348 349 /* 350 * Called from AcpiWalkDevices() when an EC device is found 351 */ 352 static ACPI_STATUS 353 acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) 354 { 355 _NOTE(ARGUNUSED(nest, context, rv)) 356 357 int status, i; 358 ACPI_BUFFER buf, crs; 359 ACPI_INTEGER gpe; 360 int io_port_cnt; 361 362 /* 363 * Save the one EC object we have 364 */ 365 ec.ec_obj = obj; 366 367 /* 368 * Find ec_base and ec_sc addresses 369 */ 370 crs.Length = ACPI_ALLOCATE_BUFFER; 371 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(obj, "_CRS", NULL, &crs, 372 ACPI_TYPE_BUFFER))) { 373 cmn_err(CE_WARN, "!acpica_install_ec: _CRS object evaluate" 374 "failed"); 375 return (AE_OK); 376 } 377 378 for (i = 0, io_port_cnt = 0; 379 i < ((ACPI_OBJECT *)crs.Pointer)->Buffer.Length; i++) { 380 io_port_des_t *io_port; 381 uint8_t *tmp; 382 383 tmp = ((ACPI_OBJECT *)crs.Pointer)->Buffer.Pointer + i; 384 if (*tmp != IO_PORT_DES) 385 continue; 386 io_port = (io_port_des_t *)tmp; 387 /* 388 * Assuming first port is ec_base and second is ec_sc 389 */ 390 if (io_port_cnt) 391 ec.ec_sc = (io_port->min_base_hi << 8) | 392 io_port->min_base_lo; 393 else 394 ec.ec_base = (io_port->min_base_hi << 8) | 395 io_port->min_base_lo; 396 397 io_port_cnt++; 398 /* 399 * Increment ahead to next struct. 400 */ 401 i += 7; 402 } 403 AcpiOsFree(crs.Pointer); 404 405 /* 406 * Drain the EC data register if something is left over from 407 * legacy mode 408 */ 409 if (inb(ec.ec_sc) & EC_OBF) { 410 #ifndef DEBUG 411 inb(ec.ec_base); /* read and discard value */ 412 #else 413 cmn_err(CE_NOTE, "!EC had something: 0x%x\n", inb(ec.ec_base)); 414 #endif 415 } 416 417 /* 418 * Get GPE 419 */ 420 buf.Length = ACPI_ALLOCATE_BUFFER; 421 /* 422 * grab contents of GPE object 423 */ 424 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(obj, "_GPE", NULL, &buf, 425 ACPI_TYPE_INTEGER))) { 426 /* emit a warning but ignore the error */ 427 cmn_err(CE_WARN, "!acpica_install_ec: _GPE object evaluate" 428 "failed"); 429 gpe = -1; 430 } else { 431 gpe = ((ACPI_OBJECT *)buf.Pointer)->Integer.Value; 432 AcpiOsFree(buf.Pointer); 433 } 434 435 /* 436 * Initialize EC mutex here 437 */ 438 mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL); 439 440 /* 441 * Assume the EC handler is available now; the next attempt 442 * to install the address space handler may actually need 443 * to use the EC handler to install it (chicken/egg) 444 */ 445 ec.ec_ok = 1; 446 447 if (AcpiInstallAddressSpaceHandler(obj, 448 ACPI_ADR_SPACE_EC, &ec_handler, &ec_setup, NULL) != AE_OK) { 449 cmn_err(CE_WARN, "!acpica: failed to add EC handler\n"); 450 ec.ec_ok = 0; /* EC handler failed to install */ 451 membar_producer(); /* force ec_ok store before mutex_destroy */ 452 mutex_destroy(&ec.ec_mutex); 453 return (AE_ERROR); 454 } 455 456 /* 457 * In case the EC handler was successfully installed but no 458 * GPE was found, return sucess; a warning was emitted above 459 */ 460 if (gpe < 0) 461 return (AE_OK); 462 463 /* 464 * Have a valid EC GPE; enable it 465 */ 466 if ((status = AcpiInstallGpeHandler(NULL, gpe, ACPI_GPE_EDGE_TRIGGERED, 467 ec_gpe_handler, NULL)) != AE_OK) { 468 cmn_err(CE_WARN, "!acpica: failed to install gpe handler status" 469 " = %d", status); 470 /* 471 * don't return an error here - GPE won't work but the EC 472 * handler may be OK 473 */ 474 } 475 476 (void) AcpiSetGpeType(NULL, gpe, ACPI_GPE_TYPE_RUNTIME); 477 (void) AcpiEnableGpe(NULL, gpe, ACPI_NOT_ISR); 478 479 return (AE_OK); 480 } 481 482 #ifdef DEBUG 483 /*ARGSUSED*/ 484 static ACPI_STATUS 485 acpica_install_smbus_v1(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) 486 { 487 488 cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0\n"); 489 return (AE_OK); 490 } 491 492 /*ARGSUSED*/ 493 static ACPI_STATUS 494 acpica_install_smbus_v2(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) 495 { 496 497 cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0\n"); 498 return (AE_OK); 499 } 500 #endif /* DEBUG */ 501 502 #ifdef NOTYET 503 static void 504 prgas(ACPI_GENERIC_ADDRESS *gas) 505 { 506 cmn_err(CE_CONT, "gas: %d %d %d %d %lx", 507 gas->AddressSpaceId, gas->RegisterBitWidth, gas->RegisterBitOffset, 508 gas->AccessWidth, (long)gas->Address); 509 } 510 511 static void 512 acpica_probe_ecdt() 513 { 514 EC_BOOT_RESOURCES *ecdt; 515 516 517 if (AcpiGetTable("ECDT", 1, (ACPI_TABLE_HEADER **) &ecdt) != AE_OK) { 518 cmn_err(CE_NOTE, "!acpica: ECDT not found\n"); 519 return; 520 } 521 522 cmn_err(CE_NOTE, "EcControl: "); 523 prgas(&ecdt->EcControl); 524 525 cmn_err(CE_NOTE, "EcData: "); 526 prgas(&ecdt->EcData); 527 } 528 #endif /* NOTYET */ 529 530 void 531 acpica_ec_init(void) 532 { 533 #ifdef NOTYET 534 /* 535 * Search the ACPI tables for an ECDT; if 536 * found, use it to install an EC handler 537 */ 538 acpica_probe_ecdt(); 539 #endif /* NOTYET */ 540 541 /* EC handler defaults to not available */ 542 ec.ec_ok = 0; 543 544 /* 545 * General model is: use GetDevices callback to install 546 * handler(s) when device is present. 547 */ 548 (void) AcpiGetDevices("PNP0C09", &acpica_install_ec, NULL, NULL); 549 #ifdef DEBUG 550 (void) AcpiGetDevices("ACPI0001", &acpica_install_smbus_v1, NULL, NULL); 551 (void) AcpiGetDevices("ACPI0005", &acpica_install_smbus_v2, NULL, NULL); 552 #endif /* DEBUG */ 553 } 554