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