1*37896b8eSriastradh /* $NetBSD: acpi_ec.c,v 1.108 2023/07/18 10:17:12 riastradh Exp $ */
2b41a7fd4Sthorpej
3b41a7fd4Sthorpej /*-
44c1d81b2Sjmcneill * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
5b41a7fd4Sthorpej * All rights reserved.
6b41a7fd4Sthorpej *
7b41a7fd4Sthorpej * Redistribution and use in source and binary forms, with or without
8b41a7fd4Sthorpej * modification, are permitted provided that the following conditions
9b41a7fd4Sthorpej * are met:
104c1d81b2Sjmcneill *
11b41a7fd4Sthorpej * 1. Redistributions of source code must retain the above copyright
12b41a7fd4Sthorpej * notice, this list of conditions and the following disclaimer.
13b41a7fd4Sthorpej * 2. Redistributions in binary form must reproduce the above copyright
144c1d81b2Sjmcneill * notice, this list of conditions and the following disclaimer in
154c1d81b2Sjmcneill * the documentation and/or other materials provided with the
164c1d81b2Sjmcneill * distribution.
17b41a7fd4Sthorpej *
184c1d81b2Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
194c1d81b2Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
204c1d81b2Sjmcneill * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
214c1d81b2Sjmcneill * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
224c1d81b2Sjmcneill * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
234c1d81b2Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
244c1d81b2Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
254c1d81b2Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
264c1d81b2Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
274c1d81b2Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
284c1d81b2Sjmcneill * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29b41a7fd4Sthorpej * SUCH DAMAGE.
30b41a7fd4Sthorpej */
31b41a7fd4Sthorpej
3236cdfbe0Sjoerg /*
3336cdfbe0Sjoerg * The ACPI Embedded Controller (EC) driver serves two different purposes:
3436cdfbe0Sjoerg * - read and write access from ASL, e.g. to read battery state
3536cdfbe0Sjoerg * - notification of ASL of System Control Interrupts.
3636cdfbe0Sjoerg *
376d1cd1baSriastradh * Lock order:
386d1cd1baSriastradh * sc_access_mtx (serializes EC transactions -- read, write, or SCI)
396d1cd1baSriastradh * -> ACPI global lock (excludes other ACPI access during EC transaction)
406d1cd1baSriastradh * -> sc_mtx (serializes state machine transitions and waits)
4136cdfbe0Sjoerg *
426d1cd1baSriastradh * SCIs are processed in a kernel thread.
4336cdfbe0Sjoerg *
4436cdfbe0Sjoerg * Read and write requests spin around for a short time as many requests
4536cdfbe0Sjoerg * can be handled instantly by the EC. During normal processing interrupt
4636cdfbe0Sjoerg * mode is used exclusively. At boot and resume time interrupts are not
4736cdfbe0Sjoerg * working and the handlers just busy loop.
4836cdfbe0Sjoerg *
4936cdfbe0Sjoerg * A callout is scheduled to compensate for missing interrupts on some
5036cdfbe0Sjoerg * hardware. If the EC doesn't process a request for 5s, it is most likely
5136cdfbe0Sjoerg * in a wedged state. No method to reset the EC is currently known.
5236cdfbe0Sjoerg *
5336cdfbe0Sjoerg * Special care has to be taken to not poll the EC in a busy loop without
5436cdfbe0Sjoerg * delay. This can prevent processing of Power Button events. At least some
5536cdfbe0Sjoerg * Lenovo Thinkpads seem to be implement the Power Button Override in the EC
5636cdfbe0Sjoerg * and the only option to recover on those models is to cut off all power.
5736cdfbe0Sjoerg */
5836cdfbe0Sjoerg
5913ac4302Slukem #include <sys/cdefs.h>
60*37896b8eSriastradh __KERNEL_RCSID(0, "$NetBSD: acpi_ec.c,v 1.108 2023/07/18 10:17:12 riastradh Exp $");
61aeb0e451Sriastradh
62aeb0e451Sriastradh #ifdef _KERNEL_OPT
63aeb0e451Sriastradh #include "opt_acpi_ec.h"
64aeb0e451Sriastradh #endif
6513ac4302Slukem
66b41a7fd4Sthorpej #include <sys/param.h>
675a425210Sjruoho #include <sys/callout.h>
684c1d81b2Sjmcneill #include <sys/condvar.h>
69b41a7fd4Sthorpej #include <sys/device.h>
70b41a7fd4Sthorpej #include <sys/kernel.h>
714c1d81b2Sjmcneill #include <sys/kthread.h>
724c1d81b2Sjmcneill #include <sys/mutex.h>
735a425210Sjruoho #include <sys/systm.h>
74b41a7fd4Sthorpej
7523ab96a3Smlelstv #include <dev/acpi/acpireg.h>
76b41a7fd4Sthorpej #include <dev/acpi/acpivar.h>
77f5210660Sjmcneill #include <dev/acpi/acpi_ecvar.h>
78b41a7fd4Sthorpej
7923ab96a3Smlelstv #define _COMPONENT ACPI_EC_COMPONENT
8023ab96a3Smlelstv ACPI_MODULE_NAME ("acpi_ec")
8123ab96a3Smlelstv
824c1d81b2Sjmcneill /* Maximum time to wait for global ACPI lock in ms */
834c1d81b2Sjmcneill #define EC_LOCK_TIMEOUT 5
84bd130abaSkochi
854c1d81b2Sjmcneill /* Maximum time to poll for completion of a command in ms */
864c1d81b2Sjmcneill #define EC_POLL_TIMEOUT 5
87b41a7fd4Sthorpej
8836cdfbe0Sjoerg /* Maximum time to give a single EC command in s */
8936cdfbe0Sjoerg #define EC_CMD_TIMEOUT 10
9036cdfbe0Sjoerg
914c1d81b2Sjmcneill /* From ACPI 3.0b, chapter 12.3 */
924c1d81b2Sjmcneill #define EC_COMMAND_READ 0x80
934c1d81b2Sjmcneill #define EC_COMMAND_WRITE 0x81
944c1d81b2Sjmcneill #define EC_COMMAND_BURST_EN 0x82
954c1d81b2Sjmcneill #define EC_COMMAND_BURST_DIS 0x83
964c1d81b2Sjmcneill #define EC_COMMAND_QUERY 0x84
97b41a7fd4Sthorpej
984c1d81b2Sjmcneill /* From ACPI 3.0b, chapter 12.2.1 */
994c1d81b2Sjmcneill #define EC_STATUS_OBF 0x01
1004c1d81b2Sjmcneill #define EC_STATUS_IBF 0x02
1014c1d81b2Sjmcneill #define EC_STATUS_CMD 0x08
1024c1d81b2Sjmcneill #define EC_STATUS_BURST 0x10
1034c1d81b2Sjmcneill #define EC_STATUS_SCI 0x20
1044c1d81b2Sjmcneill #define EC_STATUS_SMI 0x40
105b41a7fd4Sthorpej
106aeb0e451Sriastradh #define EC_STATUS_FMT \
107aeb0e451Sriastradh "\x10\10IGN7\7SMI\6SCI\5BURST\4CMD\3IGN2\2IBF\1OBF"
108aeb0e451Sriastradh
10937725553Sthorpej static const struct device_compatible_entry compat_data[] = {
11037725553Sthorpej { .compat = "PNP0C09" },
11137725553Sthorpej DEVICE_COMPAT_EOL
11284795bd3Skochi };
11384795bd3Skochi
114aeb0e451Sriastradh #define EC_STATE_ENUM(F) \
115aeb0e451Sriastradh F(EC_STATE_QUERY, "QUERY") \
116aeb0e451Sriastradh F(EC_STATE_QUERY_VAL, "QUERY_VAL") \
117aeb0e451Sriastradh F(EC_STATE_READ, "READ") \
118aeb0e451Sriastradh F(EC_STATE_READ_ADDR, "READ_ADDR") \
119aeb0e451Sriastradh F(EC_STATE_READ_VAL, "READ_VAL") \
120aeb0e451Sriastradh F(EC_STATE_WRITE, "WRITE") \
121aeb0e451Sriastradh F(EC_STATE_WRITE_ADDR, "WRITE_ADDR") \
122aeb0e451Sriastradh F(EC_STATE_WRITE_VAL, "WRITE_VAL") \
123aeb0e451Sriastradh F(EC_STATE_FREE, "FREE") \
124aeb0e451Sriastradh
1254c1d81b2Sjmcneill enum ec_state_t {
126aeb0e451Sriastradh #define F(N, S) N,
127aeb0e451Sriastradh EC_STATE_ENUM(F)
128aeb0e451Sriastradh #undef F
1294c1d81b2Sjmcneill };
130b41a7fd4Sthorpej
131aeb0e451Sriastradh #ifdef ACPIEC_DEBUG
132aeb0e451Sriastradh static const char *const acpiec_state_names[] = {
133aeb0e451Sriastradh #define F(N, S) [N] = S,
134aeb0e451Sriastradh EC_STATE_ENUM(F)
135aeb0e451Sriastradh #undef F
136aeb0e451Sriastradh };
137aeb0e451Sriastradh #endif
138aeb0e451Sriastradh
1394c1d81b2Sjmcneill struct acpiec_softc {
140d919d890Sriastradh device_t sc_dev;
141d919d890Sriastradh
1424c1d81b2Sjmcneill ACPI_HANDLE sc_ech;
14372ae89b6Smycroft
1444c1d81b2Sjmcneill ACPI_HANDLE sc_gpeh;
145fb53d8ceSjruoho uint8_t sc_gpebit;
146b41a7fd4Sthorpej
1474c1d81b2Sjmcneill bus_space_tag_t sc_data_st;
1484c1d81b2Sjmcneill bus_space_handle_t sc_data_sh;
149b41a7fd4Sthorpej
1504c1d81b2Sjmcneill bus_space_tag_t sc_csr_st;
1514c1d81b2Sjmcneill bus_space_handle_t sc_csr_sh;
152b41a7fd4Sthorpej
1534c1d81b2Sjmcneill bool sc_need_global_lock;
154fb53d8ceSjruoho uint32_t sc_global_lock;
155b41a7fd4Sthorpej
1564c1d81b2Sjmcneill kmutex_t sc_mtx, sc_access_mtx;
1574c1d81b2Sjmcneill kcondvar_t sc_cv, sc_cv_sci;
1584c1d81b2Sjmcneill enum ec_state_t sc_state;
1594c1d81b2Sjmcneill bool sc_got_sci;
16036cdfbe0Sjoerg callout_t sc_pseudo_intr;
1619a34eb3dSyamt
1624c1d81b2Sjmcneill uint8_t sc_cur_addr, sc_cur_val;
1634c1d81b2Sjmcneill };
164b41a7fd4Sthorpej
165aeb0e451Sriastradh #ifdef ACPIEC_DEBUG
166aeb0e451Sriastradh
167aeb0e451Sriastradh #define ACPIEC_DEBUG_ENUM(F) \
168aeb0e451Sriastradh F(ACPIEC_DEBUG_REG, "REG") \
169aeb0e451Sriastradh F(ACPIEC_DEBUG_RW, "RW") \
170aeb0e451Sriastradh F(ACPIEC_DEBUG_QUERY, "QUERY") \
171aeb0e451Sriastradh F(ACPIEC_DEBUG_TRANSITION, "TRANSITION") \
172aeb0e451Sriastradh F(ACPIEC_DEBUG_INTR, "INTR") \
173aeb0e451Sriastradh
174aeb0e451Sriastradh enum {
175aeb0e451Sriastradh #define F(N, S) N,
176aeb0e451Sriastradh ACPIEC_DEBUG_ENUM(F)
177aeb0e451Sriastradh #undef F
178aeb0e451Sriastradh };
179aeb0e451Sriastradh
180aeb0e451Sriastradh static const char *const acpiec_debug_names[] = {
181aeb0e451Sriastradh #define F(N, S) [N] = S,
182aeb0e451Sriastradh ACPIEC_DEBUG_ENUM(F)
183aeb0e451Sriastradh #undef F
184aeb0e451Sriastradh };
185aeb0e451Sriastradh
186aeb0e451Sriastradh int acpiec_debug = ACPIEC_DEBUG;
187aeb0e451Sriastradh
188aeb0e451Sriastradh #define DPRINTF(n, sc, fmt, ...) do \
189aeb0e451Sriastradh { \
190aeb0e451Sriastradh if (acpiec_debug & __BIT(n)) { \
191aeb0e451Sriastradh char dprintbuf[16]; \
192aeb0e451Sriastradh const char *state; \
193aeb0e451Sriastradh \
194aeb0e451Sriastradh /* paranoia */ \
195aeb0e451Sriastradh if ((sc)->sc_state < __arraycount(acpiec_state_names)) { \
196aeb0e451Sriastradh state = acpiec_state_names[(sc)->sc_state]; \
197aeb0e451Sriastradh } else { \
198aeb0e451Sriastradh snprintf(dprintbuf, sizeof(dprintbuf), "0x%x", \
199aeb0e451Sriastradh (sc)->sc_state); \
200aeb0e451Sriastradh state = dprintbuf; \
201aeb0e451Sriastradh } \
202aeb0e451Sriastradh \
203aeb0e451Sriastradh device_printf((sc)->sc_dev, "(%s) [%s] "fmt, \
204aeb0e451Sriastradh acpiec_debug_names[n], state, ##__VA_ARGS__); \
205aeb0e451Sriastradh } \
206aeb0e451Sriastradh } while (0)
207aeb0e451Sriastradh
208aeb0e451Sriastradh #else
209aeb0e451Sriastradh
210aeb0e451Sriastradh #define DPRINTF(n, sc, fmt, ...) __nothing
211aeb0e451Sriastradh
212aeb0e451Sriastradh #endif
213aeb0e451Sriastradh
214e5339e11Scegger static int acpiecdt_match(device_t, cfdata_t, void *);
2154c1d81b2Sjmcneill static void acpiecdt_attach(device_t, device_t, void *);
216b41a7fd4Sthorpej
217e5339e11Scegger static int acpiec_match(device_t, cfdata_t, void *);
2184c1d81b2Sjmcneill static void acpiec_attach(device_t, device_t, void *);
2194c1d81b2Sjmcneill
2204c1d81b2Sjmcneill static void acpiec_common_attach(device_t, device_t, ACPI_HANDLE,
221f52f7d91Sdyoung bus_space_tag_t, bus_addr_t, bus_space_tag_t, bus_addr_t,
222f52f7d91Sdyoung ACPI_HANDLE, uint8_t);
2234c1d81b2Sjmcneill
224c1b390d4Sdyoung static bool acpiec_suspend(device_t, const pmf_qual_t *);
225c1b390d4Sdyoung static bool acpiec_resume(device_t, const pmf_qual_t *);
2263fb1b3bcSalc static bool acpiec_shutdown(device_t, int);
2274c1d81b2Sjmcneill
2284c1d81b2Sjmcneill static bool acpiec_parse_gpe_package(device_t, ACPI_HANDLE,
2294c1d81b2Sjmcneill ACPI_HANDLE *, uint8_t *);
2304c1d81b2Sjmcneill
23136cdfbe0Sjoerg static void acpiec_callout(void *);
2324c1d81b2Sjmcneill static void acpiec_gpe_query(void *);
2339a25770dSjruoho static uint32_t acpiec_gpe_handler(ACPI_HANDLE, uint32_t, void *);
234fb53d8ceSjruoho static ACPI_STATUS acpiec_space_setup(ACPI_HANDLE, uint32_t, void *, void **);
235fb53d8ceSjruoho static ACPI_STATUS acpiec_space_handler(uint32_t, ACPI_PHYSICAL_ADDRESS,
236fb53d8ceSjruoho uint32_t, ACPI_INTEGER *, void *, void *);
2374c1d81b2Sjmcneill
2384dfab621Sriastradh static void acpiec_gpe_state_machine(struct acpiec_softc *);
2394c1d81b2Sjmcneill
2404c1d81b2Sjmcneill CFATTACH_DECL_NEW(acpiec, sizeof(struct acpiec_softc),
241c9b3657cSthorpej acpiec_match, acpiec_attach, NULL, NULL);
242b41a7fd4Sthorpej
2434c1d81b2Sjmcneill CFATTACH_DECL_NEW(acpiecdt, sizeof(struct acpiec_softc),
2444c1d81b2Sjmcneill acpiecdt_match, acpiecdt_attach, NULL, NULL);
245bd130abaSkochi
2464c1d81b2Sjmcneill static device_t ec_singleton = NULL;
2474c1d81b2Sjmcneill static bool acpiec_cold = false;
2484c1d81b2Sjmcneill
2494c1d81b2Sjmcneill static bool
acpiecdt_find(device_t parent,ACPI_HANDLE * ec_handle,bus_addr_t * cmd_reg,bus_addr_t * data_reg,uint8_t * gpebit)2504c1d81b2Sjmcneill acpiecdt_find(device_t parent, ACPI_HANDLE *ec_handle,
2514c1d81b2Sjmcneill bus_addr_t *cmd_reg, bus_addr_t *data_reg, uint8_t *gpebit)
2529a34eb3dSyamt {
2534c1d81b2Sjmcneill ACPI_TABLE_ECDT *ecdt;
2542bde9b60Skochi ACPI_STATUS rv;
2559a34eb3dSyamt
2564c1d81b2Sjmcneill rv = AcpiGetTable(ACPI_SIG_ECDT, 1, (ACPI_TABLE_HEADER **)&ecdt);
2572bde9b60Skochi if (ACPI_FAILURE(rv))
2584c1d81b2Sjmcneill return false;
2594c1d81b2Sjmcneill
2604c1d81b2Sjmcneill if (ecdt->Control.BitWidth != 8 || ecdt->Data.BitWidth != 8) {
2614c1d81b2Sjmcneill aprint_error_dev(parent,
2624fcde464Sjruoho "ECDT register width invalid (%u/%u)\n",
2634c1d81b2Sjmcneill ecdt->Control.BitWidth, ecdt->Data.BitWidth);
2644c1d81b2Sjmcneill return false;
2659a34eb3dSyamt }
2669a34eb3dSyamt
2674c1d81b2Sjmcneill rv = AcpiGetHandle(ACPI_ROOT_OBJECT, ecdt->Id, ec_handle);
2684c1d81b2Sjmcneill if (ACPI_FAILURE(rv)) {
2694c1d81b2Sjmcneill aprint_error_dev(parent,
2704c1d81b2Sjmcneill "failed to look up EC object %s: %s\n",
2714c1d81b2Sjmcneill ecdt->Id, AcpiFormatException(rv));
2724c1d81b2Sjmcneill return false;
2734c1d81b2Sjmcneill }
2744c1d81b2Sjmcneill
2754c1d81b2Sjmcneill *cmd_reg = ecdt->Control.Address;
2764c1d81b2Sjmcneill *data_reg = ecdt->Data.Address;
2774c1d81b2Sjmcneill *gpebit = ecdt->Gpe;
2784c1d81b2Sjmcneill
2794c1d81b2Sjmcneill return true;
2804c1d81b2Sjmcneill }
2814c1d81b2Sjmcneill
282beb4a7feSkochi static int
acpiecdt_match(device_t parent,cfdata_t match,void * aux)283e5339e11Scegger acpiecdt_match(device_t parent, cfdata_t match, void *aux)
2844c1d81b2Sjmcneill {
2854c1d81b2Sjmcneill ACPI_HANDLE ec_handle;
2864c1d81b2Sjmcneill bus_addr_t cmd_reg, data_reg;
2874c1d81b2Sjmcneill uint8_t gpebit;
2884c1d81b2Sjmcneill
2894c1d81b2Sjmcneill if (acpiecdt_find(parent, &ec_handle, &cmd_reg, &data_reg, &gpebit))
2904c1d81b2Sjmcneill return 1;
2914c1d81b2Sjmcneill else
2924c1d81b2Sjmcneill return 0;
2934c1d81b2Sjmcneill }
2944c1d81b2Sjmcneill
2954c1d81b2Sjmcneill static void
acpiecdt_attach(device_t parent,device_t self,void * aux)2964c1d81b2Sjmcneill acpiecdt_attach(device_t parent, device_t self, void *aux)
2974c1d81b2Sjmcneill {
29808411036Sdyoung struct acpibus_attach_args *aa = aux;
2994c1d81b2Sjmcneill ACPI_HANDLE ec_handle;
3004c1d81b2Sjmcneill bus_addr_t cmd_reg, data_reg;
3014c1d81b2Sjmcneill uint8_t gpebit;
3024c1d81b2Sjmcneill
3034c1d81b2Sjmcneill if (!acpiecdt_find(parent, &ec_handle, &cmd_reg, &data_reg, &gpebit))
3044c1d81b2Sjmcneill panic("ECDT disappeared");
3054c1d81b2Sjmcneill
306c315121aSjmcneill aprint_naive("\n");
3074c1d81b2Sjmcneill aprint_normal(": ACPI Embedded Controller via ECDT\n");
3084c1d81b2Sjmcneill
309f52f7d91Sdyoung acpiec_common_attach(parent, self, ec_handle, aa->aa_iot, cmd_reg,
310f52f7d91Sdyoung aa->aa_iot, data_reg, NULL, gpebit);
3114c1d81b2Sjmcneill }
3124c1d81b2Sjmcneill
3134c1d81b2Sjmcneill static int
acpiec_match(device_t parent,cfdata_t match,void * aux)314e5339e11Scegger acpiec_match(device_t parent, cfdata_t match, void *aux)
315b41a7fd4Sthorpej {
316b41a7fd4Sthorpej struct acpi_attach_args *aa = aux;
317b41a7fd4Sthorpej
31837725553Sthorpej return acpi_compatible_match(aa, compat_data);
319b41a7fd4Sthorpej }
320b41a7fd4Sthorpej
321beb4a7feSkochi static void
acpiec_attach(device_t parent,device_t self,void * aux)3224c1d81b2Sjmcneill acpiec_attach(device_t parent, device_t self, void *aux)
323b41a7fd4Sthorpej {
324b41a7fd4Sthorpej struct acpi_attach_args *aa = aux;
3254c1d81b2Sjmcneill struct acpi_resources ec_res;
326b41a7fd4Sthorpej struct acpi_io *io0, *io1;
3274c1d81b2Sjmcneill ACPI_HANDLE gpe_handle;
3284c1d81b2Sjmcneill uint8_t gpebit;
329b41a7fd4Sthorpej ACPI_STATUS rv;
330b41a7fd4Sthorpej
3314c1d81b2Sjmcneill if (ec_singleton != NULL) {
33274dd0aeeSjmcneill aprint_naive(": using %s\n", device_xname(ec_singleton));
33374dd0aeeSjmcneill aprint_normal(": using %s\n", device_xname(ec_singleton));
33412a0c4d5Sriastradh goto fail0;
3354c1d81b2Sjmcneill }
336b41a7fd4Sthorpej
3378a7046f1Sjdolecek if (!acpi_device_present(aa->aa_node->ad_handle)) {
3388a7046f1Sjdolecek aprint_normal(": not present\n");
3398a7046f1Sjdolecek goto fail0;
3408a7046f1Sjdolecek }
3418a7046f1Sjdolecek
3424c1d81b2Sjmcneill if (!acpiec_parse_gpe_package(self, aa->aa_node->ad_handle,
3434c1d81b2Sjmcneill &gpe_handle, &gpebit))
34412a0c4d5Sriastradh goto fail0;
345b41a7fd4Sthorpej
3464c1d81b2Sjmcneill rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS",
3474c1d81b2Sjmcneill &ec_res, &acpi_resource_parse_ops_default);
3484c1d81b2Sjmcneill if (rv != AE_OK) {
3494c1d81b2Sjmcneill aprint_error_dev(self, "resource parsing failed: %s\n",
350bd130abaSkochi AcpiFormatException(rv));
35112a0c4d5Sriastradh goto fail0;
352bd130abaSkochi }
353bd130abaSkochi
3544c1d81b2Sjmcneill if ((io0 = acpi_res_io(&ec_res, 0)) == NULL) {
3554c1d81b2Sjmcneill aprint_error_dev(self, "no data register resource\n");
35612a0c4d5Sriastradh goto fail1;
357b41a7fd4Sthorpej }
3584c1d81b2Sjmcneill if ((io1 = acpi_res_io(&ec_res, 1)) == NULL) {
3594c1d81b2Sjmcneill aprint_error_dev(self, "no CSR register resource\n");
36012a0c4d5Sriastradh goto fail1;
361b41a7fd4Sthorpej }
362b41a7fd4Sthorpej
3634c1d81b2Sjmcneill acpiec_common_attach(parent, self, aa->aa_node->ad_handle,
364f52f7d91Sdyoung aa->aa_iot, io1->ar_base, aa->aa_iot, io0->ar_base,
365f52f7d91Sdyoung gpe_handle, gpebit);
366b41a7fd4Sthorpej
3674c1d81b2Sjmcneill acpi_resource_cleanup(&ec_res);
36812a0c4d5Sriastradh return;
36912a0c4d5Sriastradh
37012a0c4d5Sriastradh fail1: acpi_resource_cleanup(&ec_res);
37112a0c4d5Sriastradh fail0: if (!pmf_device_register(self, NULL, NULL))
37212a0c4d5Sriastradh aprint_error_dev(self, "couldn't establish power handler\n");
373b41a7fd4Sthorpej }
374b41a7fd4Sthorpej
375b41a7fd4Sthorpej static void
acpiec_common_attach(device_t parent,device_t self,ACPI_HANDLE ec_handle,bus_space_tag_t cmdt,bus_addr_t cmd_reg,bus_space_tag_t datat,bus_addr_t data_reg,ACPI_HANDLE gpe_handle,uint8_t gpebit)3764c1d81b2Sjmcneill acpiec_common_attach(device_t parent, device_t self,
377f52f7d91Sdyoung ACPI_HANDLE ec_handle, bus_space_tag_t cmdt, bus_addr_t cmd_reg,
378f52f7d91Sdyoung bus_space_tag_t datat, bus_addr_t data_reg,
3794c1d81b2Sjmcneill ACPI_HANDLE gpe_handle, uint8_t gpebit)
380b41a7fd4Sthorpej {
3814c1d81b2Sjmcneill struct acpiec_softc *sc = device_private(self);
3822bde9b60Skochi ACPI_STATUS rv;
3834c1d81b2Sjmcneill ACPI_INTEGER val;
384b41a7fd4Sthorpej
385d919d890Sriastradh sc->sc_dev = self;
386d919d890Sriastradh
387f52f7d91Sdyoung sc->sc_csr_st = cmdt;
388f52f7d91Sdyoung sc->sc_data_st = datat;
389f52f7d91Sdyoung
3904c1d81b2Sjmcneill sc->sc_ech = ec_handle;
3914c1d81b2Sjmcneill sc->sc_gpeh = gpe_handle;
3924c1d81b2Sjmcneill sc->sc_gpebit = gpebit;
393b41a7fd4Sthorpej
3944c1d81b2Sjmcneill sc->sc_state = EC_STATE_FREE;
3954c1d81b2Sjmcneill mutex_init(&sc->sc_mtx, MUTEX_DRIVER, IPL_TTY);
3964c1d81b2Sjmcneill mutex_init(&sc->sc_access_mtx, MUTEX_DEFAULT, IPL_NONE);
3974c1d81b2Sjmcneill cv_init(&sc->sc_cv, "eccv");
3984c1d81b2Sjmcneill cv_init(&sc->sc_cv_sci, "ecsci");
3994c1d81b2Sjmcneill
4004c1d81b2Sjmcneill if (bus_space_map(sc->sc_data_st, data_reg, 1, 0,
4014c1d81b2Sjmcneill &sc->sc_data_sh) != 0) {
4024c1d81b2Sjmcneill aprint_error_dev(self, "unable to map data register\n");
4034c1d81b2Sjmcneill return;
4044c1d81b2Sjmcneill }
4054c1d81b2Sjmcneill
4064c1d81b2Sjmcneill if (bus_space_map(sc->sc_csr_st, cmd_reg, 1, 0, &sc->sc_csr_sh) != 0) {
4074c1d81b2Sjmcneill aprint_error_dev(self, "unable to map CSR register\n");
4084c1d81b2Sjmcneill goto post_data_map;
4094c1d81b2Sjmcneill }
4104c1d81b2Sjmcneill
4114c1d81b2Sjmcneill rv = acpi_eval_integer(sc->sc_ech, "_GLK", &val);
4124c1d81b2Sjmcneill if (rv == AE_OK) {
4134c1d81b2Sjmcneill sc->sc_need_global_lock = val != 0;
4144c1d81b2Sjmcneill } else if (rv != AE_NOT_FOUND) {
4154c1d81b2Sjmcneill aprint_error_dev(self, "unable to evaluate _GLK: %s\n",
4164c1d81b2Sjmcneill AcpiFormatException(rv));
4174c1d81b2Sjmcneill goto post_csr_map;
4184c1d81b2Sjmcneill } else {
4194c1d81b2Sjmcneill sc->sc_need_global_lock = false;
4204c1d81b2Sjmcneill }
4214c1d81b2Sjmcneill if (sc->sc_need_global_lock)
4224c1d81b2Sjmcneill aprint_normal_dev(self, "using global ACPI lock\n");
4234c1d81b2Sjmcneill
42436cdfbe0Sjoerg callout_init(&sc->sc_pseudo_intr, CALLOUT_MPSAFE);
425c5f043f1Sriastradh callout_setfunc(&sc->sc_pseudo_intr, acpiec_callout, sc);
42636cdfbe0Sjoerg
4274c1d81b2Sjmcneill rv = AcpiInstallAddressSpaceHandler(sc->sc_ech, ACPI_ADR_SPACE_EC,
4287951f6eaSriastradh acpiec_space_handler, acpiec_space_setup, sc);
4294c1d81b2Sjmcneill if (rv != AE_OK) {
4304c1d81b2Sjmcneill aprint_error_dev(self,
4314c1d81b2Sjmcneill "unable to install address space handler: %s\n",
4324c1d81b2Sjmcneill AcpiFormatException(rv));
4334c1d81b2Sjmcneill goto post_csr_map;
4344c1d81b2Sjmcneill }
4354c1d81b2Sjmcneill
4364c1d81b2Sjmcneill rv = AcpiInstallGpeHandler(sc->sc_gpeh, sc->sc_gpebit,
43709324b0aSriastradh ACPI_GPE_EDGE_TRIGGERED, acpiec_gpe_handler, sc);
4384c1d81b2Sjmcneill if (rv != AE_OK) {
4394c1d81b2Sjmcneill aprint_error_dev(self, "unable to install GPE handler: %s\n",
4404c1d81b2Sjmcneill AcpiFormatException(rv));
4414c1d81b2Sjmcneill goto post_csr_map;
4424c1d81b2Sjmcneill }
4434c1d81b2Sjmcneill
4449a25770dSjruoho rv = AcpiEnableGpe(sc->sc_gpeh, sc->sc_gpebit);
4454c1d81b2Sjmcneill if (rv != AE_OK) {
4464c1d81b2Sjmcneill aprint_error_dev(self, "unable to enable GPE: %s\n",
4474c1d81b2Sjmcneill AcpiFormatException(rv));
4484c1d81b2Sjmcneill goto post_csr_map;
4494c1d81b2Sjmcneill }
4504c1d81b2Sjmcneill
4514c1d81b2Sjmcneill if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, acpiec_gpe_query,
45278820b19Sriastradh sc, NULL, "acpiec sci thread")) {
4534c1d81b2Sjmcneill aprint_error_dev(self, "unable to create query kthread\n");
4544c1d81b2Sjmcneill goto post_csr_map;
4554c1d81b2Sjmcneill }
4564c1d81b2Sjmcneill
4574c1d81b2Sjmcneill ec_singleton = self;
4584c1d81b2Sjmcneill
4593fb1b3bcSalc if (!pmf_device_register1(self, acpiec_suspend, acpiec_resume,
4603fb1b3bcSalc acpiec_shutdown))
4614c1d81b2Sjmcneill aprint_error_dev(self, "couldn't establish power handler\n");
4624c1d81b2Sjmcneill
4634c1d81b2Sjmcneill return;
4644c1d81b2Sjmcneill
4654c1d81b2Sjmcneill post_csr_map:
4664c1d81b2Sjmcneill (void)AcpiRemoveGpeHandler(sc->sc_gpeh, sc->sc_gpebit,
4674c1d81b2Sjmcneill acpiec_gpe_handler);
4684c1d81b2Sjmcneill (void)AcpiRemoveAddressSpaceHandler(sc->sc_ech,
4694c1d81b2Sjmcneill ACPI_ADR_SPACE_EC, acpiec_space_handler);
4704c1d81b2Sjmcneill bus_space_unmap(sc->sc_csr_st, sc->sc_csr_sh, 1);
4714c1d81b2Sjmcneill post_data_map:
4724c1d81b2Sjmcneill bus_space_unmap(sc->sc_data_st, sc->sc_data_sh, 1);
47312a0c4d5Sriastradh if (!pmf_device_register(self, NULL, NULL))
47412a0c4d5Sriastradh aprint_error_dev(self, "couldn't establish power handler\n");
4754c1d81b2Sjmcneill }
4764c1d81b2Sjmcneill
4774c1d81b2Sjmcneill static bool
acpiec_suspend(device_t dv,const pmf_qual_t * qual)478c1b390d4Sdyoung acpiec_suspend(device_t dv, const pmf_qual_t *qual)
4794c1d81b2Sjmcneill {
480cf29e175Sriastradh struct acpiec_softc *sc = device_private(dv);
481b30a235bSriastradh
482cf29e175Sriastradh /*
483cf29e175Sriastradh * XXX This looks bad because acpiec_cold is global and
484cf29e175Sriastradh * sc->sc_mtx doesn't look like it's global, but we can have
485cf29e175Sriastradh * only one acpiec(4) device anyway. Maybe acpiec_cold should
486cf29e175Sriastradh * live in the softc to make this look less bad?
487cf29e175Sriastradh *
488cf29e175Sriastradh * XXX Should this block read/write/query transactions until
489cf29e175Sriastradh * resume?
490cf29e175Sriastradh *
491cf29e175Sriastradh * XXX Should this interrupt existing transactions to make them
492cf29e175Sriastradh * fail promptly or restart on resume?
493cf29e175Sriastradh */
494cf29e175Sriastradh mutex_enter(&sc->sc_mtx);
4954c1d81b2Sjmcneill acpiec_cold = true;
496cf29e175Sriastradh mutex_exit(&sc->sc_mtx);
4974c1d81b2Sjmcneill
4984c1d81b2Sjmcneill return true;
4994c1d81b2Sjmcneill }
5004c1d81b2Sjmcneill
5014c1d81b2Sjmcneill static bool
acpiec_resume(device_t dv,const pmf_qual_t * qual)502c1b390d4Sdyoung acpiec_resume(device_t dv, const pmf_qual_t *qual)
5034c1d81b2Sjmcneill {
504cf29e175Sriastradh struct acpiec_softc *sc = device_private(dv);
505b30a235bSriastradh
506cf29e175Sriastradh mutex_enter(&sc->sc_mtx);
5074c1d81b2Sjmcneill acpiec_cold = false;
508cf29e175Sriastradh mutex_exit(&sc->sc_mtx);
5094c1d81b2Sjmcneill
5104c1d81b2Sjmcneill return true;
5114c1d81b2Sjmcneill }
5124c1d81b2Sjmcneill
5134c1d81b2Sjmcneill static bool
acpiec_shutdown(device_t dv,int how)5143fb1b3bcSalc acpiec_shutdown(device_t dv, int how)
5153fb1b3bcSalc {
516cf29e175Sriastradh struct acpiec_softc *sc = device_private(dv);
5173fb1b3bcSalc
518cf29e175Sriastradh mutex_enter(&sc->sc_mtx);
5193fb1b3bcSalc acpiec_cold = true;
520cf29e175Sriastradh mutex_exit(&sc->sc_mtx);
521cf29e175Sriastradh
5223fb1b3bcSalc return true;
5233fb1b3bcSalc }
5243fb1b3bcSalc
5253fb1b3bcSalc static bool
acpiec_parse_gpe_package(device_t self,ACPI_HANDLE ec_handle,ACPI_HANDLE * gpe_handle,uint8_t * gpebit)5264c1d81b2Sjmcneill acpiec_parse_gpe_package(device_t self, ACPI_HANDLE ec_handle,
5274c1d81b2Sjmcneill ACPI_HANDLE *gpe_handle, uint8_t *gpebit)
5284c1d81b2Sjmcneill {
5294c1d81b2Sjmcneill ACPI_BUFFER buf;
5304c1d81b2Sjmcneill ACPI_OBJECT *p, *c;
5314c1d81b2Sjmcneill ACPI_STATUS rv;
5324c1d81b2Sjmcneill
5334c1d81b2Sjmcneill rv = acpi_eval_struct(ec_handle, "_GPE", &buf);
5344c1d81b2Sjmcneill if (rv != AE_OK) {
5354c1d81b2Sjmcneill aprint_error_dev(self, "unable to evaluate _GPE: %s\n",
5364c1d81b2Sjmcneill AcpiFormatException(rv));
5374c1d81b2Sjmcneill return false;
5384c1d81b2Sjmcneill }
5394c1d81b2Sjmcneill
5404c1d81b2Sjmcneill p = buf.Pointer;
5414c1d81b2Sjmcneill
5424c1d81b2Sjmcneill if (p->Type == ACPI_TYPE_INTEGER) {
5434c1d81b2Sjmcneill *gpe_handle = NULL;
5444c1d81b2Sjmcneill *gpebit = p->Integer.Value;
54523ab96a3Smlelstv ACPI_FREE(p);
5464c1d81b2Sjmcneill return true;
5474c1d81b2Sjmcneill }
5484c1d81b2Sjmcneill
5494c1d81b2Sjmcneill if (p->Type != ACPI_TYPE_PACKAGE) {
5504c1d81b2Sjmcneill aprint_error_dev(self, "_GPE is neither integer nor package\n");
55123ab96a3Smlelstv ACPI_FREE(p);
5524c1d81b2Sjmcneill return false;
5534c1d81b2Sjmcneill }
5544c1d81b2Sjmcneill
5554c1d81b2Sjmcneill if (p->Package.Count != 2) {
556b30a235bSriastradh aprint_error_dev(self,
557b30a235bSriastradh "_GPE package does not contain 2 elements\n");
55823ab96a3Smlelstv ACPI_FREE(p);
5594c1d81b2Sjmcneill return false;
5604c1d81b2Sjmcneill }
5614c1d81b2Sjmcneill
5624c1d81b2Sjmcneill c = &p->Package.Elements[0];
563e5a9d901Sjruoho rv = acpi_eval_reference_handle(c, gpe_handle);
564e5a9d901Sjruoho
565e5a9d901Sjruoho if (ACPI_FAILURE(rv)) {
566e5a9d901Sjruoho aprint_error_dev(self, "failed to evaluate _GPE handle\n");
56723ab96a3Smlelstv ACPI_FREE(p);
5684c1d81b2Sjmcneill return false;
5694c1d81b2Sjmcneill }
570e5a9d901Sjruoho
5714c1d81b2Sjmcneill c = &p->Package.Elements[1];
572e5a9d901Sjruoho
5734c1d81b2Sjmcneill if (c->Type != ACPI_TYPE_INTEGER) {
5744c1d81b2Sjmcneill aprint_error_dev(self,
5754c1d81b2Sjmcneill "_GPE package needs integer as 2nd field\n");
57623ab96a3Smlelstv ACPI_FREE(p);
5774c1d81b2Sjmcneill return false;
5784c1d81b2Sjmcneill }
5794c1d81b2Sjmcneill *gpebit = c->Integer.Value;
58023ab96a3Smlelstv ACPI_FREE(p);
5814c1d81b2Sjmcneill return true;
5824c1d81b2Sjmcneill }
583b41a7fd4Sthorpej
5844c1d81b2Sjmcneill static uint8_t
acpiec_read_data(struct acpiec_softc * sc)5854c1d81b2Sjmcneill acpiec_read_data(struct acpiec_softc *sc)
5864c1d81b2Sjmcneill {
587aeb0e451Sriastradh uint8_t x;
588aeb0e451Sriastradh
5896d1cd1baSriastradh KASSERT(mutex_owned(&sc->sc_mtx));
5906d1cd1baSriastradh
591aeb0e451Sriastradh x = bus_space_read_1(sc->sc_data_st, sc->sc_data_sh, 0);
592aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_REG, sc, "read data=0x%"PRIx8"\n", x);
593aeb0e451Sriastradh
594aeb0e451Sriastradh return x;
5954c1d81b2Sjmcneill }
5969a34eb3dSyamt
5974c1d81b2Sjmcneill static void
acpiec_write_data(struct acpiec_softc * sc,uint8_t val)5984c1d81b2Sjmcneill acpiec_write_data(struct acpiec_softc *sc, uint8_t val)
5994c1d81b2Sjmcneill {
600aeb0e451Sriastradh
6016d1cd1baSriastradh KASSERT(mutex_owned(&sc->sc_mtx));
6026d1cd1baSriastradh
603aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_REG, sc, "write data=0x%"PRIx8"\n", val);
6044c1d81b2Sjmcneill bus_space_write_1(sc->sc_data_st, sc->sc_data_sh, 0, val);
6054c1d81b2Sjmcneill }
606b41a7fd4Sthorpej
6074c1d81b2Sjmcneill static uint8_t
acpiec_read_status(struct acpiec_softc * sc)6084c1d81b2Sjmcneill acpiec_read_status(struct acpiec_softc *sc)
6094c1d81b2Sjmcneill {
610aeb0e451Sriastradh uint8_t x;
611aeb0e451Sriastradh
6126d1cd1baSriastradh KASSERT(mutex_owned(&sc->sc_mtx));
6136d1cd1baSriastradh
614aeb0e451Sriastradh x = bus_space_read_1(sc->sc_csr_st, sc->sc_csr_sh, 0);
615aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_REG, sc, "read status=0x%"PRIx8"\n", x);
616aeb0e451Sriastradh
617aeb0e451Sriastradh return x;
6184c1d81b2Sjmcneill }
6199a34eb3dSyamt
6204c1d81b2Sjmcneill static void
acpiec_write_command(struct acpiec_softc * sc,uint8_t cmd)6214c1d81b2Sjmcneill acpiec_write_command(struct acpiec_softc *sc, uint8_t cmd)
6224c1d81b2Sjmcneill {
623aeb0e451Sriastradh
6246d1cd1baSriastradh KASSERT(mutex_owned(&sc->sc_mtx));
6256d1cd1baSriastradh
626aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_REG, sc, "write command=0x%"PRIx8"\n", cmd);
6274c1d81b2Sjmcneill bus_space_write_1(sc->sc_csr_st, sc->sc_csr_sh, 0, cmd);
6284c1d81b2Sjmcneill }
6294c1d81b2Sjmcneill
6304c1d81b2Sjmcneill static ACPI_STATUS
acpiec_space_setup(ACPI_HANDLE region,uint32_t func,void * arg,void ** region_arg)631fb53d8ceSjruoho acpiec_space_setup(ACPI_HANDLE region, uint32_t func, void *arg,
6324c1d81b2Sjmcneill void **region_arg)
6334c1d81b2Sjmcneill {
634b30a235bSriastradh
6354c1d81b2Sjmcneill if (func == ACPI_REGION_DEACTIVATE)
6364c1d81b2Sjmcneill *region_arg = NULL;
6374c1d81b2Sjmcneill else
6384c1d81b2Sjmcneill *region_arg = arg;
6394c1d81b2Sjmcneill
6404c1d81b2Sjmcneill return AE_OK;
6414c1d81b2Sjmcneill }
6424c1d81b2Sjmcneill
6434c1d81b2Sjmcneill static void
acpiec_lock(struct acpiec_softc * sc)644f29130a7Sriastradh acpiec_lock(struct acpiec_softc *sc)
6454c1d81b2Sjmcneill {
6464c1d81b2Sjmcneill ACPI_STATUS rv;
6474c1d81b2Sjmcneill
6484c1d81b2Sjmcneill mutex_enter(&sc->sc_access_mtx);
6494c1d81b2Sjmcneill
6504c1d81b2Sjmcneill if (sc->sc_need_global_lock) {
651b30a235bSriastradh rv = AcpiAcquireGlobalLock(EC_LOCK_TIMEOUT,
652b30a235bSriastradh &sc->sc_global_lock);
6534c1d81b2Sjmcneill if (rv != AE_OK) {
654f29130a7Sriastradh aprint_error_dev(sc->sc_dev,
655b30a235bSriastradh "failed to acquire global lock: %s\n",
6564c1d81b2Sjmcneill AcpiFormatException(rv));
6574c1d81b2Sjmcneill return;
6584c1d81b2Sjmcneill }
6594c1d81b2Sjmcneill }
6604c1d81b2Sjmcneill }
6614c1d81b2Sjmcneill
6624c1d81b2Sjmcneill static void
acpiec_unlock(struct acpiec_softc * sc)663f29130a7Sriastradh acpiec_unlock(struct acpiec_softc *sc)
6644c1d81b2Sjmcneill {
6654c1d81b2Sjmcneill ACPI_STATUS rv;
6664c1d81b2Sjmcneill
6674c1d81b2Sjmcneill if (sc->sc_need_global_lock) {
6684c1d81b2Sjmcneill rv = AcpiReleaseGlobalLock(sc->sc_global_lock);
6694c1d81b2Sjmcneill if (rv != AE_OK) {
670f29130a7Sriastradh aprint_error_dev(sc->sc_dev,
671b30a235bSriastradh "failed to release global lock: %s\n",
6724c1d81b2Sjmcneill AcpiFormatException(rv));
6734c1d81b2Sjmcneill }
6744c1d81b2Sjmcneill }
6754c1d81b2Sjmcneill mutex_exit(&sc->sc_access_mtx);
6764c1d81b2Sjmcneill }
6774c1d81b2Sjmcneill
6784c1d81b2Sjmcneill static ACPI_STATUS
acpiec_wait_timeout(struct acpiec_softc * sc)6792d6bed45Sriastradh acpiec_wait_timeout(struct acpiec_softc *sc)
6802d6bed45Sriastradh {
6812d6bed45Sriastradh device_t dv = sc->sc_dev;
6822d6bed45Sriastradh int i;
6832d6bed45Sriastradh
6842d6bed45Sriastradh for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
6854dfab621Sriastradh acpiec_gpe_state_machine(sc);
6862d6bed45Sriastradh if (sc->sc_state == EC_STATE_FREE)
6872d6bed45Sriastradh return AE_OK;
6882d6bed45Sriastradh delay(1);
6892d6bed45Sriastradh }
6902d6bed45Sriastradh
691*37896b8eSriastradh DPRINTF(ACPIEC_DEBUG_RW, sc, "SCI polling timeout\n");
6922d6bed45Sriastradh if (cold || acpiec_cold) {
6932d6bed45Sriastradh int timeo = 1000 * EC_CMD_TIMEOUT;
6942d6bed45Sriastradh
6952d6bed45Sriastradh while (sc->sc_state != EC_STATE_FREE && timeo-- > 0) {
6962d6bed45Sriastradh delay(1000);
6974dfab621Sriastradh acpiec_gpe_state_machine(sc);
6982d6bed45Sriastradh }
6992d6bed45Sriastradh if (sc->sc_state != EC_STATE_FREE) {
7002d6bed45Sriastradh aprint_error_dev(dv, "command timed out, state %d\n",
7012d6bed45Sriastradh sc->sc_state);
7022d6bed45Sriastradh return AE_ERROR;
7032d6bed45Sriastradh }
7042d6bed45Sriastradh } else {
7052d6bed45Sriastradh const unsigned deadline = getticks() + EC_CMD_TIMEOUT*hz;
7062d6bed45Sriastradh unsigned delta;
7072d6bed45Sriastradh
7082d6bed45Sriastradh while (sc->sc_state != EC_STATE_FREE &&
7092d6bed45Sriastradh (delta = deadline - getticks()) < INT_MAX)
7102d6bed45Sriastradh (void)cv_timedwait(&sc->sc_cv, &sc->sc_mtx, delta);
7112d6bed45Sriastradh if (sc->sc_state != EC_STATE_FREE) {
7122d6bed45Sriastradh aprint_error_dev(dv,
7132d6bed45Sriastradh "command takes over %d sec...\n",
7142d6bed45Sriastradh EC_CMD_TIMEOUT);
7152d6bed45Sriastradh return AE_ERROR;
7162d6bed45Sriastradh }
7172d6bed45Sriastradh }
7182d6bed45Sriastradh
7192d6bed45Sriastradh return AE_OK;
7202d6bed45Sriastradh }
7212d6bed45Sriastradh
7222d6bed45Sriastradh static ACPI_STATUS
acpiec_read(struct acpiec_softc * sc,uint8_t addr,uint8_t * val)723ed4f1b04Sriastradh acpiec_read(struct acpiec_softc *sc, uint8_t addr, uint8_t *val)
7244c1d81b2Sjmcneill {
7251788c938Sriastradh ACPI_STATUS rv;
7264c1d81b2Sjmcneill
727f29130a7Sriastradh acpiec_lock(sc);
7284c1d81b2Sjmcneill mutex_enter(&sc->sc_mtx);
7294c1d81b2Sjmcneill
730aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_RW, sc,
731aeb0e451Sriastradh "pid %ld %s, lid %ld%s%s: read addr 0x%"PRIx8"\n",
732aeb0e451Sriastradh (long)curproc->p_pid, curproc->p_comm,
733aeb0e451Sriastradh (long)curlwp->l_lid, curlwp->l_name ? " " : "",
734aeb0e451Sriastradh curlwp->l_name ? curlwp->l_name : "",
735aeb0e451Sriastradh addr);
736aeb0e451Sriastradh
737c66ad441Sriastradh KASSERT(sc->sc_state == EC_STATE_FREE);
738c66ad441Sriastradh
7394c1d81b2Sjmcneill sc->sc_cur_addr = addr;
7404c1d81b2Sjmcneill sc->sc_state = EC_STATE_READ;
7414c1d81b2Sjmcneill
7422d6bed45Sriastradh rv = acpiec_wait_timeout(sc);
7432d6bed45Sriastradh if (ACPI_FAILURE(rv))
7441788c938Sriastradh goto out;
7458603578eSriastradh
746aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_RW, sc,
747aeb0e451Sriastradh "pid %ld %s, lid %ld%s%s: read addr 0x%"PRIx8": 0x%"PRIx8"\n",
748aeb0e451Sriastradh (long)curproc->p_pid, curproc->p_comm,
749aeb0e451Sriastradh (long)curlwp->l_lid, curlwp->l_name ? " " : "",
750aeb0e451Sriastradh curlwp->l_name ? curlwp->l_name : "",
751aeb0e451Sriastradh addr, sc->sc_cur_val);
752aeb0e451Sriastradh
7534c1d81b2Sjmcneill *val = sc->sc_cur_val;
7542d6bed45Sriastradh
7552d6bed45Sriastradh out: mutex_exit(&sc->sc_mtx);
756f29130a7Sriastradh acpiec_unlock(sc);
7571788c938Sriastradh return rv;
7584c1d81b2Sjmcneill }
7594c1d81b2Sjmcneill
7604c1d81b2Sjmcneill static ACPI_STATUS
acpiec_write(struct acpiec_softc * sc,uint8_t addr,uint8_t val)761ed4f1b04Sriastradh acpiec_write(struct acpiec_softc *sc, uint8_t addr, uint8_t val)
7624c1d81b2Sjmcneill {
7631788c938Sriastradh ACPI_STATUS rv;
7644c1d81b2Sjmcneill
765f29130a7Sriastradh acpiec_lock(sc);
7664c1d81b2Sjmcneill mutex_enter(&sc->sc_mtx);
7674c1d81b2Sjmcneill
768aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_RW, sc,
769aeb0e451Sriastradh "pid %ld %s, lid %ld%s%s write addr 0x%"PRIx8": 0x%"PRIx8"\n",
770aeb0e451Sriastradh (long)curproc->p_pid, curproc->p_comm,
771aeb0e451Sriastradh (long)curlwp->l_lid, curlwp->l_name ? " " : "",
772aeb0e451Sriastradh curlwp->l_name ? curlwp->l_name : "",
773aeb0e451Sriastradh addr, val);
774aeb0e451Sriastradh
775c66ad441Sriastradh KASSERT(sc->sc_state == EC_STATE_FREE);
776c66ad441Sriastradh
7774c1d81b2Sjmcneill sc->sc_cur_addr = addr;
7784c1d81b2Sjmcneill sc->sc_cur_val = val;
7794c1d81b2Sjmcneill sc->sc_state = EC_STATE_WRITE;
7804c1d81b2Sjmcneill
7812d6bed45Sriastradh rv = acpiec_wait_timeout(sc);
7822d6bed45Sriastradh if (ACPI_FAILURE(rv))
7831788c938Sriastradh goto out;
7848603578eSriastradh
785aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_RW, sc,
786aeb0e451Sriastradh "pid %ld %s, lid %ld%s%s: write addr 0x%"PRIx8": 0x%"PRIx8
787aeb0e451Sriastradh " done\n",
788aeb0e451Sriastradh (long)curproc->p_pid, curproc->p_comm,
789aeb0e451Sriastradh (long)curlwp->l_lid, curlwp->l_name ? " " : "",
790aeb0e451Sriastradh curlwp->l_name ? curlwp->l_name : "",
791aeb0e451Sriastradh addr, val);
7922d6bed45Sriastradh
7932d6bed45Sriastradh out: mutex_exit(&sc->sc_mtx);
794f29130a7Sriastradh acpiec_unlock(sc);
7951788c938Sriastradh return rv;
7964c1d81b2Sjmcneill }
7974c1d81b2Sjmcneill
7988eacad47Sriastradh /*
7998eacad47Sriastradh * acpiec_space_handler(func, paddr, bitwidth, value, arg, region_arg)
8008eacad47Sriastradh *
8018eacad47Sriastradh * Transfer bitwidth/8 bytes of data between paddr and *value:
8028eacad47Sriastradh * from paddr to *value when func is ACPI_READ, and the other way
8037951f6eaSriastradh * when func is ACPI_WRITE. arg is the acpiec_softc pointer.
8047951f6eaSriastradh * region_arg is ignored (XXX why? determined by
8058eacad47Sriastradh * acpiec_space_setup but never used by anything that I can see).
8068eacad47Sriastradh *
8078eacad47Sriastradh * The caller always provides storage at *value large enough for
8088eacad47Sriastradh * an ACPI_INTEGER object, i.e., a 64-bit integer. However,
8098eacad47Sriastradh * bitwidth may be larger; in this case the caller provides larger
8108eacad47Sriastradh * storage at *value, e.g. 128 bits as documented in
8118eacad47Sriastradh * <https://gnats.netbsd.org/55206>.
8128eacad47Sriastradh *
8138eacad47Sriastradh * On reads, this fully initializes one ACPI_INTEGER's worth of
8148eacad47Sriastradh * data at *value, even if bitwidth < 64. The integer is
8158eacad47Sriastradh * interpreted in host byte order; in other words, bytes of data
8168eacad47Sriastradh * are transferred in order between paddr and (uint8_t *)value.
8178eacad47Sriastradh * The transfer is not atomic; it may go byte-by-byte.
8188eacad47Sriastradh *
8198eacad47Sriastradh * XXX This only really makes sense on little-endian systems.
8208eacad47Sriastradh * E.g., thinkpad_acpi.c assumes that a single byte is transferred
8218eacad47Sriastradh * in the low-order bits of the result. A big-endian system could
8228eacad47Sriastradh * read a 64-bit integer in big-endian (and it did for a while!),
8238eacad47Sriastradh * but what should it do for larger reads? Unclear!
8248eacad47Sriastradh *
8258eacad47Sriastradh * XXX It's not clear whether the object at *value is always
8268eacad47Sriastradh * _aligned_ adequately for an ACPI_INTEGER object. Currently it
8278eacad47Sriastradh * always is as long as malloc, used by AcpiOsAllocate, returns
8288eacad47Sriastradh * 64-bit-aligned data.
8298eacad47Sriastradh */
8304c1d81b2Sjmcneill static ACPI_STATUS
acpiec_space_handler(uint32_t func,ACPI_PHYSICAL_ADDRESS paddr,uint32_t width,ACPI_INTEGER * value,void * arg,void * region_arg)831fb53d8ceSjruoho acpiec_space_handler(uint32_t func, ACPI_PHYSICAL_ADDRESS paddr,
832fb53d8ceSjruoho uint32_t width, ACPI_INTEGER *value, void *arg, void *region_arg)
8334c1d81b2Sjmcneill {
8347951f6eaSriastradh struct acpiec_softc *sc = arg;
8354c1d81b2Sjmcneill ACPI_STATUS rv;
8364963ddcaSjmcneill uint8_t addr, *buf;
837bb9c42c5Sriastradh unsigned int i;
8384c1d81b2Sjmcneill
8394963ddcaSjmcneill if (paddr > 0xff || width % 8 != 0 ||
840f6b4b121Sriastradh value == NULL || arg == NULL || paddr + width / 8 > 0x100)
8414c1d81b2Sjmcneill return AE_BAD_PARAMETER;
8424c1d81b2Sjmcneill
8434c1d81b2Sjmcneill addr = paddr;
8444963ddcaSjmcneill buf = (uint8_t *)value;
8454c1d81b2Sjmcneill
8464c1d81b2Sjmcneill rv = AE_OK;
8474c1d81b2Sjmcneill
8480aae29f3Sriastradh switch (func) {
8490aae29f3Sriastradh case ACPI_READ:
8504963ddcaSjmcneill for (i = 0; i < width; i += 8, ++addr, ++buf) {
851ed4f1b04Sriastradh rv = acpiec_read(sc, addr, buf);
8520aae29f3Sriastradh if (rv != AE_OK)
8530aae29f3Sriastradh break;
854bb9c42c5Sriastradh }
8558eacad47Sriastradh /*
8568eacad47Sriastradh * Make sure to fully initialize at least an
8578eacad47Sriastradh * ACPI_INTEGER-sized object.
8588eacad47Sriastradh */
8598eacad47Sriastradh for (; i < sizeof(*value)*8; i += 8, ++buf)
8608eacad47Sriastradh *buf = 0;
861bb9c42c5Sriastradh break;
862bb9c42c5Sriastradh case ACPI_WRITE:
8634963ddcaSjmcneill for (i = 0; i < width; i += 8, ++addr, ++buf) {
864ed4f1b04Sriastradh rv = acpiec_write(sc, addr, *buf);
865bb9c42c5Sriastradh if (rv != AE_OK)
866bb9c42c5Sriastradh break;
867bb9c42c5Sriastradh }
868bb9c42c5Sriastradh break;
869bb9c42c5Sriastradh default:
870ed4f1b04Sriastradh aprint_error_dev(sc->sc_dev,
871ed4f1b04Sriastradh "invalid Address Space function called: %x\n",
872ed4f1b04Sriastradh (unsigned int)func);
873bb9c42c5Sriastradh return AE_BAD_PARAMETER;
874bb9c42c5Sriastradh }
8754c1d81b2Sjmcneill
8764c1d81b2Sjmcneill return rv;
8774c1d81b2Sjmcneill }
8784c1d81b2Sjmcneill
8794c1d81b2Sjmcneill static void
acpiec_wait(struct acpiec_softc * sc)8802d6bed45Sriastradh acpiec_wait(struct acpiec_softc *sc)
8812d6bed45Sriastradh {
8822d6bed45Sriastradh int i;
8832d6bed45Sriastradh
8842d6bed45Sriastradh /*
8852d6bed45Sriastradh * First, attempt to get the query by polling.
8862d6bed45Sriastradh */
8872d6bed45Sriastradh for (i = 0; i < EC_POLL_TIMEOUT; ++i) {
8884dfab621Sriastradh acpiec_gpe_state_machine(sc);
8892d6bed45Sriastradh if (sc->sc_state == EC_STATE_FREE)
8902d6bed45Sriastradh return;
8912d6bed45Sriastradh delay(1);
8922d6bed45Sriastradh }
8932d6bed45Sriastradh
8942d6bed45Sriastradh /*
8952d6bed45Sriastradh * Polling timed out. Try waiting for interrupts -- either GPE
8962d6bed45Sriastradh * interrupts, or periodic callouts in case GPE interrupts are
8972d6bed45Sriastradh * broken.
8982d6bed45Sriastradh */
8992d6bed45Sriastradh DPRINTF(ACPIEC_DEBUG_QUERY, sc, "SCI polling timeout\n");
9002d6bed45Sriastradh while (sc->sc_state != EC_STATE_FREE)
9012d6bed45Sriastradh cv_wait(&sc->sc_cv, &sc->sc_mtx);
9022d6bed45Sriastradh }
9032d6bed45Sriastradh
9042d6bed45Sriastradh static void
acpiec_gpe_query(void * arg)9054c1d81b2Sjmcneill acpiec_gpe_query(void *arg)
9064c1d81b2Sjmcneill {
90778820b19Sriastradh struct acpiec_softc *sc = arg;
9084c1d81b2Sjmcneill uint8_t reg;
9094c1d81b2Sjmcneill char qxx[5];
9104c1d81b2Sjmcneill ACPI_STATUS rv;
9114c1d81b2Sjmcneill
9124c1d81b2Sjmcneill loop:
913f222fc6eSriastradh /*
914f222fc6eSriastradh * Wait until the EC sends an SCI requesting a query.
915f222fc6eSriastradh */
9164c1d81b2Sjmcneill mutex_enter(&sc->sc_mtx);
917f7af87d4Sriastradh while (!sc->sc_got_sci)
9184c1d81b2Sjmcneill cv_wait(&sc->sc_cv_sci, &sc->sc_mtx);
919aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_QUERY, sc, "SCI query requested\n");
9204c1d81b2Sjmcneill mutex_exit(&sc->sc_mtx);
9214c1d81b2Sjmcneill
922f222fc6eSriastradh /*
923f222fc6eSriastradh * EC wants to submit a query to us. Exclude concurrent reads
924f222fc6eSriastradh * and writes while we handle it.
925f222fc6eSriastradh */
926f29130a7Sriastradh acpiec_lock(sc);
9274c1d81b2Sjmcneill mutex_enter(&sc->sc_mtx);
9284c1d81b2Sjmcneill
929aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_QUERY, sc, "SCI query\n");
930aeb0e451Sriastradh
931c66ad441Sriastradh KASSERT(sc->sc_state == EC_STATE_FREE);
932c66ad441Sriastradh
9334c1d81b2Sjmcneill /* The Query command can always be issued, so be defensive here. */
934f7af87d4Sriastradh KASSERT(sc->sc_got_sci);
9354c1d81b2Sjmcneill sc->sc_got_sci = false;
9364c1d81b2Sjmcneill sc->sc_state = EC_STATE_QUERY;
9374c1d81b2Sjmcneill
9382d6bed45Sriastradh acpiec_wait(sc);
9394c1d81b2Sjmcneill
9404c1d81b2Sjmcneill reg = sc->sc_cur_val;
941aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_QUERY, sc, "SCI query: 0x%"PRIx8"\n", reg);
9424c1d81b2Sjmcneill
9434c1d81b2Sjmcneill mutex_exit(&sc->sc_mtx);
944f29130a7Sriastradh acpiec_unlock(sc);
9454c1d81b2Sjmcneill
9464c1d81b2Sjmcneill if (reg == 0)
9474c1d81b2Sjmcneill goto loop; /* Spurious query result */
948b41a7fd4Sthorpej
949b41a7fd4Sthorpej /*
950b41a7fd4Sthorpej * Evaluate _Qxx to respond to the controller.
951b41a7fd4Sthorpej */
9524c1d81b2Sjmcneill snprintf(qxx, sizeof(qxx), "_Q%02X", (unsigned int)reg);
9534c1d81b2Sjmcneill rv = AcpiEvaluateObject(sc->sc_ech, qxx, NULL, NULL);
9544c1d81b2Sjmcneill if (rv != AE_OK && rv != AE_NOT_FOUND) {
95578820b19Sriastradh aprint_error_dev(sc->sc_dev, "GPE query method %s failed: %s",
95621fd508dScegger qxx, AcpiFormatException(rv));
957b41a7fd4Sthorpej }
958b41a7fd4Sthorpej
9594c1d81b2Sjmcneill goto loop;
9604c1d81b2Sjmcneill }
961b41a7fd4Sthorpej
9624c1d81b2Sjmcneill static void
acpiec_gpe_state_machine(struct acpiec_softc * sc)9634dfab621Sriastradh acpiec_gpe_state_machine(struct acpiec_softc *sc)
9644c1d81b2Sjmcneill {
9654c1d81b2Sjmcneill uint8_t reg;
9664c1d81b2Sjmcneill
9676d1cd1baSriastradh KASSERT(mutex_owned(&sc->sc_mtx));
9686d1cd1baSriastradh
9694c1d81b2Sjmcneill reg = acpiec_read_status(sc);
9704c1d81b2Sjmcneill
971aeb0e451Sriastradh #ifdef ACPIEC_DEBUG
972aeb0e451Sriastradh if (acpiec_debug & __BIT(ACPIEC_DEBUG_TRANSITION)) {
973aeb0e451Sriastradh char buf[128];
974aeb0e451Sriastradh
975aeb0e451Sriastradh snprintb(buf, sizeof(buf), EC_STATUS_FMT, reg);
976aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_TRANSITION, sc, "%s\n", buf);
977aeb0e451Sriastradh }
978aeb0e451Sriastradh #endif
979aeb0e451Sriastradh
9804c1d81b2Sjmcneill switch (sc->sc_state) {
9814c1d81b2Sjmcneill case EC_STATE_QUERY:
9824c1d81b2Sjmcneill if ((reg & EC_STATUS_IBF) != 0)
9834c1d81b2Sjmcneill break; /* Nothing of interest here. */
9844c1d81b2Sjmcneill acpiec_write_command(sc, EC_COMMAND_QUERY);
9854c1d81b2Sjmcneill sc->sc_state = EC_STATE_QUERY_VAL;
9864c1d81b2Sjmcneill break;
9874c1d81b2Sjmcneill
9884c1d81b2Sjmcneill case EC_STATE_QUERY_VAL:
9894c1d81b2Sjmcneill if ((reg & EC_STATUS_OBF) == 0)
9904c1d81b2Sjmcneill break; /* Nothing of interest here. */
9914c1d81b2Sjmcneill sc->sc_cur_val = acpiec_read_data(sc);
9924c1d81b2Sjmcneill sc->sc_state = EC_STATE_FREE;
9934c1d81b2Sjmcneill break;
9944c1d81b2Sjmcneill
9954c1d81b2Sjmcneill case EC_STATE_READ:
9964c1d81b2Sjmcneill if ((reg & EC_STATUS_IBF) != 0)
9974c1d81b2Sjmcneill break; /* Nothing of interest here. */
9984c1d81b2Sjmcneill acpiec_write_command(sc, EC_COMMAND_READ);
9994c1d81b2Sjmcneill sc->sc_state = EC_STATE_READ_ADDR;
10004c1d81b2Sjmcneill break;
10014c1d81b2Sjmcneill
10024c1d81b2Sjmcneill case EC_STATE_READ_ADDR:
10034c1d81b2Sjmcneill if ((reg & EC_STATUS_IBF) != 0)
10044c1d81b2Sjmcneill break; /* Nothing of interest here. */
10054c1d81b2Sjmcneill acpiec_write_data(sc, sc->sc_cur_addr);
10064c1d81b2Sjmcneill sc->sc_state = EC_STATE_READ_VAL;
10074c1d81b2Sjmcneill break;
10084c1d81b2Sjmcneill
10094c1d81b2Sjmcneill case EC_STATE_READ_VAL:
10104c1d81b2Sjmcneill if ((reg & EC_STATUS_OBF) == 0)
10114c1d81b2Sjmcneill break; /* Nothing of interest here. */
10124c1d81b2Sjmcneill sc->sc_cur_val = acpiec_read_data(sc);
10134c1d81b2Sjmcneill sc->sc_state = EC_STATE_FREE;
10144c1d81b2Sjmcneill break;
10154c1d81b2Sjmcneill
10164c1d81b2Sjmcneill case EC_STATE_WRITE:
10174c1d81b2Sjmcneill if ((reg & EC_STATUS_IBF) != 0)
10184c1d81b2Sjmcneill break; /* Nothing of interest here. */
10194c1d81b2Sjmcneill acpiec_write_command(sc, EC_COMMAND_WRITE);
10204c1d81b2Sjmcneill sc->sc_state = EC_STATE_WRITE_ADDR;
10214c1d81b2Sjmcneill break;
10224c1d81b2Sjmcneill
10234c1d81b2Sjmcneill case EC_STATE_WRITE_ADDR:
10244c1d81b2Sjmcneill if ((reg & EC_STATUS_IBF) != 0)
10254c1d81b2Sjmcneill break; /* Nothing of interest here. */
10264c1d81b2Sjmcneill acpiec_write_data(sc, sc->sc_cur_addr);
10274c1d81b2Sjmcneill sc->sc_state = EC_STATE_WRITE_VAL;
10284c1d81b2Sjmcneill break;
10294c1d81b2Sjmcneill
10304c1d81b2Sjmcneill case EC_STATE_WRITE_VAL:
10314c1d81b2Sjmcneill if ((reg & EC_STATUS_IBF) != 0)
10324c1d81b2Sjmcneill break; /* Nothing of interest here. */
10334c1d81b2Sjmcneill acpiec_write_data(sc, sc->sc_cur_val);
1034aa3033d4Sriastradh sc->sc_state = EC_STATE_FREE;
10354c1d81b2Sjmcneill break;
10364c1d81b2Sjmcneill
10374c1d81b2Sjmcneill case EC_STATE_FREE:
10384c1d81b2Sjmcneill break;
1039f222fc6eSriastradh
10404c1d81b2Sjmcneill default:
10414c1d81b2Sjmcneill panic("invalid state");
10424c1d81b2Sjmcneill }
104336cdfbe0Sjoerg
1044f222fc6eSriastradh /*
1045aa3033d4Sriastradh * If we are not in a transaction, wake anyone waiting to start
1046aa3033d4Sriastradh * one. If an SCI was requested, notify the SCI thread that it
1047aa3033d4Sriastradh * needs to handle the SCI.
1048da926812Sriastradh */
1049da926812Sriastradh if (sc->sc_state == EC_STATE_FREE) {
1050aa3033d4Sriastradh cv_signal(&sc->sc_cv);
1051da926812Sriastradh if (reg & EC_STATUS_SCI) {
1052da926812Sriastradh DPRINTF(ACPIEC_DEBUG_TRANSITION, sc,
1053da926812Sriastradh "wake SCI thread\n");
1054da926812Sriastradh sc->sc_got_sci = true;
1055da926812Sriastradh cv_signal(&sc->sc_cv_sci);
1056da926812Sriastradh }
1057da926812Sriastradh }
1058da926812Sriastradh
1059da926812Sriastradh /*
1060f222fc6eSriastradh * In case GPE interrupts are broken, poll once per tick for EC
1061f222fc6eSriastradh * status updates while a transaction is still pending.
1062f222fc6eSriastradh */
1063aeb0e451Sriastradh if (sc->sc_state != EC_STATE_FREE) {
1064aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_INTR, sc, "schedule callout\n");
106536cdfbe0Sjoerg callout_schedule(&sc->sc_pseudo_intr, 1);
106636cdfbe0Sjoerg }
106736cdfbe0Sjoerg
1068aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_TRANSITION, sc, "return\n");
1069aeb0e451Sriastradh }
1070aeb0e451Sriastradh
107136cdfbe0Sjoerg static void
acpiec_callout(void * arg)107236cdfbe0Sjoerg acpiec_callout(void *arg)
107336cdfbe0Sjoerg {
1074c5f043f1Sriastradh struct acpiec_softc *sc = arg;
107536cdfbe0Sjoerg
107636cdfbe0Sjoerg mutex_enter(&sc->sc_mtx);
1077aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_INTR, sc, "callout\n");
10784dfab621Sriastradh acpiec_gpe_state_machine(sc);
107936cdfbe0Sjoerg mutex_exit(&sc->sc_mtx);
1080b41a7fd4Sthorpej }
1081b41a7fd4Sthorpej
1082fb53d8ceSjruoho static uint32_t
acpiec_gpe_handler(ACPI_HANDLE hdl,uint32_t gpebit,void * arg)10839a25770dSjruoho acpiec_gpe_handler(ACPI_HANDLE hdl, uint32_t gpebit, void *arg)
1084b41a7fd4Sthorpej {
108509324b0aSriastradh struct acpiec_softc *sc = arg;
1086b41a7fd4Sthorpej
10874c1d81b2Sjmcneill mutex_enter(&sc->sc_mtx);
1088aeb0e451Sriastradh DPRINTF(ACPIEC_DEBUG_INTR, sc, "GPE\n");
10894dfab621Sriastradh acpiec_gpe_state_machine(sc);
10904c1d81b2Sjmcneill mutex_exit(&sc->sc_mtx);
1091a7c9d49cSkochi
1092c9d84242Sjruoho return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
1093b41a7fd4Sthorpej }
1094f5210660Sjmcneill
1095f5210660Sjmcneill ACPI_STATUS
acpiec_bus_read(device_t dv,u_int addr,ACPI_INTEGER * val,int width)1096f5210660Sjmcneill acpiec_bus_read(device_t dv, u_int addr, ACPI_INTEGER *val, int width)
1097f5210660Sjmcneill {
10987951f6eaSriastradh struct acpiec_softc *sc = device_private(dv);
10997951f6eaSriastradh
11007951f6eaSriastradh return acpiec_space_handler(ACPI_READ, addr, width * 8, val, sc, NULL);
1101f5210660Sjmcneill }
1102f5210660Sjmcneill
1103f5210660Sjmcneill ACPI_STATUS
acpiec_bus_write(device_t dv,u_int addr,ACPI_INTEGER val,int width)1104f5210660Sjmcneill acpiec_bus_write(device_t dv, u_int addr, ACPI_INTEGER val, int width)
1105f5210660Sjmcneill {
11067951f6eaSriastradh struct acpiec_softc *sc = device_private(dv);
11077951f6eaSriastradh
11087951f6eaSriastradh return acpiec_space_handler(ACPI_WRITE, addr, width * 8, &val, sc,
1109b30a235bSriastradh NULL);
1110f5210660Sjmcneill }
1111f5210660Sjmcneill
1112f5210660Sjmcneill ACPI_HANDLE
acpiec_get_handle(device_t dv)1113f5210660Sjmcneill acpiec_get_handle(device_t dv)
1114f5210660Sjmcneill {
1115f5210660Sjmcneill struct acpiec_softc *sc = device_private(dv);
1116f5210660Sjmcneill
1117f5210660Sjmcneill return sc->sc_ech;
1118f5210660Sjmcneill }
1119