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 2006 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 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <sys/file.h> 32 #include <sys/errno.h> 33 #include <sys/conf.h> 34 #include <sys/modctl.h> 35 #include <sys/open.h> 36 #include <sys/stat.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/note.h> 40 41 #include <sys/acpi/acpi.h> 42 #include <sys/acpica.h> 43 44 /* 45 * Internal prototypes 46 */ 47 static int ec_wait_ibf_clear(int sc_addr); 48 static int ec_wait_obf_set(int sc_addr); 49 50 /* 51 * EC status bits 52 */ 53 #define EC_IBF (0x02) 54 #define EC_OBF (0x01) 55 #define EC_SMI (0x40) 56 #define EC_SCI (0x20) 57 58 /* 59 * EC commands 60 */ 61 #define EC_RD (0x80) 62 #define EC_WR (0x81) 63 #define EC_BE (0x82) 64 #define EC_BD (0x83) 65 #define EC_QR (0x84) 66 67 #define IO_PORT_DES (0x47) 68 69 /* 70 * EC softstate 71 */ 72 struct ec_softstate { 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 * ACPI CA address space handler interface functions 93 */ 94 /*ARGSUSED*/ 95 static ACPI_STATUS 96 ec_setup(ACPI_HANDLE reg, UINT32 func, void *context, void **ret) 97 { 98 99 return (AE_OK); 100 } 101 102 static int 103 ec_rd(int addr) 104 { 105 int cnt, rv; 106 uint8_t sc; 107 108 mutex_enter(&ec.ec_mutex); 109 sc = inb(ec.ec_sc); 110 111 #ifdef DEBUG 112 if (sc & EC_IBF) { 113 cmn_err(CE_NOTE, "!ec_rd: IBF already set"); 114 } 115 116 if (sc & EC_OBF) { 117 cmn_err(CE_NOTE, "!ec_rd: OBF already set"); 118 } 119 #endif 120 121 outb(ec.ec_sc, EC_RD); /* output a read command */ 122 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 123 cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting " 124 "for IBF to clear"); 125 mutex_exit(&ec.ec_mutex); 126 return (-1); 127 } 128 129 outb(ec.ec_base, addr); /* output addr */ 130 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 131 cmn_err(CE_NOTE, "!ec_rd:2: timed-out waiting " 132 "for IBF to clear"); 133 mutex_exit(&ec.ec_mutex); 134 return (-1); 135 } 136 if (ec_wait_obf_set(ec.ec_sc) < 0) { 137 cmn_err(CE_NOTE, "!ec_rd:1: timed-out waiting " 138 "for OBF to set"); 139 mutex_exit(&ec.ec_mutex); 140 return (-1); 141 } 142 143 rv = inb(ec.ec_base); 144 mutex_exit(&ec.ec_mutex); 145 return (rv); 146 } 147 148 static int 149 ec_wr(int addr, uint8_t *val) 150 { 151 int cnt; 152 uint8_t sc; 153 154 mutex_enter(&ec.ec_mutex); 155 sc = inb(ec.ec_sc); 156 157 #ifdef DEBUG 158 if (sc & EC_IBF) { 159 cmn_err(CE_NOTE, "!ec_wr: IBF already set"); 160 } 161 162 if (sc & EC_OBF) { 163 cmn_err(CE_NOTE, "!ec_wr: OBF already set"); 164 } 165 #endif 166 167 outb(ec.ec_sc, EC_WR); /* output a write command */ 168 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 169 cmn_err(CE_NOTE, "!ec_wr:1: timed-out waiting " 170 "for IBF to clear"); 171 mutex_exit(&ec.ec_mutex); 172 return (-1); 173 } 174 175 outb(ec.ec_base, addr); /* output addr */ 176 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 177 cmn_err(CE_NOTE, "!ec_wr:2: timed-out waiting " 178 "for IBF to clear"); 179 mutex_exit(&ec.ec_mutex); 180 return (-1); 181 } 182 183 outb(ec.ec_base, *val); /* write data */ 184 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 185 cmn_err(CE_NOTE, "!ec_wr:3: timed-out waiting " 186 "for IBF to clear"); 187 mutex_exit(&ec.ec_mutex); 188 return (-1); 189 } 190 191 mutex_exit(&ec.ec_mutex); 192 return (0); 193 } 194 195 static int 196 ec_query(void) 197 { 198 int cnt, rv; 199 uint8_t sc; 200 201 mutex_enter(&ec.ec_mutex); 202 outb(ec.ec_sc, EC_QR); /* output a query command */ 203 if (ec_wait_ibf_clear(ec.ec_sc) < 0) { 204 cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting " 205 "for IBF to clear"); 206 mutex_exit(&ec.ec_mutex); 207 return (-1); 208 } 209 210 if (ec_wait_obf_set(ec.ec_sc) < 0) { 211 cmn_err(CE_NOTE, "!ec_query:1: timed-out waiting " 212 "for OBF to set"); 213 mutex_exit(&ec.ec_mutex); 214 return (-1); 215 } 216 217 rv = inb(ec.ec_base); 218 mutex_exit(&ec.ec_mutex); 219 return (rv); 220 } 221 222 static ACPI_STATUS 223 ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width, 224 ACPI_INTEGER *val, void *context, void *regcontext) 225 { 226 _NOTE(ARGUNUSED(context, regcontext)) 227 int tmp; 228 229 /* 230 * Add safety checks for BIOSes not strictly compliant 231 * with ACPI spec 232 */ 233 if ((width % 8) != 0) { 234 cmn_err(CE_NOTE, "!ec_handler: width %d not multiple of 8", 235 width); 236 return (AE_ERROR); 237 } 238 239 if (width > 8) { 240 cmn_err(CE_NOTE, "!ec_handler: width %d greater than 8", width); 241 return (AE_ERROR); 242 } 243 244 switch (func) { 245 case ACPI_READ: 246 tmp = ec_rd(addr); 247 if (tmp < 0) 248 return (AE_ERROR); 249 *val = tmp; 250 break; 251 case ACPI_WRITE: 252 if (ec_wr(addr, (uint8_t *)val) < 0) 253 return (AE_ERROR); 254 break; 255 default: 256 return (AE_ERROR); 257 } 258 259 return (AE_OK); 260 } 261 262 263 static void 264 ec_gpe_callback(void *ctx) 265 { 266 _NOTE(ARGUNUSED(ctx)) 267 268 char query_str[5]; 269 int query; 270 271 if (!(inb(ec.ec_sc) & EC_SCI)) 272 return; 273 274 query = ec_query(); 275 if (query >= 0) { 276 (void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query); 277 (void) AcpiEvaluateObject(ec.ec_obj, query_str, NULL, NULL); 278 } 279 280 } 281 282 static UINT32 283 ec_gpe_handler(void *ctx) 284 { 285 _NOTE(ARGUNUSED(ctx)) 286 287 AcpiOsExecute(OSL_GPE_HANDLER, ec_gpe_callback, NULL); 288 return (0); 289 } 290 291 /* 292 * Busy-wait for IBF to clear 293 * return < 0 for time out, 0 for no error 294 */ 295 static int 296 ec_wait_ibf_clear(int sc_addr) 297 { 298 int cnt; 299 300 cnt = 0; 301 while (inb(sc_addr) & EC_IBF) { 302 cnt += 1; 303 drv_usecwait(10); 304 if (cnt > 10000) { 305 return (-1); 306 } 307 } 308 return (0); 309 } 310 311 /* 312 * Busy-wait for OBF to set 313 * return < 0 for time out, 0 for no error 314 */ 315 static int 316 ec_wait_obf_set(int sc_addr) 317 { 318 int cnt; 319 320 cnt = 0; 321 while (!(inb(sc_addr) & EC_OBF)) { 322 cnt += 1; 323 drv_usecwait(10); 324 if (cnt > 10000) { 325 return (-1); 326 } 327 } 328 return (0); 329 } 330 331 332 333 /* 334 * Called from AcpiWalkDevices() when an EC device is found 335 */ 336 static ACPI_STATUS 337 acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) 338 { 339 _NOTE(ARGUNUSED(nest, context, rv)) 340 341 int status, i; 342 ACPI_STATUS res; 343 ACPI_BUFFER buf, crs; 344 ACPI_OBJECT *gpe_obj, *crs_obj; 345 ACPI_INTEGER gpe; 346 int io_port_cnt; 347 348 /* 349 * Save the one EC object we have 350 */ 351 ec.ec_obj = obj; 352 353 /* 354 * Find ec_base and ec_sc addresses 355 */ 356 crs.Length = ACPI_ALLOCATE_BUFFER; 357 res = AcpiEvaluateObject(obj, "_CRS", NULL, &crs); 358 if (ACPI_FAILURE(res)) { 359 cmn_err(CE_WARN, "!acpica_install_ec: _CRS object evaluate" 360 "failed"); 361 return (AE_OK); 362 } 363 crs_obj = crs.Pointer; 364 if (crs_obj->Type != ACPI_TYPE_BUFFER) { 365 cmn_err(CE_WARN, "!acpica_install_ec: not a buffer"); 366 AcpiOsFree(crs.Pointer); 367 return (AE_OK); 368 } 369 370 for (i = 0, io_port_cnt = 0; i < crs_obj->Buffer.Length; i++) { 371 io_port_des_t *io_port; 372 uint8_t *tmp; 373 374 tmp = crs_obj->Buffer.Pointer + i; 375 if (*tmp != IO_PORT_DES) 376 continue; 377 io_port = (io_port_des_t *)tmp; 378 /* 379 * Assuming first port is ec_base and second is ec_sc 380 */ 381 if (io_port_cnt) 382 ec.ec_sc = (io_port->min_base_hi << 8) | 383 io_port->min_base_lo; 384 else 385 ec.ec_base = (io_port->min_base_hi << 8) | 386 io_port->min_base_lo; 387 388 io_port_cnt++; 389 /* 390 * Increment ahead to next struct. 391 */ 392 i += 7; 393 } 394 AcpiOsFree(crs.Pointer); 395 396 /* 397 * Drain the EC data register if something is left over from 398 * legacy mode 399 */ 400 if (inb(ec.ec_sc) & EC_OBF) { 401 #ifndef DEBUG 402 inb(ec.ec_base); /* read and discard value */ 403 #else 404 cmn_err(CE_NOTE, "!EC had something: 0x%x\n", inb(ec.ec_base)); 405 #endif 406 } 407 408 /* 409 * Get GPE 410 */ 411 buf.Length = ACPI_ALLOCATE_BUFFER; 412 /* 413 * grab contents of GPE object 414 */ 415 if (ACPI_FAILURE(AcpiEvaluateObject(obj, "_GPE", NULL, &buf))) { 416 cmn_err(CE_WARN, "!acpica_install_ec: _GPE object evaluate" 417 "failed"); 418 return (AE_OK); 419 } 420 gpe_obj = buf.Pointer; 421 if (gpe_obj->Type != ACPI_TYPE_INTEGER) { 422 cmn_err(CE_WARN, "!acpica_install_ec: not an int"); 423 AcpiOsFree(buf.Pointer); 424 return (AE_OK); 425 } 426 gpe = gpe_obj->Integer.Value; 427 AcpiOsFree(buf.Pointer); 428 429 /* 430 * Initialize EC mutex here 431 */ 432 mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL); 433 434 if (AcpiInstallAddressSpaceHandler(obj, 435 ACPI_ADR_SPACE_EC, &ec_handler, &ec_setup, NULL) != AE_OK) { 436 cmn_err(CE_WARN, "!acpica: failed to add EC handler\n"); 437 mutex_destroy(&ec.ec_mutex); 438 return (AE_ERROR); 439 } 440 441 /* 442 * Enable EC GPE 443 */ 444 if ((status = AcpiInstallGpeHandler(NULL, gpe, ACPI_GPE_EDGE_TRIGGERED, 445 ec_gpe_handler, NULL)) != AE_OK) { 446 cmn_err(CE_WARN, "!acpica: failed to install gpe handler status" 447 " = %d", status); 448 /* 449 * don't return an error here - GPE won't work but the EC 450 * handler may be OK 451 */ 452 } 453 454 status = AcpiSetGpeType(NULL, gpe, ACPI_GPE_TYPE_RUNTIME); 455 status = AcpiEnableGpe(NULL, gpe, ACPI_NOT_ISR); 456 457 return (AE_OK); 458 } 459 460 #ifdef DEBUG 461 /*ARGSUSED*/ 462 static ACPI_STATUS 463 acpica_install_smbus_v1(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) 464 { 465 466 cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0\n"); 467 return (AE_OK); 468 } 469 470 /*ARGSUSED*/ 471 static ACPI_STATUS 472 acpica_install_smbus_v2(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) 473 { 474 475 cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0\n"); 476 return (AE_OK); 477 } 478 #endif /* DEBUG */ 479 480 #ifdef NOTYET 481 static void 482 prgas(ACPI_GENERIC_ADDRESS *gas) 483 { 484 cmn_err(CE_CONT, "gas: %d %d %d %d %lx", 485 gas->AddressSpaceId, gas->RegisterBitWidth, gas->RegisterBitOffset, 486 gas->AccessWidth, (long)gas->Address); 487 } 488 489 static void 490 acpica_probe_ecdt() 491 { 492 EC_BOOT_RESOURCES *ecdt; 493 494 495 if (AcpiGetFirmwareTable("ECDT", 1, ACPI_LOGICAL_ADDRESSING, 496 (ACPI_TABLE_HEADER **) &ecdt) != AE_OK) { 497 cmn_err(CE_NOTE, "!acpica: ECDT not found\n"); 498 return; 499 } 500 501 cmn_err(CE_NOTE, "EcControl: "); 502 prgas(&ecdt->EcControl); 503 504 cmn_err(CE_NOTE, "EcData: "); 505 prgas(&ecdt->EcData); 506 } 507 #endif /* NOTYET */ 508 509 void 510 acpica_ec_init(void) 511 { 512 #ifdef NOTYET 513 /* 514 * Search the ACPI tables for an ECDT; if 515 * found, use it to install an EC handler 516 */ 517 acpica_probe_ecdt(); 518 #endif /* NOTYET */ 519 520 /* 521 * General model is: use GetDevices callback to install 522 * handler(s) when device is present. 523 */ 524 (void) AcpiGetDevices("PNP0C09", &acpica_install_ec, NULL, NULL); 525 #ifdef DEBUG 526 (void) AcpiGetDevices("ACPI0001", &acpica_install_smbus_v1, NULL, NULL); 527 (void) AcpiGetDevices("ACPI0005", &acpica_install_smbus_v2, NULL, NULL); 528 #endif /* DEBUG */ 529 } 530