diff options
author | John Sonnenschein <johns@joyent.com> | 2011-12-15 22:43:18 +0000 |
---|---|---|
committer | John Sonnenschein <johns@joyent.com> | 2011-12-15 22:43:18 +0000 |
commit | c4aa323138d7633d35c7cbf4123aa100970a91f9 (patch) | |
tree | 596fe3d0c3374835bf0e2e1d31e3ea8f3659422a /usr/src/uts/intel | |
parent | 73c531dd5303a633bcaaf609c6eeb2554096b478 (diff) | |
parent | f9b22ee9e48b04a9ef01426e92a13c5b164c0763 (diff) | |
download | illumos-joyent-c4aa323138d7633d35c7cbf4123aa100970a91f9.tar.gz |
Merge branch 'master' into gcc4
Diffstat (limited to 'usr/src/uts/intel')
-rw-r--r-- | usr/src/uts/intel/io/acpica/acpica.c | 159 | ||||
-rw-r--r-- | usr/src/uts/intel/io/acpica/acpica_ec.c | 568 | ||||
-rw-r--r-- | usr/src/uts/intel/io/acpica/osl.c | 2 | ||||
-rw-r--r-- | usr/src/uts/intel/sys/acpi/platform/acsolaris.h | 4 |
4 files changed, 510 insertions, 223 deletions
diff --git a/usr/src/uts/intel/io/acpica/acpica.c b/usr/src/uts/intel/io/acpica/acpica.c index 9c9c73dc03..999002f5c3 100644 --- a/usr/src/uts/intel/io/acpica/acpica.c +++ b/usr/src/uts/intel/io/acpica/acpica.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. */ /* * Copyright (c) 2009, Intel Corporation. @@ -65,7 +66,22 @@ static struct modlinkage modlinkage = { * Local prototypes */ +struct parsed_prw { + ACPI_HANDLE prw_gpeobj; + int prw_gpebit; + int prw_level; +}; + static void acpica_init_kstats(void); +static ACPI_STATUS acpica_init_PRW( + ACPI_HANDLE hdl, + UINT32 lvl, + void *ctxp, + void **rvpp); + +static ACPI_STATUS acpica_parse_PRW( + ACPI_BUFFER *prw_buf, + struct parsed_prw *prw); /* * Local data @@ -166,7 +182,11 @@ _fini(void) } /* - * Install acpica-provided address-space handlers + * Install acpica-provided (default) address-space handlers + * that may be needed before AcpiEnableSubsystem() runs. + * See the comment in AcpiInstallAddressSpaceHandler(). + * Default handlers for remaining address spaces are + * installed later, in AcpiEnableSubsystem. */ static int acpica_install_handlers() @@ -200,6 +220,13 @@ acpica_install_handlers() rv = AE_ERROR; } + if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_DATA_TABLE, + ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) { + cmn_err(CE_WARN, "!acpica: no default handler for" + " Data Table"); + rv = AE_ERROR; + } return (rv); } @@ -421,12 +448,23 @@ acpica_init() /* do after AcpiEnableSubsystem() so GPEs are initialized */ acpica_ec_init(); /* initialize EC if present */ + /* This runs all device _STA and _INI methods. */ if (ACPI_FAILURE(status = AcpiInitializeObjects(0))) goto error; acpica_init_state = ACPICA_INITIALIZED; /* + * [ACPI, sec. 4.4.1.1] + * As of ACPICA version 20101217 (December 2010), the _PRW methods + * (Power Resources for Wake) are no longer automatically executed + * as part of the ACPICA initialization. The OS must do this. + */ + (void) AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + UINT32_MAX, acpica_init_PRW, NULL, NULL, NULL); + (void) AcpiUpdateAllGpes(); + + /* * If we are running on the Xen hypervisor as dom0 we need to * find the ioapics so we can prevent ACPI from trying to * access them. @@ -539,6 +577,47 @@ acpica_get_sci(int *sci_irq, iflag_t *sci_flags) } /* + * Call-back function used for _PRW initialization. For every + * device node that has a _PRW method, evaluate, parse, and do + * AcpiSetupGpeForWake(). + */ +static ACPI_STATUS +acpica_init_PRW( + ACPI_HANDLE devhdl, + UINT32 depth, + void *ctxp, + void **rvpp) +{ + ACPI_STATUS status; + ACPI_BUFFER prw_buf; + struct parsed_prw prw; + + prw_buf.Pointer = NULL; + prw_buf.Length = ACPI_ALLOCATE_BUFFER; + + /* + * Attempt to evaluate _PRW object. + * If no valid object is found, return quietly, since not all + * devices have _PRW objects. + */ + status = AcpiEvaluateObject(devhdl, "_PRW", NULL, &prw_buf); + if (ACPI_FAILURE(status)) + goto done; + status = acpica_parse_PRW(&prw_buf, &prw); + if (ACPI_FAILURE(status)) + goto done; + + (void) AcpiSetupGpeForWake(devhdl, + prw.prw_gpeobj, prw.prw_gpebit); + +done: + if (prw_buf.Pointer != NULL) + AcpiOsFree(prw_buf.Pointer); + + return (AE_OK); +} + +/* * Sets ACPI wake state for device referenced by dip. * If level is S0 (0), disables wake event; otherwise, * enables wake event which will wake system from level. @@ -547,12 +626,12 @@ static int acpica_ddi_setwake(dev_info_t *dip, int level) { ACPI_STATUS status; - ACPI_HANDLE devobj, gpeobj; - ACPI_OBJECT *prw, *gpe; + ACPI_HANDLE devobj; ACPI_BUFFER prw_buf; ACPI_OBJECT_LIST arglist; ACPI_OBJECT args[3]; - int gpebit, pwr_res_count, prw_level, rv; + struct parsed_prw prw; + int rv; /* * initialize these early so we can use a common @@ -627,12 +706,49 @@ acpica_ddi_setwake(dev_info_t *dip, int level) * devices have _PRW objects. */ status = AcpiEvaluateObject(devobj, "_PRW", NULL, &prw_buf); - prw = prw_buf.Pointer; - if (ACPI_FAILURE(status) || prw_buf.Length == 0 || prw == NULL || - prw->Type != ACPI_TYPE_PACKAGE || prw->Package.Count < 2 || - prw->Package.Elements[1].Type != ACPI_TYPE_INTEGER) { + if (ACPI_FAILURE(status)) + goto done; + status = acpica_parse_PRW(&prw_buf, &prw); + if (ACPI_FAILURE(status)) goto done; + + rv = -1; + if (level == 0) { + status = AcpiDisableGpe(prw.prw_gpeobj, prw.prw_gpebit); + if (ACPI_FAILURE(status)) + goto done; + } else if (prw.prw_level >= level) { + status = AcpiSetGpeWakeMask(prw.prw_gpeobj, prw.prw_gpebit, + ACPI_GPE_ENABLE); + if (ACPI_SUCCESS(status)) { + status = AcpiEnableGpe(prw.prw_gpeobj, prw.prw_gpebit); + if (ACPI_FAILURE(status)) + goto done; + } } + rv = 0; +done: + if (prw_buf.Pointer != NULL) + AcpiOsFree(prw_buf.Pointer); + return (rv); +} + +static ACPI_STATUS +acpica_parse_PRW( + ACPI_BUFFER *prw_buf, + struct parsed_prw *p_prw) +{ + ACPI_HANDLE gpeobj; + ACPI_OBJECT *prw, *gpe; + int gpebit, prw_level; + + if (prw_buf->Length == 0 || prw_buf->Pointer == NULL) + return (AE_NULL_OBJECT); + + prw = prw_buf->Pointer; + if (prw->Type != ACPI_TYPE_PACKAGE || prw->Package.Count < 2 || + prw->Package.Elements[1].Type != ACPI_TYPE_INTEGER) + return (AE_TYPE); /* fetch the lowest wake level from the _PRW */ prw_level = prw->Package.Elements[1].Integer.Value; @@ -649,30 +765,21 @@ acpica_ddi_setwake(dev_info_t *dip, int level) gpe = &prw->Package.Elements[0]; if (gpe->Package.Count != 2 || gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER) - goto done; + return (AE_TYPE); gpeobj = gpe->Package.Elements[0].Reference.Handle; gpebit = gpe->Package.Elements[1].Integer.Value; if (gpeobj == NULL) - goto done; + return (AE_NULL_OBJECT); + break; default: - goto done; + return (AE_TYPE); } - rv = -1; - if (level == 0) { - if (ACPI_FAILURE(AcpiDisableGpe(gpeobj, gpebit))) - goto done; - } else if (prw_level >= level) { - if (ACPI_SUCCESS( - AcpiSetGpeWakeMask(gpeobj, gpebit, ACPI_GPE_ENABLE))) - if (ACPI_FAILURE(AcpiEnableGpe(gpeobj, gpebit))) - goto done; - } - rv = 0; -done: - if (prw_buf.Pointer != NULL) - AcpiOsFree(prw_buf.Pointer); - return (rv); + p_prw->prw_gpeobj = gpeobj; + p_prw->prw_gpebit = gpebit; + p_prw->prw_level = prw_level; + + return (AE_OK); } /* diff --git a/usr/src/uts/intel/io/acpica/acpica_ec.c b/usr/src/uts/intel/io/acpica/acpica_ec.c index 2de53b578c..e76c87e273 100644 --- a/usr/src/uts/intel/io/acpica/acpica_ec.c +++ b/usr/src/uts/intel/io/acpica/acpica_ec.c @@ -22,6 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2011 Joyent, Inc. All rights reserved. + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. */ /* * Solaris x86 ACPI CA Embedded Controller operation region handler @@ -42,12 +43,6 @@ #include <sys/acpica.h> /* - * Internal prototypes - */ -static int ec_wait_ibf_clear(int sc_addr); -static int ec_wait_obf_set(int sc_addr); - -/* * EC status bits * Low to high * Output buffer full? @@ -80,11 +75,13 @@ static int ec_wait_obf_set(int sc_addr); /* * EC softstate */ -struct ec_softstate { - uint8_t ec_ok; /* != 0 when EC handler is attached */ +static struct ec_softstate { + uint8_t ec_ok; /* != 0 if we have ec_base, ec_sc */ uint16_t ec_base; /* base of EC I/O port - data */ - uint16_t ec_sc; /* EC status/command */ - ACPI_HANDLE ec_obj; /* handle to ACPI object for EC */ + uint16_t ec_sc; /* EC status/command */ + ACPI_HANDLE ec_dev_hdl; /* EC device handle */ + ACPI_HANDLE ec_gpe_hdl; /* GPE info */ + ACPI_INTEGER ec_gpe_bit; kmutex_t ec_mutex; /* serialize access to EC */ } ec; @@ -101,6 +98,12 @@ typedef struct io_port_des { } io_port_des_t; /* + * Patchable to ignore an ECDT, in case using that + * causes problems on someone's system. + */ +int ec_ignore_ecdt = 0; + +/* * Patchable timeout values for EC input-buffer-full-clear * and output-buffer-full-set. These are in 10uS units and * default to 1 second. @@ -109,14 +112,43 @@ int ibf_clear_timeout = 100000; int obf_set_timeout = 100000; /* - * ACPI CA address space handler interface functions + * ACPI CA EC address space handler support functions */ -/*ARGSUSED*/ -static ACPI_STATUS -ec_setup(ACPI_HANDLE reg, UINT32 func, void *context, void **ret) + +/* + * Busy-wait for IBF to clear + * return < 0 for time out, 0 for no error + */ +static int +ec_wait_ibf_clear(int sc_addr) { + int cnt; - return (AE_OK); + cnt = ibf_clear_timeout; + while (inb(sc_addr) & EC_IBF) { + if (cnt-- <= 0) + return (-1); + drv_usecwait(10); + } + return (0); +} + +/* + * Busy-wait for OBF to set + * return < 0 for time out, 0 for no error + */ +static int +ec_wait_obf_set(int sc_addr) +{ + int cnt; + + cnt = obf_set_timeout; + while (!(inb(sc_addr) & EC_OBF)) { + if (cnt-- <= 0) + return (-1); + drv_usecwait(10); + } + return (0); } /* @@ -172,7 +204,7 @@ ec_rd(int addr) * Only called from ec_handler(), which validates ec_ok */ static int -ec_wr(int addr, uint8_t *val) +ec_wr(int addr, uint8_t val) { int cnt; uint8_t sc; @@ -206,7 +238,7 @@ ec_wr(int addr, uint8_t *val) return (-1); } - outb(ec.ec_base, *val); /* write data */ + outb(ec.ec_base, val); /* write data */ if (ec_wait_ibf_clear(ec.ec_sc) < 0) { cmn_err(CE_NOTE, "!ec_wr:3: timed-out waiting " "for IBF to clear"); @@ -248,12 +280,16 @@ ec_query(void) return (rv); } +/* + * ACPI CA EC address space handler + * Requires: ec.ec_sc, ec.ec_base + */ static ACPI_STATUS ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width, - ACPI_INTEGER *val, void *context, void *regcontext) + UINT64 *val, void *context, void *regcontext) { _NOTE(ARGUNUSED(context, regcontext)) - int tmp; + int i, tw, tmp; /* Guard against unexpected invocation */ if (ec.ec_ok == 0) @@ -263,52 +299,71 @@ ec_handler(UINT32 func, ACPI_PHYSICAL_ADDRESS addr, UINT32 width, * Add safety checks for BIOSes not strictly compliant * with ACPI spec */ - if (((width % 8) != 0) || (width > 8)) { + if ((width % 8) != 0) { cmn_err(CE_NOTE, "!ec_handler: invalid width %d", width); - return (AE_ERROR); + return (AE_BAD_PARAMETER); + } + if (val == NULL) { + cmn_err(CE_NOTE, "!ec_handler: NULL value pointer"); + return (AE_BAD_PARAMETER); } - switch (func) { - case ACPI_READ: - tmp = ec_rd(addr); - if (tmp < 0) - return (AE_ERROR); - *val = tmp; - break; - case ACPI_WRITE: - if (ec_wr(addr, (uint8_t *)val) < 0) - return (AE_ERROR); - break; - default: - return (AE_ERROR); + while (width > 0) { + + /* One UINT64 *val at a time. */ + tw = min(width, 64); + + if (func == ACPI_READ) + *val = 0; + + /* Do I/O of up to 64 bits */ + for (i = 0; i < tw; i += 8, addr++) { + switch (func) { + case ACPI_READ: + tmp = ec_rd(addr); + if (tmp < 0) + return (AE_ERROR); + *val |= ((UINT64)tmp) << i; + break; + case ACPI_WRITE: + tmp = ((*val) >> i) & 0xFF; + if (ec_wr(addr, (uint8_t)tmp) < 0) + return (AE_ERROR); + break; + default: + return (AE_ERROR); + } + } + val++; + width -= tw; } return (AE_OK); } - +/* + * Called via taskq entry enqueued by ec_gpe_handler, + * which validates ec_ok + */ static void ec_gpe_callback(void *ctx) { _NOTE(ARGUNUSED(ctx)) - char query_str[5]; int query; if (!(inb(ec.ec_sc) & EC_SCI)) - return; - - if (ec.ec_ok == 0) { - cmn_err(CE_WARN, "!ec_gpe_callback: EC handler unavailable"); - return; - } + goto out; query = ec_query(); - if (query >= 0) { - (void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query); - (void) AcpiEvaluateObject(ec.ec_obj, query_str, NULL, NULL); - } + if (query < 0) + goto out; + + (void) snprintf(query_str, 5, "_Q%02X", (uint8_t)query); + (void) AcpiEvaluateObject(ec.ec_dev_hdl, query_str, NULL, NULL); +out: + AcpiFinishGpe(ec.ec_gpe_hdl, ec.ec_gpe_bit); } static UINT32 @@ -318,75 +373,129 @@ ec_gpe_handler(ACPI_HANDLE GpeDevice, UINT32 GpeNumber, void *ctx) _NOTE(ARGUNUSED(GpeNumber)) _NOTE(ARGUNUSED(ctx)) + /* + * With ec_ok==0, we will not install a GPE handler, + * so this is just paranoia. But if this were to + * happen somehow, don't add the taskq entry, and + * tell the caller we're done with this GPE call. + */ + if (ec.ec_ok == 0) + return (ACPI_REENABLE_GPE); + AcpiOsExecute(OSL_GPE_HANDLER, ec_gpe_callback, NULL); + + /* + * Returning zero tells the ACPI system that we will + * handle this event asynchronously. + */ return (0); } /* - * Busy-wait for IBF to clear - * return < 0 for time out, 0 for no error + * Some systems describe the EC using an "ECDT" (table). + * If we find one use it (unless ec_ignore_ecdt is set). + * Modern systems don't provide an ECDT. */ -static int -ec_wait_ibf_clear(int sc_addr) +static ACPI_STATUS +ec_probe_ecdt(void) { - int cnt; + ACPI_TABLE_HEADER *th; + ACPI_TABLE_ECDT *ecdt; + ACPI_HANDLE dev_hdl; + ACPI_STATUS status; + + status = AcpiGetTable(ACPI_SIG_ECDT, 1, &th); +#ifndef DEBUG + if (status == AE_NOT_FOUND) + return (status); +#endif + if (ACPI_FAILURE(status)) { + cmn_err(CE_NOTE, "!acpica: ECDT not found"); + return (status); + } + if (ec_ignore_ecdt) { + /* pretend it was not found */ + cmn_err(CE_NOTE, "!acpica: ECDT ignored"); + return (AE_NOT_FOUND); + } - cnt = ibf_clear_timeout; - while (inb(sc_addr) & EC_IBF) { - if (cnt-- <= 0) - return (-1); - drv_usecwait(10); + ecdt = (ACPI_TABLE_ECDT *)th; + if (ecdt->Control.BitWidth != 8 || + ecdt->Data.BitWidth != 8) { + cmn_err(CE_NOTE, "!acpica: bad ECDT I/O width"); + return (AE_BAD_VALUE); } + status = AcpiGetHandle(NULL, (char *)ecdt->Id, &dev_hdl); + if (ACPI_FAILURE(status)) { + cmn_err(CE_NOTE, "!acpica: no ECDT device handle"); + return (status); + } + + /* + * Success. Save info for attach. + */ + ec.ec_base = ecdt->Data.Address; + ec.ec_sc = ecdt->Control.Address; + ec.ec_dev_hdl = dev_hdl; + ec.ec_gpe_hdl = NULL; + ec.ec_gpe_bit = ecdt->Gpe; + ec.ec_ok = 1; + +#ifdef DEBUG + cmn_err(CE_NOTE, "!acpica:ec_probe_ecdt: success"); +#endif return (0); } /* - * Busy-wait for OBF to set - * return < 0 for time out, 0 for no error + * Called from AcpiWalkDevices() when an EC device is found */ -static int -ec_wait_obf_set(int sc_addr) +static ACPI_STATUS +ec_find(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) { - int cnt; + _NOTE(ARGUNUSED(nest, rv)) - cnt = obf_set_timeout; - while (!(inb(sc_addr) & EC_OBF)) { - if (cnt-- <= 0) - return (-1); - drv_usecwait(10); - } - return (0); + *((ACPI_HANDLE *)context) = obj; + return (AE_OK); } - - /* - * Called from AcpiWalkDevices() when an EC device is found + * Normal way to get the details about the EC, + * by searching the name space. */ static ACPI_STATUS -acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) +ec_probe_ns(void) { - _NOTE(ARGUNUSED(nest, context, rv)) - - int status, i; + ACPI_HANDLE dev_hdl; ACPI_BUFFER buf, crs; - ACPI_INTEGER gpe; - int io_port_cnt; - - /* - * Save the one EC object we have - */ - ec.ec_obj = obj; + ACPI_OBJECT *gpe_obj; + ACPI_HANDLE gpe_hdl; + ACPI_INTEGER gpe_bit; + ACPI_STATUS status; + int i, io_port_cnt; + uint16_t ec_sc, ec_base; + + dev_hdl = NULL; + (void) AcpiGetDevices("PNP0C09", &ec_find, (void *)&dev_hdl, NULL); + if (dev_hdl == NULL) { +#ifdef DEBUG + /* Not an error, just no EC on this machine. */ + cmn_err(CE_WARN, "!acpica:ec_probe_ns: " + "PNP0C09 not found"); +#endif + return (AE_NOT_FOUND); + } /* * Find ec_base and ec_sc addresses */ crs.Length = ACPI_ALLOCATE_BUFFER; - if (ACPI_FAILURE(AcpiEvaluateObjectTyped(obj, "_CRS", NULL, &crs, - ACPI_TYPE_BUFFER))) { - cmn_err(CE_WARN, "!acpica_install_ec: _CRS object evaluate" - "failed"); - return (AE_OK); + status = AcpiEvaluateObjectTyped(dev_hdl, "_CRS", NULL, &crs, + ACPI_TYPE_BUFFER); + if (ACPI_FAILURE(status)) { + cmn_err(CE_WARN, "!acpica:ec_probe_ns: " + "_CRS object evaluate failed"); + return (status); } for (i = 0, io_port_cnt = 0; @@ -399,13 +508,13 @@ acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) continue; io_port = (io_port_des_t *)tmp; /* - * Assuming first port is ec_base and second is ec_sc + * first port is ec_base and second is ec_sc */ - if (io_port_cnt) - ec.ec_sc = (io_port->min_base_hi << 8) | + if (io_port_cnt == 0) + ec_base = (io_port->min_base_hi << 8) | io_port->min_base_lo; - else - ec.ec_base = (io_port->min_base_hi << 8) | + if (io_port_cnt == 1) + ec_sc = (io_port->min_base_hi << 8) | io_port->min_base_lo; io_port_cnt++; @@ -415,152 +524,219 @@ acpica_install_ec(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) i += 7; } AcpiOsFree(crs.Pointer); - - /* - * Drain the EC data register if something is left over from - * legacy mode - */ - if (inb(ec.ec_sc) & EC_OBF) { -#ifndef DEBUG - inb(ec.ec_base); /* read and discard value */ -#else - cmn_err(CE_NOTE, "!EC had something: 0x%x\n", inb(ec.ec_base)); -#endif + if (io_port_cnt < 2) { + cmn_err(CE_WARN, "!acpica:ec_probe_ns: " + "_CRS parse failed"); + return (AE_BAD_VALUE); } /* - * Get GPE + * Get the GPE info. */ buf.Length = ACPI_ALLOCATE_BUFFER; - /* - * grab contents of GPE object - */ - if (ACPI_FAILURE(AcpiEvaluateObjectTyped(obj, "_GPE", NULL, &buf, - ACPI_TYPE_INTEGER))) { - /* emit a warning but ignore the error */ - cmn_err(CE_WARN, "!acpica_install_ec: _GPE object evaluate" - "failed"); - gpe = -1; - } else { - gpe = ((ACPI_OBJECT *)buf.Pointer)->Integer.Value; - AcpiOsFree(buf.Pointer); + status = AcpiEvaluateObject(dev_hdl, "_GPE", NULL, &buf); + if (ACPI_FAILURE(status)) { + cmn_err(CE_WARN, "!acpica:ec_probe_ns: " + "_GPE object evaluate"); + return (status); } - + gpe_obj = (ACPI_OBJECT *)buf.Pointer; /* - * Initialize EC mutex here + * process the GPE description */ - mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL); + switch (gpe_obj->Type) { + case ACPI_TYPE_INTEGER: + gpe_hdl = NULL; + gpe_bit = gpe_obj->Integer.Value; + break; + case ACPI_TYPE_PACKAGE: + if (gpe_obj->Package.Count != 2) + goto bad_gpe; + gpe_obj = gpe_obj->Package.Elements; + if (gpe_obj[1].Type != ACPI_TYPE_INTEGER) + goto bad_gpe; + gpe_hdl = gpe_obj[0].Reference.Handle; + gpe_bit = gpe_obj[1].Integer.Value; + break; + bad_gpe: + default: + status = AE_BAD_VALUE; + break; + } + AcpiOsFree(buf.Pointer); + if (ACPI_FAILURE(status)) { + cmn_err(CE_WARN, "!acpica:ec_probe_ns: " + "_GPE parse failed"); + return (status); + } /* - * Assume the EC handler is available now; the next attempt - * to install the address space handler may actually need - * to use the EC handler to install it (chicken/egg) + * Success. Save info for attach. */ + ec.ec_base = ec_base; + ec.ec_sc = ec_sc; + ec.ec_dev_hdl = dev_hdl; + ec.ec_gpe_hdl = gpe_hdl; + ec.ec_gpe_bit = gpe_bit; ec.ec_ok = 1; - if (AcpiInstallAddressSpaceHandler(obj, - ACPI_ADR_SPACE_EC, &ec_handler, &ec_setup, NULL) != AE_OK) { - cmn_err(CE_WARN, "!acpica: failed to add EC handler\n"); - ec.ec_ok = 0; /* EC handler failed to install */ - membar_producer(); /* force ec_ok store before mutex_destroy */ - mutex_destroy(&ec.ec_mutex); - return (AE_ERROR); - } +#ifdef DEBUG + cmn_err(CE_NOTE, "!acpica:ec_probe_ns: success"); +#endif + return (0); +} + +/* + * Setup the Embedded Controller (EC) address space handler. + * Entered only if one of the EC probe methods found an EC. + */ +static void +ec_init(void) +{ + ACPI_STATUS rc; + int x; + + /* paranoia */ + if (ec.ec_ok == 0) + return; /* - * In case the EC handler was successfully installed but no - * GPE was found, return sucess; a warning was emitted above + * Drain the EC data register if something is left over from + * legacy mode */ - if (gpe < 0) - return (AE_OK); + if (inb(ec.ec_sc) & EC_OBF) { + x = inb(ec.ec_base); /* read and discard value */ +#ifdef DEBUG + cmn_err(CE_NOTE, "!EC had something: 0x%x", x); +#endif + } /* - * Have a valid EC GPE; enable it + * Install an "EC address space" handler. + * + * This call does a name space walk under the passed + * object looking for child objects with an EC space + * region for which to install this handler. Using + * the ROOT object makes sure we find them all. + * + * XXX: Some systems return an error from this call + * after a partial success, i.e. where the NS walk + * installs on some nodes and fails on other nodes. + * In such cases, disabling the EC and GPE handlers + * makes things worse, so just report the error and + * leave the EC handler enabled. + * + * At one point, it seemed that doing this part of + * EC setup earlier may help, which is why this is + * now a separate function from ec_attach. Someone + * needs to figure our why some systems give us an + * error return from this call. (TODO) */ - if ((status = AcpiInstallGpeHandler(NULL, gpe, ACPI_GPE_EDGE_TRIGGERED, - ec_gpe_handler, NULL)) != AE_OK) { - cmn_err(CE_WARN, "!acpica: failed to install gpe handler status" - " = %d", status); - /* - * don't return an error here - GPE won't work but the EC - * handler may be OK - */ + rc = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_EC, &ec_handler, NULL, NULL); + if (rc != AE_OK) { + cmn_err(CE_WARN, "!acpica:ec_init: " + "install AS handler, rc=0x%x", rc); + return; } - - (void) AcpiEnableGpe(NULL, gpe); - - return (AE_OK); +#ifdef DEBUG + cmn_err(CE_NOTE, "!acpica:ec_init: success"); +#endif } -#ifdef DEBUG -/*ARGSUSED*/ -static ACPI_STATUS -acpica_install_smbus_v1(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) +/* + * Attach the EC General-Purpose Event (GPE) handler. + */ +static void +ec_attach(void) { + ACPI_STATUS rc; - cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0\n"); - return (AE_OK); -} + /* + * Guard against call without probe results. + */ + if (ec.ec_ok == 0) { + cmn_err(CE_WARN, "!acpica:ec_attach: " + "no EC device found"); + return; + } -/*ARGSUSED*/ -static ACPI_STATUS -acpica_install_smbus_v2(ACPI_HANDLE obj, UINT32 nest, void *context, void **rv) -{ + /* + * Install the GPE handler and enable it. + */ + rc = AcpiInstallGpeHandler(ec.ec_gpe_hdl, ec.ec_gpe_bit, + ACPI_GPE_EDGE_TRIGGERED, ec_gpe_handler, NULL); + if (rc != AE_OK) { + cmn_err(CE_WARN, "!acpica:ec_attach: " + "install GPE handler, rc=0x%x", rc); + goto errout; + } - cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0\n"); - return (AE_OK); -} -#endif /* DEBUG */ + rc = AcpiEnableGpe(ec.ec_gpe_hdl, ec.ec_gpe_bit); + if (rc != AE_OK) { + cmn_err(CE_WARN, "!acpica:ec_attach: " + "enable GPE handler, rc=0x%x", rc); + goto errout; + } -#ifdef NOTYET -static void -prgas(ACPI_GENERIC_ADDRESS *gas) -{ - cmn_err(CE_CONT, "gas: %d %d %d %d %lx", - gas->AddressSpaceId, gas->RegisterBitWidth, gas->RegisterBitOffset, - gas->AccessWidth, (long)gas->Address); +#ifdef DEBUG + cmn_err(CE_NOTE, "!acpica:ec_attach: success"); +#endif + return; + +errout: + AcpiRemoveGpeHandler(ec.ec_gpe_hdl, ec.ec_gpe_bit, + ec_gpe_handler); } +/* + * System Management Bus Controller (SMBC) + * These also go through the EC. + * (not yet supported) + */ static void -acpica_probe_ecdt() +smbus_attach(void) { - EC_BOOT_RESOURCES *ecdt; - +#ifdef DEBUG + ACPI_HANDLE obj; - if (AcpiGetTable("ECDT", 1, (ACPI_TABLE_HEADER **) &ecdt) != AE_OK) { - cmn_err(CE_NOTE, "!acpica: ECDT not found\n"); - return; + obj = NULL; + (void) AcpiGetDevices("ACPI0001", &ec_find, (void *)&obj, NULL); + if (obj != NULL) { + cmn_err(CE_NOTE, "!acpica: found an SMBC Version 1.0"); } - cmn_err(CE_NOTE, "EcControl: "); - prgas(&ecdt->EcControl); - - cmn_err(CE_NOTE, "EcData: "); - prgas(&ecdt->EcData); + obj = NULL; + (void) AcpiGetDevices("ACPI0005", &ec_find, (void *)&obj, NULL); + if (obj != NULL) { + cmn_err(CE_NOTE, "!acpica: found an SMBC Version 2.0"); + } +#endif /* DEBUG */ } -#endif /* NOTYET */ +/* + * Initialize the EC, if present. + */ void acpica_ec_init(void) { -#ifdef NOTYET + ACPI_STATUS rc; + /* - * Search the ACPI tables for an ECDT; if - * found, use it to install an EC handler + * Initialize EC mutex here */ - acpica_probe_ecdt(); -#endif /* NOTYET */ - - /* EC handler defaults to not available */ - ec.ec_ok = 0; + mutex_init(&ec.ec_mutex, NULL, MUTEX_DRIVER, NULL); /* - * General model is: use GetDevices callback to install - * handler(s) when device is present. + * First search the ACPI tables for an ECDT, and + * if not found, search the name space for it. */ - (void) AcpiGetDevices("PNP0C09", &acpica_install_ec, NULL, NULL); -#ifdef DEBUG - (void) AcpiGetDevices("ACPI0001", &acpica_install_smbus_v1, NULL, NULL); - (void) AcpiGetDevices("ACPI0005", &acpica_install_smbus_v2, NULL, NULL); -#endif /* DEBUG */ + rc = ec_probe_ecdt(); + if (ACPI_FAILURE(rc)) + rc = ec_probe_ns(); + if (ACPI_SUCCESS(rc)) { + ec_init(); + ec_attach(); + } + smbus_attach(); } diff --git a/usr/src/uts/intel/io/acpica/osl.c b/usr/src/uts/intel/io/acpica/osl.c index 783db12211..d5bfab754f 100644 --- a/usr/src/uts/intel/io/acpica/osl.c +++ b/usr/src/uts/intel/io/acpica/osl.c @@ -713,7 +713,7 @@ AcpiOsGetThreadId(void) * ACPI CA assumes that thread ID is castable to a pointer, * so we use the current thread pointer. */ - return (ACPI_CAST_PTHREAD_T ((uintptr_t)curthread)); + return (ACPI_CAST_PTHREAD_T((uintptr_t)curthread)); } /* diff --git a/usr/src/uts/intel/sys/acpi/platform/acsolaris.h b/usr/src/uts/intel/sys/acpi/platform/acsolaris.h index d48d1a8fed..b0cd8bfcf3 100644 --- a/usr/src/uts/intel/sys/acpi/platform/acsolaris.h +++ b/usr/src/uts/intel/sys/acpi/platform/acsolaris.h @@ -19,6 +19,7 @@ * CDDL HEADER END */ /* + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,6 +37,9 @@ extern "C" { #include <sys/cpu.h> #include <sys/thread.h> +/* Function name used for debug output. */ +#define ACPI_GET_FUNCTION_NAME __func__ + uint32_t __acpi_acquire_global_lock(void *); uint32_t __acpi_release_global_lock(void *); void __acpi_wbinvd(void); |