summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/acpica/acpi.c6
-rw-r--r--sys/dev/acpica/acpi_ec.c202
-rw-r--r--sys/dev/acpica/acpivar.h36
3 files changed, 172 insertions, 72 deletions
diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index 2b890d9..ca361de 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -383,6 +383,12 @@ acpi_attach(device_t dev)
goto out;
}
+ /*
+ * Call the ECDT probe function to provide EC functionality before
+ * the namespace has been evaluated.
+ */
+ acpi_ec_ecdt_probe(dev);
+
if (ACPI_FAILURE(status = AcpiInitializeObjects(flags))) {
device_printf(dev, "could not initialize ACPI objects: %s\n", AcpiFormatException(status));
goto out;
diff --git a/sys/dev/acpica/acpi_ec.c b/sys/dev/acpica/acpi_ec.c
index 833a639..c4a6617 100644
--- a/sys/dev/acpica/acpi_ec.c
+++ b/sys/dev/acpica/acpi_ec.c
@@ -221,6 +221,26 @@ typedef UINT8 EC_EVENT;
#define EC_SET_CSR(sc, v) \
bus_space_write_1((sc)->ec_csr_tag, (sc)->ec_csr_handle, 0, (v))
+/* Embedded Controller Boot Resources Table (ECDT) */
+typedef struct {
+ ACPI_TABLE_HEADER header;
+ ACPI_GENERIC_ADDRESS control;
+ ACPI_GENERIC_ADDRESS data;
+ UINT32 uid;
+ UINT8 gpe_bit;
+ char ec_id[0];
+} ACPI_TABLE_ECDT;
+
+/* Indicate that this device has already been probed via ECDT. */
+#define DEV_ECDT_FLAG 0x80000000
+
+/* Indicate that this device should use the global lock. */
+#define DEV_GLK_FLAG 0x40000000
+
+/* Get/set GPE bit value in the magic ivar. */
+#define DEV_GET_GPEBIT(x) ((x) & 0xff)
+#define DEV_SET_GPEBIT(x, y) ((x) = ((x) & ~0xff) | ((y) & 0xff))
+
/*
* Driver softc.
*/
@@ -307,13 +327,11 @@ static ACPI_STATUS EcRead(struct acpi_ec_softc *sc, UINT8 Address,
UINT8 *Data);
static ACPI_STATUS EcWrite(struct acpi_ec_softc *sc, UINT8 Address,
UINT8 *Data);
-static void acpi_ec_identify(driver_t driver, device_t bus);
static int acpi_ec_probe(device_t dev);
static int acpi_ec_attach(device_t dev);
static device_method_t acpi_ec_methods[] = {
/* Device interface */
- DEVMETHOD(device_identify, acpi_ec_identify),
DEVMETHOD(device_probe, acpi_ec_probe),
DEVMETHOD(device_attach, acpi_ec_attach),
@@ -330,37 +348,155 @@ static devclass_t acpi_ec_devclass;
DRIVER_MODULE(acpi_ec, acpi, acpi_ec_driver, acpi_ec_devclass, 0, 0);
/*
- * Look for an ECDT table and if we find one, set up a default EC
- * space handler to catch possible attempts to access EC space before
+ * Look for an ECDT and if we find one, set up default GPE and
+ * space handlers to catch attempts to access EC space before
* we have a real driver instance in place.
- * We're not really an identify routine, but because we get called
- * before most other things, this works out OK.
+ * TODO: if people report invalid ECDTs, add a tunable to disable them.
*/
-static void
-acpi_ec_identify(driver_t driver, device_t bus)
+void
+acpi_ec_ecdt_probe(device_t parent)
{
+ ACPI_TABLE_ECDT *ecdt;
+ ACPI_STATUS status;
+ device_t child;
+ ACPI_HANDLE h;
+ int magic;
+
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
- /* XXX implement - need an ACPI 2.0 system to test this */
+ /* Find and validate the ECDT. */
+ status = AcpiGetFirmwareTable("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
+ (ACPI_TABLE_HEADER **)&ecdt);
+ if (ACPI_FAILURE(status) ||
+ ecdt->control.RegisterBitWidth != 8 ||
+ ecdt->data.RegisterBitWidth != 8) {
+ return;
+ }
+
+ /* Create the child device with the given unit number. */
+ child = BUS_ADD_CHILD(parent, 0, "acpi_ec", ecdt->uid);
+ if (child == NULL) {
+ printf("acpi_ec_ecdt_probe: can't add child\n");
+ return;
+ }
+
+ /* Find and save the ACPI handle for this device. */
+ status = AcpiGetHandle(NULL, ecdt->ec_id, &h);
+ if (ACPI_FAILURE(status)) {
+ device_delete_child(parent, child);
+ printf("acpi_ec_ecdt_probe: can't get handle\n");
+ return;
+ }
+ acpi_set_handle(child, h);
+
+ /* Set the data and CSR register addresses. */
+ bus_set_resource(child, SYS_RES_IOPORT, 0, ecdt->data.Address,
+ /*count*/1);
+ bus_set_resource(child, SYS_RES_IOPORT, 1, ecdt->control.Address,
+ /*count*/1);
+
+ /*
+ * Store values for the probe/attach routines to use. Store the
+ * ECDT GPE bit and set the global lock flag (just to be safe).
+ * We'll determine whether we really want to use the global lock
+ * in a later call to attach.
+ */
+ magic = DEV_ECDT_FLAG | DEV_GLK_FLAG;
+ DEV_SET_GPEBIT(magic, ecdt->gpe_bit);
+ acpi_set_magic(child, magic);
+
+ /* Finish the attach process. */
+ if (device_probe_and_attach(child) != 0)
+ device_delete_child(parent, child);
}
-/*
- * We could setup resources in the probe routine in order to have them printed
- * when the device is attached.
- */
static int
acpi_ec_probe(device_t dev)
{
- int ret = ENXIO;
+ ACPI_HANDLE h;
+ ACPI_STATUS status;
+ device_t peer;
+ char desc[64];
+ int magic, uid, glk, gpebit, ret = ENXIO;
+
+ /* Check that this is an EC device and it's not disabled. */
+ if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("ec") ||
+ !acpi_MatchHid(dev, "PNP0C09")) {
+ return (ENXIO);
+ }
- if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && !acpi_disabled("ec") &&
- acpi_MatchHid(dev, "PNP0C09")) {
+ /*
+ * If probed via ECDT, set description and continue. Otherwise,
+ * we can access the namespace and make sure this is not a
+ * duplicate probe.
+ */
+ magic = acpi_get_magic(dev);
+ if ((magic & DEV_ECDT_FLAG) != 0) {
+ snprintf(desc, sizeof(desc), "embedded controller: ECDT, GPE %#x, GLK",
+ DEV_GET_GPEBIT(magic));
+ device_set_desc_copy(dev, desc);
+ ret = 0;
+ } else {
+ h = acpi_get_handle(dev);
/*
- * Set device description
+ * Read the unit ID to check for duplicate attach and the
+ * global lock value to see if we should acquire it when
+ * accessing the EC.
*/
- device_set_desc(dev, "embedded controller");
- ret = 0;
+ status = acpi_EvaluateInteger(h, "_UID", &uid);
+ if (ACPI_FAILURE(status))
+ uid = 0;
+ status = acpi_EvaluateInteger(h, "_GLK", &glk);
+ if (ACPI_FAILURE(status))
+ glk = 0;
+
+ /*
+ * Evaluate the _GPE method to find the GPE bit used by the EC to
+ * signal status (SCI). Note that we don't handle the case where
+ * it can return a package instead of an int.
+ */
+ status = acpi_EvaluateInteger(h, "_GPE", &gpebit);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "can't evaluate _GPE - %s\n",
+ AcpiFormatException(status));
+ return (ENXIO);
+ }
+
+ /* Store the values we got from the namespace for attach. */
+ magic = glk != 0 ? DEV_GLK_FLAG : 0;
+ DEV_SET_GPEBIT(magic, gpebit);
+ acpi_set_magic(dev, magic);
+
+ /*
+ * Check for a duplicate probe. This can happen when a probe
+ * via ECDT succeeded already. If there is a duplicate, override
+ * its value for GLK in the peer's softc since the ECDT case
+ * always enables the global lock to be safe. Otherwise, just
+ * continue on to attach.
+ */
+ peer = devclass_get_device(acpi_ec_devclass, uid);
+ if (peer == NULL || !device_is_alive(peer)) {
+ snprintf(desc, sizeof(desc), "embedded controller: GPE %#x%s",
+ gpebit, glk != 0 ? ", GLK" : "");
+ device_set_desc_copy(dev, desc);
+ ret = 0;
+ } else {
+ struct acpi_ec_softc *sc;
+
+ /*
+ * Set the peer's sc->ec_glk with locks held so we won't
+ * override it between another thread's lock/unlock calls.
+ */
+ sc = device_get_softc(peer);
+ if (sc->ec_glk != glk) {
+ ACPI_VPRINT(peer, acpi_device_get_parent_softc(peer),
+ "Changing GLK from %d to %d\n", sc->ec_glk, glk);
+ mtx_lock(&sc->ec_mtx);
+ sc->ec_glk = glk != 0 ? 1 : 0;
+ mtx_unlock(&sc->ec_mtx);
+ }
+ }
}
return (ret);
@@ -371,7 +507,7 @@ acpi_ec_attach(device_t dev)
{
struct acpi_ec_softc *sc;
ACPI_STATUS Status;
- int errval = 0;
+ int magic, errval = 0;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
@@ -382,6 +518,11 @@ acpi_ec_attach(device_t dev)
sc->ec_polldelay = EC_POLL_DELAY;
mtx_init(&sc->ec_mtx, "ACPI embedded controller", NULL, MTX_DEF);
+ /* Retrieve previously probed values via device ivars. */
+ magic = acpi_get_magic(dev);
+ sc->ec_glk = (magic & DEV_GLK_FLAG) != 0 ? 1 : 0;
+ sc->ec_gpebit = DEV_GET_GPEBIT(magic);
+
/* Attach bus resources for data and command/status ports. */
sc->ec_data_rid = 0;
sc->ec_data_res = bus_alloc_resource(sc->ec_dev, SYS_RES_IOPORT,
@@ -405,23 +546,6 @@ acpi_ec_attach(device_t dev)
sc->ec_csr_tag = rman_get_bustag(sc->ec_csr_res);
sc->ec_csr_handle = rman_get_bushandle(sc->ec_csr_res);
- /* Check if global lock should be used. If not, leave flag as 0. */
- acpi_EvaluateInteger(sc->ec_handle, "_GLK", &sc->ec_glk);
-
- /*
- * Evaluate the _GPE method to find the GPE bit used by the EC to signal
- * status (SCI). Note that we don't handle the case where it can
- * return a package instead of an int.
- */
- ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "querying GPE\n"));
- Status = acpi_EvaluateInteger(sc->ec_handle, "_GPE", &sc->ec_gpebit);
- if (ACPI_FAILURE(Status)) {
- device_printf(dev, "can't evaluate _GPE - %s\n",
- AcpiFormatException(Status));
- errval = ENXIO;
- goto out;
- }
-
/*
* Install a handler for this EC's GPE bit. We want edge-triggered
* behavior.
@@ -452,10 +576,6 @@ acpi_ec_attach(device_t dev)
goto out;
}
- ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev),
- "GPE bit is %#x, %susing global lock\n", sc->ec_gpebit,
- sc->ec_glk == 0 ? "not " : "");
-
ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "acpi_ec_attach complete\n"));
return (0);
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index e2e5911..42ab459 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -133,37 +133,6 @@ struct acpi_device {
#define ACPI_INTR_APIC 1
#define ACPI_INTR_SAPIC 2
-/* XXX this is no longer referenced anywhere, remove? */
-#if 0
-/*
- * This is a cheap and nasty way to get around the horrid counted list
- * argument format that AcpiEvalateObject uses.
- */
-#define ACPI_OBJECTLIST_MAX 16
-struct acpi_object_list {
- UINT32 count;
- ACPI_OBJECT *pointer[ACPI_OBJECTLIST_MAX];
- ACPI_OBJECT object[ACPI_OBJECTLIST_MAX];
-};
-
-static __inline struct acpi_object_list *
-acpi_AllocObjectList(int nobj)
-{
- struct acpi_object_list *l;
- int i;
-
- if (nobj > ACPI_OBJECTLIST_MAX)
- return(NULL);
- if ((l = AcpiOsAllocate(sizeof(*l))) == NULL)
- return(NULL);
- bzero(l, sizeof(*l));
- for (i = 0; i < ACPI_OBJECTLIST_MAX; i++)
- l->pointer[i] = &l->object[i];
- l->count = nobj;
- return(l);
-}
-#endif /* unused */
-
/*
* Note that the low ivar values are reserved to provide
* interface compatibility with ISA drivers which can also
@@ -376,6 +345,11 @@ extern int acpi_battery_get_battdesc(int, struct acpi_battdesc *);
extern int acpi_cmbat_get_battinfo(int, struct acpi_battinfo *);
/*
+ * Embedded controller.
+ */
+extern void acpi_ec_ecdt_probe(device_t);
+
+/*
* AC adapter interface.
*/
OpenPOWER on IntegriCloud