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