1/** 2 * \file 3 * \brief ACPI embedded controller (EC) driver 4 */ 5 6/* 7 * Copyright (c) 2009, 2010, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15/* 16 * Portions based on the FreeBSD driver, which carries the following notice: 17 * 18 * Copyright (c) 2003-2007 Nate Lawson 19 * Copyright (c) 2000 Michael Smith 20 * Copyright (c) 2000 BSDi 21 * All rights reserved. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the above copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 */ 44 45#include <stdio.h> 46#include <barrelfish/barrelfish.h> 47#include <acpi.h> 48 49#include <dev/acpi_ec_dev.h> 50 51#include "acpi_shared.h" 52#include "acpi_debug.h" 53 54struct ec { 55 ACPI_HANDLE handle; ///< Handle to EC object 56 ACPI_INTEGER uid; ///< UID of this EC object 57 bool use_glk; ///< Whether to use the ACPI global lock 58 acpi_ec_t dev; ///< Mackerel device status 59}; 60 61static ACPI_STATUS doread(struct ec *ec, uint8_t addr, uint8_t *data) 62{ 63 // spinwait for input buffer empty 64 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 65 66 // send the read command 67 acpi_ec_cmd_wr(&ec->dev, acpi_ec_read); 68 69 // spinwait for input buffer empty 70 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 71 72 // send the address 73 acpi_ec_data_wr(&ec->dev, addr); 74 75 // spinwait for output buffer full 76 while (!acpi_ec_status_obf_rdf(&ec->dev)) ; 77 78 // read byte 79 *data = acpi_ec_data_rd(&ec->dev); 80 81 return AE_OK; 82} 83 84static ACPI_STATUS dowrite(struct ec *ec, uint8_t addr, uint8_t data) 85{ 86 // spinwait for input buffer empty 87 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 88 89 // send the write command 90 acpi_ec_cmd_wr(&ec->dev, acpi_ec_write); 91 92 // spinwait for input buffer empty 93 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 94 95 acpi_ec_data_wr(&ec->dev, addr); 96 97 // spinwait for input buffer empty 98 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 99 100 // write byte 101 acpi_ec_data_wr(&ec->dev, data); 102 103 // spinwait for input buffer empty 104 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 105 106 return AE_OK; 107} 108 109static uint32_t gpe_handler( 110 ACPI_HANDLE GpeDevice, 111 UINT32 GpeNumber, 112 void *arg) 113{ 114 struct ec *ec = arg; 115 ACPI_STATUS as; 116 117 /* check if an SCI is pending */ 118 if (acpi_ec_status_sci_evt_rdf(&ec->dev)) { 119 // spinwait for input buffer empty 120 while (acpi_ec_status_ibf_rdf(&ec->dev)) ; 121 122 // send query command 123 acpi_ec_cmd_wr(&ec->dev, acpi_ec_query); 124 125 // spinwait for output buffer full 126 while (!acpi_ec_status_obf_rdf(&ec->dev)) ; 127 128 // read data 129 uint8_t data = acpi_ec_data_rd(&ec->dev); 130 131 printf("EC: GPE query %X\n", data); 132 133 if (data != 0) { 134 // evaluate query method _QXX to respond 135 char method[5]; 136 snprintf(method, sizeof(method), "_Q%02X", data); 137 as = AcpiEvaluateObject(ec->handle, method, NULL, NULL); 138 if (ACPI_FAILURE(as)) { 139 ACPI_DEBUG("EC: error 0x%"PRIx32" in query method %s\n", as, method); 140 } 141 } 142 } 143 144 return 0; // noop 145} 146 147static ACPI_STATUS space_handler(uint32_t function, ACPI_PHYSICAL_ADDRESS xaddr, 148 uint32_t bitwidth, ACPI_INTEGER *value, 149 void *handler_context, void *region_context) 150{ 151 struct ec *ec = handler_context; 152 uint8_t addr = xaddr, data; 153 ACPI_INTEGER retval = 0; 154 ACPI_STATUS as = AE_OK; 155 156 assert(bitwidth % 8 == 0); 157 assert(xaddr + (bitwidth / 8) <= 256); 158 159 for (int i = 0; i < bitwidth; i += 8, addr++) { 160 switch(function) { 161 case ACPI_READ: 162 as = doread(ec, addr, &data); 163 if (ACPI_SUCCESS(as)) { 164 retval |= (ACPI_INTEGER)data << i; 165 } 166 break; 167 168 case ACPI_WRITE: 169 data = *value >> i; 170 as = dowrite(ec, addr, data); 171 break; 172 173 default: 174 USER_PANIC("NYI"); 175 as = AE_ERROR; 176 } 177 178 if (ACPI_FAILURE(as)) { 179 ACPI_DEBUG("error 0x%"PRIx32" in EC space handler\n", as); 180 break; 181 } 182 } 183 184 return as; 185} 186 187static ACPI_STATUS ec_probe(ACPI_HANDLE handle, uint32_t nestlevel, 188 void *context, void **retval) 189{ 190 ACPI_STATUS as; 191 192 struct ec *ec = malloc(sizeof(struct ec)); 193 assert(ec != NULL); 194 195 ec->handle = handle; 196 197 // store UID of object 198 as = acpi_eval_integer(handle, "_UID", &ec->uid); 199 if (ACPI_FAILURE(as)) { 200 ec->uid = 0; 201 } 202 203 // do we need to use the global lock when accessing? 204 ACPI_INTEGER tmp; 205 as = acpi_eval_integer(handle, "_GLK", &tmp); 206 ec->use_glk = (ACPI_SUCCESS(as) && tmp); 207 assert(!ec->use_glk); // NYI by this driver 208 209 // evaluate _GPE to find the GPE used by this EC 210 ACPI_BUFFER buf = { 211 .Pointer = NULL, 212 .Length = ACPI_ALLOCATE_BUFFER, 213 }; 214 as = AcpiEvaluateObject(handle, "_GPE", NULL, &buf); 215 if (ACPI_FAILURE(as)) { 216 ACPI_DEBUG("_GPE failed: 0x%"PRIx32"\n", as); 217 return as; 218 } 219 220 ACPI_OBJECT *obj = (ACPI_OBJECT *)buf.Pointer; 221 assert(obj != NULL); 222 223 ACPI_INTEGER gpe; 224 if (obj->Type == ACPI_TYPE_INTEGER) { 225 gpe = obj->Integer.Value; 226 } else if (obj->Type == ACPI_TYPE_PACKAGE) { 227 // see 12.11 228 USER_PANIC("NYI"); 229 return AE_ERROR; 230 } else { 231 ACPI_DEBUG("_GPE returned unexpected object type %"PRIu32"\n", obj->Type); 232 free(buf.Pointer); 233 return AE_TYPE; 234 } 235 236 // Free returned buffer 237 free(buf.Pointer); 238 239 // locate device ports 240 buf.Pointer = NULL; 241 buf.Length = ACPI_ALLOCATE_BUFFER; 242 as = AcpiGetCurrentResources(handle, &buf); 243 if (ACPI_FAILURE(as)) { 244 ACPI_DEBUG("error calling _CRS\n"); 245 return as; 246 } 247 248 // walk resources 249 uint16_t ioports[2]; 250 int nports = 0; 251 252 ACPI_RESOURCE *resource = buf.Pointer; 253 assert(resource != NULL); 254 do { 255 switch(resource->Type) { 256 case ACPI_RESOURCE_TYPE_IO: 257 if (nports < 2) { 258 ioports[nports++] = resource->Data.Io.Minimum; 259 } 260 break; 261 default: 262 ACPI_DEBUG("unhandled EC resource type %"PRIu32"\n", resource->Type); 263 } 264 265 resource = ACPI_ADD_PTR(ACPI_RESOURCE, resource, resource->Length); 266 } while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG); 267 268 free(buf.Pointer); 269 270 if (nports < 2) { 271 ACPI_DEBUG("insufficient IO ports included in EC resource set\n"); 272 return AE_ERROR; 273 } 274 275 // init mackerel state 276 acpi_ec_initialize(&ec->dev, ioports[0], ioports[1]); 277 278 // register GPE handler 279 as = AcpiInstallGpeHandler(NULL, gpe, ACPI_GPE_EDGE_TRIGGERED, gpe_handler, 280 ec); 281 assert(ACPI_SUCCESS(as)); 282 283 // install address space handler 284 as = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_EC, 285 space_handler, NULL, ec); 286 assert(ACPI_SUCCESS(as)); 287 288 // enable GPE 289 /* 290 * XXX: This function does no longer exist in the new ACPI library 291 as = AcpiSetGpeType(NULL, gpe, ACPI_GPE_TYPE_RUNTIME); 292 if (ACPI_FAILURE(as)) { 293 ACPI_DEBUG("Failed to set GPE %"PRIu64" type: 0x%"PRIx32"\n", gpe, as); 294 return as; 295 } 296 */ 297 298 as = AcpiEnableGpe(NULL, gpe); 299 if (ACPI_FAILURE(as)) { 300 ACPI_DEBUG("Failed to enable GPE %"PRIu64": 0x%"PRIx32"\n", gpe, as); 301 return as; 302 } 303 304 return AE_OK; 305} 306 307/* init first EC in an ECDT table, if present */ 308void ec_probe_ecdt(void) 309{ 310 ACPI_STATUS as; 311 ACPI_TABLE_ECDT *ecdt; 312 ACPI_TABLE_HEADER *th; 313 314 /* parse ECDT table */ 315 as = AcpiGetTable("ECDT", 1, (ACPI_TABLE_HEADER **)&th); 316 if (ACPI_FAILURE(as)) { 317 return; 318 } 319 else { 320 ecdt = (ACPI_TABLE_ECDT*)th; 321 } 322 323 assert(ecdt->Control.BitWidth == 8 && ecdt->Data.BitWidth == 8); 324 325 printf("Initialising EC driver from ECDT...\n"); 326 327 struct ec *ec = malloc(sizeof(struct ec)); 328 assert(ec != NULL); 329 330 // lookup handle 331 as = AcpiGetHandle(NULL, (char *) ecdt->Id, &ec->handle); 332 if (ACPI_FAILURE(as)) { 333 ACPI_DEBUG("EC: can't get handle for %s\n", ecdt->Id); 334 return; 335 } 336 337 // store UID of object 338 ec->uid = ecdt->Uid; 339 340 // do we need to use the global lock when accessing? 341 ACPI_INTEGER tmp; 342 as = acpi_eval_integer(ec->handle, "_GLK", &tmp); 343 ec->use_glk = (ACPI_SUCCESS(as) && tmp); 344 assert(!ec->use_glk); // NYI by this driver 345 346 // init mackerel state 347 assert(ecdt->Control.SpaceId == ACPI_ADR_SPACE_SYSTEM_IO); 348 assert(ecdt->Data.SpaceId == ACPI_ADR_SPACE_SYSTEM_IO); 349 acpi_ec_initialize(&ec->dev, ecdt->Data.Address, ecdt->Control.Address); 350 351 // register GPE handler 352 as = AcpiInstallGpeHandler(NULL, ecdt->Gpe, ACPI_GPE_EDGE_TRIGGERED, 353 gpe_handler, ec); 354 assert(ACPI_SUCCESS(as)); 355 356 // install address space handler 357 as = AcpiInstallAddressSpaceHandler(ec->handle, ACPI_ADR_SPACE_EC, 358 space_handler, NULL, ec); 359 assert(ACPI_SUCCESS(as)); 360 361 // enable GPE 362 /* 363 * XXX: This function does no longer exist in the new ACPI library 364 as = AcpiSetGpeType(NULL, ecdt->Gpe, ACPI_GPE_TYPE_RUNTIME); 365 if (ACPI_FAILURE(as)) { 366 ACPI_DEBUG("Failed to set GPE %d type: 0x%"PRIx32"\n", ecdt->Gpe, as); 367 return; 368 } 369 */ 370 as = AcpiEnableGpe(NULL, ecdt->Gpe); 371 if (ACPI_FAILURE(as)) { 372 ACPI_DEBUG("Failed to enable GPE %d: 0x%"PRIx32"\n", ecdt->Gpe, as); 373 return; 374 } 375} 376 377/* probe and init all other EC devices 378 * FIXME: doesn't protect against re-initing devices listed in ECDT! */ 379void ec_init(void) 380{ 381 AcpiGetDevices("PNP0C09", ec_probe, NULL, NULL); 382} 383