[Haiku-commits] r31102 - in haiku/trunk/src/add-ons/kernel/drivers/power: . acpi_embedded_controller
czeidler at mail.berlios.de
czeidler at mail.berlios.de
Thu Jun 18 20:30:13 CEST 2009
Author: czeidler
Date: 2009-06-18 20:30:06 +0200 (Thu, 18 Jun 2009)
New Revision: 31102
ViewCVS: http://svn.berlios.de/viewcvs/haiku?rev=31102&view=rev
Added:
haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/
haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/Jamfile
haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.cpp
haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.h
haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/acpi_embedded_controller.cpp
haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/acpi_embedded_controller.h
Modified:
haiku/trunk/src/add-ons/kernel/drivers/power/Jamfile
Log:
Embedded Controller driver, used the BSD driver as base. Thanks to Ithamar for pointing out that I need this driver to read the acpi battery status which now works for me (driver coming soon).
My laptop now really switch off on shutdown :-)
Modified: haiku/trunk/src/add-ons/kernel/drivers/power/Jamfile
===================================================================
--- haiku/trunk/src/add-ons/kernel/drivers/power/Jamfile 2009-06-18 18:18:40 UTC (rev 31101)
+++ haiku/trunk/src/add-ons/kernel/drivers/power/Jamfile 2009-06-18 18:30:06 UTC (rev 31102)
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel drivers power ;
SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_button ;
+SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_embedded_controller ;
SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_lid ;
SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_thermal ;
SubInclude HAIKU_TOP src add-ons kernel drivers power enhanced_speedstep ;
Added: haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/Jamfile
===================================================================
--- haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/Jamfile 2009-06-18 18:18:40 UTC (rev 31101)
+++ haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/Jamfile 2009-06-18 18:30:06 UTC (rev 31102)
@@ -0,0 +1,11 @@
+SubDir HAIKU_TOP src add-ons kernel drivers power acpi_embedded_controller ;
+
+
+UsePrivateHeaders kernel ;
+
+KernelAddon acpi_embedded_controller :
+ acpi_embedded_controller.cpp
+ SmallResourceData.cpp
+ ;
+
+Depends acpi_embedded_controller : acpi ;
Added: haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.cpp
===================================================================
--- haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.cpp 2009-06-18 18:18:40 UTC (rev 31101)
+++ haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.cpp 2009-06-18 18:30:06 UTC (rev 31102)
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2009, Haiku, Inc. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Clemens Zeidler, haiku at clemens-zeidler.de
+ */
+
+#include "SmallResourceData.h"
+
+#include <stdlib.h>
+
+//#define TRACE_SMALLRESOURCEDATA
+#ifdef TRACE_SMALLRESOURCEDATA
+# define TRACE(x...) dprintf("Small Resource Data: "x)
+#else
+# define TRACE(x...)
+#endif
+
+
+void
+io_port::Print()
+{
+ dprintf("io_port:\n");
+ int i = (deviceAddresses16Bit ? 1 : 0);
+ dprintf("deviceAddresses16Bit %i\n", i);
+ dprintf("minimumBase %i\n", minimumBase);
+ dprintf("maximumBase %i\n", maximumBase);
+ dprintf("minimumBaseAlignment %i\n", minimumBaseAlignment);
+ dprintf("contigiuousIOPorts %i\n", contigiuousIOPorts);
+}
+
+
+SmallResourceData::SmallResourceData(acpi_device_module_info* acpi,
+ acpi_device acpiCookie, char* method)
+{
+ acpi_data buffer;
+ buffer.pointer = NULL;
+ buffer.length = ACPI_ALLOCATE_BUFFER;
+
+ fStatus = acpi->evaluate_method(acpiCookie, method, NULL, &buffer);
+
+ if (fStatus != B_OK)
+ return;
+
+ fBuffer = (acpi_object_type*)buffer.pointer;
+
+ if (fBuffer[0].object_type != ACPI_TYPE_BUFFER) {
+ fStatus = B_ERROR;
+ return;
+ }
+
+ fResourcePointer = (int8*)fBuffer[0].data.buffer.buffer;
+ fBufferSize = fBuffer[0].data.buffer.length;
+ fRemainingBufferSize = fBufferSize;
+
+ // ToDo: Check checksum of the endtag. The sum of all databytes + checksum
+ // is zero. See section 6.4.2.8.
+}
+
+
+SmallResourceData::~SmallResourceData()
+{
+ if (InitCheck() == B_OK)
+ free(fBuffer);
+}
+
+
+status_t
+SmallResourceData::InitCheck()
+{
+ return fStatus;
+}
+
+
+int8
+SmallResourceData::GetType()
+{
+ return *fResourcePointer;
+}
+
+
+status_t
+SmallResourceData::ReadIOPort(io_port* ioPort)
+{
+ const size_t packageSize = 8;
+
+ if (fRemainingBufferSize < packageSize)
+ return B_ERROR;
+ if (fResourcePointer[0] != kIOPort)
+ return B_ERROR;
+
+ ioPort->deviceAddresses16Bit = (fResourcePointer[1] == 1);
+
+ int16 tmp;
+ tmp = fResourcePointer[3];
+ tmp = tmp << 8;
+ tmp |= fResourcePointer[2];
+ ioPort->minimumBase = tmp;
+
+ tmp = fResourcePointer[5];
+ tmp = tmp << 8;
+ tmp |= fResourcePointer[4];
+ ioPort->maximumBase = tmp;
+
+ ioPort->minimumBaseAlignment = fResourcePointer[6];
+ ioPort->contigiuousIOPorts = fResourcePointer[7];
+
+ fResourcePointer += packageSize;
+ fRemainingBufferSize -= packageSize;
+ TRACE("SmallResourceData: remaining buffer size %i\n",
+ int(fRemainingBufferSize));
+ return B_OK;
+}
Added: haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.h
===================================================================
--- haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.h 2009-06-18 18:18:40 UTC (rev 31101)
+++ haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/SmallResourceData.h 2009-06-18 18:30:06 UTC (rev 31102)
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2009, Haiku, Inc. All Rights Reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Clemens Zeidler, haiku at clemens-zeidler.de
+ */
+
+#ifndef SMALLRESOURCEDATA_H
+#define SMALLRESOURCEDATA_H
+
+#include <ACPI.h>
+#include <Errors.h>
+
+
+enum resource_type
+{
+ kIOPort = 0x47,
+ kEndTag = 0x78
+};
+
+
+struct io_port
+{
+ void Print();
+ //! The logical device decodes 16-bit addresses.
+ bool deviceAddresses16Bit;
+
+ uint16 minimumBase;
+ uint16 maximumBase;
+ uint8 minimumBaseAlignment;
+ uint8 contigiuousIOPorts;
+};
+
+
+/*! ToDo: implement also the other resource data, see acpi section 6.2.4 */
+class SmallResourceData
+{
+public:
+ SmallResourceData(acpi_device_module_info* acpi,
+ acpi_device acpiCookie, char* method);
+ ~SmallResourceData();
+
+ status_t InitCheck();
+
+ int8 GetType();
+ /*! Get resource data and jump to the next resource. */
+ status_t ReadIOPort(io_port* ioPort);
+
+private:
+ acpi_object_type* fBuffer;
+ size_t fBufferSize;
+ size_t fRemainingBufferSize;
+
+ int8* fResourcePointer;
+
+ status_t fStatus;
+};
+
+
+#endif
Added: haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/acpi_embedded_controller.cpp
===================================================================
--- haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/acpi_embedded_controller.cpp 2009-06-18 18:18:40 UTC (rev 31101)
+++ haiku/trunk/src/add-ons/kernel/drivers/power/acpi_embedded_controller/acpi_embedded_controller.cpp 2009-06-18 18:30:06 UTC (rev 31102)
@@ -0,0 +1,937 @@
+/*-
+ * Copyright (c) 2009 Clemens Zeidler
+ * Copyright (c) 2003-2007 Nate Lawson
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "acpi_embedded_controller.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ACPI.h>
+#include <condition_variable.h>
+#include <Errors.h>
+#include <KernelExport.h>
+#include <drivers/PCI.h>
+
+#include "SmallResourceData.h"
+
+#define ACPI_EC_DRIVER_NAME "drivers/power/acpi_embedded_controller/driver_v1"
+
+#define ACPI_EC_DEVICE_NAME "drivers/power/acpi_embedded_controller/device_v1"
+
+/* Base Namespace devices are published to */
+#define ACPI_EC_BASENAME "power/embedded_controller/%d"
+
+// name of pnp generator of path ids
+#define ACPI_EC_PATHID_GENERATOR "embedded_controller/path_id"
+
+device_manager_info *gDeviceManager = NULL;
+
+pci_module_info *gPCIManager = NULL;
+
+
+uint8
+bus_space_read_1(int address)
+{
+ return gPCIManager->read_io_8(address);
+}
+
+
+void
+bus_space_write_1(int address, uint8 v)
+{
+ gPCIManager->write_io_8(address, v);
+}
+
+
+status_t
+acpi_GetInteger(acpi_device_module_info* acpi, acpi_device& acpiCookie,
+ char* path, int* number)
+{
+ status_t status;
+ acpi_data buf;
+ acpi_object_type object;
+ buf.pointer = &object;
+ buf.length = sizeof(acpi_object_type);
+ /*
+ * Assume that what we've been pointed at is an Integer object, or
+ * a method that will return an Integer.
+ */
+ status = acpi->evaluate_method(acpiCookie, path, NULL, &buf);
+
+ if (status == B_OK) {
+ if (object.object_type == ACPI_TYPE_INTEGER)
+ *number = object.data.integer;
+ else
+ status = B_ERROR;
+ }
+ return status;
+}
+
+
+acpi_handle
+acpi_GetReference(acpi_module_info* acpi, acpi_handle scope,
+ acpi_object_type *obj)
+{
+ acpi_handle h;
+
+ if (obj == NULL)
+ return (NULL);
+
+ switch (obj->object_type) {
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ case ACPI_TYPE_ANY:
+ h = obj->data.reference.handle;
+ break;
+ case ACPI_TYPE_STRING:
+ /*
+ * The String object usually contains a fully-qualified path, so
+ * scope can be NULL.
+ *
+ * XXX This may not always be the case.
+ */
+ if (acpi->get_handle(scope, obj->data.string.string, &h) != B_OK)
+ h = NULL;
+ break;
+ default:
+ h = NULL;
+ break;
+ }
+
+ return (h);
+}
+
+
+int
+acpi_PkgInt(acpi_object_type *res, int idx, int *dst)
+{
+ acpi_object_type *obj;
+
+ obj = &res->data.package.objects[idx];
+ if (obj == NULL || obj->object_type != ACPI_TYPE_INTEGER)
+ return (EINVAL);
+ *dst = obj->data.integer;
+
+ return (0);
+}
+
+
+int
+acpi_PkgInt32(acpi_object_type *res, int idx, uint32 *dst)
+{
+ int tmp;
+ int error;
+
+ error = acpi_PkgInt(res, idx, &tmp);
+ if (error == 0)
+ *dst = (uint32)tmp;
+
+ return (error);
+}
+
+
+static status_t
+embedded_controller_open(void *initCookie, const char *path, int flags, void** cookie)
+{
+ acpi_ec_softc *device = (acpi_ec_softc*)initCookie;
+ *cookie = device;
+
+ return B_OK;
+}
+
+
+static status_t
+embedded_controller_close(void* cookie)
+{
+ return B_OK;
+}
+
+
+static status_t
+embedded_controller_read(void* _cookie, off_t position, void *buffer, size_t* numBytes)
+{
+ return B_IO_ERROR;
+}
+
+
+static status_t
+embedded_controller_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
+{
+ return B_IO_ERROR;
+}
+
+
+status_t
+embedded_controller_control(void* _cookie, uint32 op, void* arg, size_t len)
+{
+ return B_ERROR;
+}
+
+
+static status_t
+embedded_controller_free(void* cookie)
+{
+ return B_OK;
+}
+
+
+// #pragma mark - driver module API
+
+int32
+acpi_get_type(device_node* dev)
+{
+ const char *bus;
+ if (gDeviceManager->get_attr_string(dev, B_DEVICE_BUS, &bus, false))
+ return -1;
+
+ if (strcmp(bus, "acpi"))
+ return -1;
+
+ uint32 deviceType;
+ if (gDeviceManager->get_attr_uint32(dev, ACPI_DEVICE_TYPE_ITEM,
+ &deviceType, false) != B_OK)
+ return -1;
+
+ return deviceType;
+}
+
+
+static float
+embedded_controller_support(device_node *dev)
+{
+ static char *ec_ids[] = { "PNP0C09", NULL };
+
+ /* Check that this is a device. */
+ if (acpi_get_type(dev) != ACPI_TYPE_DEVICE)
+ return 0.;
+
+ const char *name;
+ if (gDeviceManager->get_attr_string(dev, ACPI_DEVICE_HID_ITEM, &name,
+ false) != B_OK || strcmp(name, ec_ids[0]))
+ return 0.0;
+
+ TRACE("supported device found %s\n", name);
+ return 0.6;
+}
+
+
+static status_t
+embedded_controller_register_device(device_node *node)
+{
+ device_attr attrs[] = {
+ { B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
+ { string: "ACPI embedded controller" }},
+ { NULL }
+ };
+
+ return gDeviceManager->register_node(node, ACPI_EC_DRIVER_NAME, attrs,
+ NULL, NULL);
+}
+
+
+static status_t
+embedded_controller_init_driver(device_node *dev, void **_driverCookie)
+{
+ TRACE("init driver\n");
+ acpi_ec_softc *sc;
+ sc = (acpi_ec_softc*)malloc(sizeof(acpi_ec_softc));
+ memset(sc, 0, sizeof(acpi_ec_softc));
+
+ *_driverCookie = sc;
+ sc->ec_dev = dev;
+
+ sc->ec_condition_var.Init(NULL, "ec condition variable");
+
+ device_node *parent;
+ parent = gDeviceManager->get_parent_node(dev);
+ gDeviceManager->get_driver(parent, (driver_module_info **)&sc->ec_acpi,
+ (void **)&sc->ec_handle);
+ gDeviceManager->put_node(parent);
+
+ SmallResourceData resourceData(sc->ec_acpi, sc->ec_handle, "_CRS");
+ if (resourceData.InitCheck() != B_OK) {
+ TRACE("failed to read _CRS resource\n") ;
+ return B_ERROR;
+ }
+ io_port portData;
+
+ if (get_module(B_ACPI_MODULE_NAME, (module_info**)&sc->ec_acpi_module) != B_OK)
+ return B_ERROR;
+
+ // DPC module
+ if (gDPC == NULL && get_module(B_DPC_MODULE_NAME,
+ (module_info **)&gDPC) != B_OK) {
+ dprintf("failed to get dpc module for os execution\n");
+ return B_ERROR;
+ }
+
+ if (gDPCHandle == NULL) {
+ if (gDPC->new_dpc_queue(&gDPCHandle, "acpi_task",
+ B_NORMAL_PRIORITY) != B_OK) {
+ dprintf("failed to create os execution queue\n");
+ return B_ERROR;
+ }
+ }
+
+ acpi_data buf;
+ buf.pointer = NULL;
+ buf.length = ACPI_ALLOCATE_BUFFER;
+
+
+ /*
+ * 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.
+ */
+
+ status_t status;
+ status = acpi_GetInteger(sc->ec_acpi, sc->ec_handle, "_UID", &sc->ec_uid);
+ if (status != B_OK)
+ sc->ec_uid = 0;
+ status = acpi_GetInteger(sc->ec_acpi, sc->ec_handle, "_GLK", &sc->ec_glk);
+ if (status != B_OK)
+ sc->ec_glk = 0;
+
+ /*
+ * Evaluate the _GPE method to find the GPE bit used by the EC to
+ * signal status (SCI). If it's a package, it contains a reference
+ * and GPE bit, similar to _PRW.
+ */
+ status = sc->ec_acpi->evaluate_method(sc->ec_handle, "_GPE", NULL, &buf);
+ if (status != B_OK) {
+ TRACE("can't evaluate _GPE\n");
+ goto error;
+ }
+
+ acpi_object_type* obj;
+ obj = (acpi_object_type*)buf.pointer;
+ if (obj == NULL)
+ goto error;
+
+ switch (obj->object_type) {
+ case ACPI_TYPE_INTEGER:
+ sc->ec_gpehandle = NULL;
+ sc->ec_gpebit = obj->data.integer;
+ break;
+ case ACPI_TYPE_PACKAGE:
+ if (!ACPI_PKG_VALID(obj, 2))
+ goto error;
+ sc->ec_gpehandle =
+ acpi_GetReference(sc->ec_acpi_module, NULL,
+ &obj->data.package.objects[0]);
+ if (sc->ec_gpehandle == NULL ||
+ acpi_PkgInt32(obj, 1, (uint32*)&sc->ec_gpebit) != 0)
+ goto error;
+ break;
+ default:
+ TRACE("_GPE has invalid type %i\n", int(obj->object_type));
+ goto error;
+ }
+
+ sc->ec_suspending = FALSE;
+
+ /* Attach bus resources for data and command/status ports. */
+ sc->ec_data_rid = 0;
+ if (resourceData.ReadIOPort(&portData) != B_OK)
+ goto error;
+
+ sc->ec_data_pci_address = portData.minimumBase;
+
+ sc->ec_csr_rid = 1;
+ if (resourceData.ReadIOPort(&portData) != B_OK)
+ goto error;
+
+ sc->ec_csr_pci_address = portData.minimumBase;
+
+ /*
+ * Install a handler for this EC's GPE bit. We want edge-triggered
+ * behavior.
+ */
+ TRACE("attaching GPE handler\n");
+ status = sc->ec_acpi_module->install_gpe_handler(sc->ec_gpehandle,
+ sc->ec_gpebit, ACPI_GPE_EDGE_TRIGGERED, &EcGpeHandler, sc);
+ if (status != B_OK) {
+ TRACE("can't install ec GPE handler\n");
+ goto error;
+ }
+
+ /*
+ * Install address space handler
+ */
+ TRACE("attaching address space handler\n");
+ status = sc->ec_acpi->install_address_space_handler(sc->ec_handle,
+ ACPI_ADR_SPACE_EC, &EcSpaceHandler, &EcSpaceSetup, sc);
+ if (status != B_OK) {
+ TRACE("can't install address space handler\n");
+ goto error;
+ }
+
+ /* Enable runtime GPEs for the handler. */
+ status = sc->ec_acpi_module->set_gpe_type(sc->ec_gpehandle, sc->ec_gpebit,
+ ACPI_GPE_TYPE_RUNTIME);
+ if (status != B_OK) {
+ TRACE("AcpiSetGpeType failed.\n");
+ goto error;
+ }
+ status = sc->ec_acpi_module->enable_gpe(sc->ec_gpehandle, sc->ec_gpebit,
+ ACPI_NOT_ISR);
+ if (status != B_OK) {
+ TRACE("AcpiEnableGpe failed.\n");
+ goto error;
+ }
+
+ return (0);
+
+error:
+ if (buf.pointer)
+ free(buf.pointer);
+
+ sc->ec_acpi_module->remove_gpe_handler(sc->ec_gpehandle, sc->ec_gpebit,
+ &EcGpeHandler);
+ sc->ec_acpi->remove_address_space_handler(sc->ec_handle, ACPI_ADR_SPACE_EC,
+ EcSpaceHandler);
+
+ return (ENXIO);
+}
+
+
+static void
+embedded_controller_uninit_driver(void *driverCookie)
+{
+ acpi_ec_softc* sc = (struct acpi_ec_softc *)driverCookie;
+ free(sc);
+ put_module(B_ACPI_MODULE_NAME);
+ put_module(B_DPC_MODULE_NAME);
+}
+
+
+static status_t
+embedded_controller_register_child_devices(void *_cookie)
+{
+ device_node *node = ((acpi_ec_softc*)_cookie)->ec_dev;
+
+ int pathID = gDeviceManager->create_id(ACPI_EC_PATHID_GENERATOR);
+ if (pathID < 0) {
+ TRACE("register_child_device couldn't create a path_id\n");
+ return B_ERROR;
+ }
+
+ char name[128];
+ snprintf(name, sizeof(name), ACPI_EC_BASENAME, pathID);
+
+ return gDeviceManager->publish_device(node, name, ACPI_EC_DEVICE_NAME);
+}
+
+
+static status_t
+embedded_controller_init_device(void *driverCookie, void **cookie)
+{
+ return B_ERROR;
+}
+
+
+static void
+embedded_controller_uninit_device(void *_cookie)
+{
+ acpi_ec_softc *device = (acpi_ec_softc*)_cookie;
+ free(device);
+}
+
+
+module_dependency module_dependencies[] = {
+ { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
+ { B_PCI_MODULE_NAME, (module_info **)&gPCIManager},
+ {}
+};
+
+
+driver_module_info embedded_controller_driver_module = {
+ {
+ ACPI_EC_DRIVER_NAME,
+ 0,
+ NULL
+ },
+
+ embedded_controller_support,
+ embedded_controller_register_device,
+ embedded_controller_init_driver,
+ embedded_controller_uninit_driver,
+ embedded_controller_register_child_devices,
+ NULL, // rescan
+ NULL, // removed
+};
+
+
+struct device_module_info embedded_controller_device_module = {
+ {
+ ACPI_EC_DEVICE_NAME,
+ 0,
+ NULL
+ },
+
+ embedded_controller_init_device,
+ embedded_controller_uninit_device,
+ NULL,
+
+ embedded_controller_open,
+ embedded_controller_close,
+ embedded_controller_free,
+ embedded_controller_read,
+ embedded_controller_write,
+ NULL,
+ embedded_controller_control,
+
+ NULL,
+ NULL
+};
+
+
+module_info *modules[] = {
+ (module_info *)&embedded_controller_driver_module,
+ (module_info *)&embedded_controller_device_module,
+ NULL
+};
+
+
+static void
+EcGpeQueryHandler(void *context)
+{
+ struct acpi_ec_softc *sc = (struct acpi_ec_softc *)context;
+ uint8 data;
+ status_t status;
+ char qxx[5];
+
+ ASSERT(context != NULL);//, ("EcGpeQueryHandler called with NULL"));
+
+ /* Serialize user access with EcSpaceHandler(). */
+ status = EcLock(sc);
+ if (status != B_OK) {
+ TRACE("GpeQuery lock error.\n");
+ return;
+ }
+
+ /*
+ * Send a query command to the EC to find out which _Qxx call it
+ * wants to make. This command clears the SCI bit and also the
+ * interrupt source since we are edge-triggered. To prevent the GPE
+ * that may arise from running the query from causing another query
+ * to be queued, we clear the pending flag only after running it.
+ */
+ status = EcCommand(sc, EC_COMMAND_QUERY);
+ sc->ec_sci_pend = FALSE;
+ if (status != B_OK) {
+ EcUnlock(sc);
+ TRACE("GPE query failed.\n");
+ return;
+ }
+ data = EC_GET_DATA(sc);
+
+ /*
+ * We have to unlock before running the _Qxx method below since that
+ * method may attempt to read/write from EC address space, causing
+ * recursive acquisition of the lock.
+ */
+ EcUnlock(sc);
+
+ /* Ignore the value for "no outstanding event". (13.3.5) */
+ TRACE("ec query ok,%s running _Q%02X\n", Data ? "" : " not", data);
+ if (data == 0)
+ return;
+
+ /* Evaluate _Qxx to respond to the controller. */
+ snprintf(qxx, sizeof(qxx), "_Q%02X", data);
+ AcpiUtStrupr(qxx);
+ status = sc->ec_acpi->evaluate_method(sc->ec_handle, qxx, NULL, NULL);
+ if (status != B_OK) {
+ TRACE("evaluation of query method %s failed\n", qxx);
+ }
+}
+
+/*
+ * The GPE handler is called when IBE/OBF or SCI events occur. We are
+ * called from an unknown lock context.
+ */
+static uint32
+EcGpeHandler(void *context)
+{
+ struct acpi_ec_softc *sc = (acpi_ec_softc*)context;
+ status_t status;
+ EC_STATUS EcStatus;
+
+ ASSERT(context != NULL);//, ("EcGpeHandler called with NULL"));
+ TRACE("ec gpe handler start\n");
+
+ /*
+ * Notify EcWaitEvent() that the status register is now fresh. If we
+ * didn't do this, it wouldn't be possible to distinguish an old IBE
+ * from a new one, for example when doing a write transaction (writing
+ * address and then data values.)
+ */
+ atomic_add(&sc->ec_gencount, 1);
+ sc->ec_condition_var.NotifyAll();
+ /*
+ * If the EC_SCI bit of the status register is set, queue a query handler.
+ * It will run the query and _Qxx method later, under the lock.
+ */
+ EcStatus = EC_GET_CSR(sc);
+ if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
+ TRACE("ec gpe queueing query handler\n");
+ status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, context);
+ if (status == B_OK)
+ sc->ec_sci_pend = TRUE;
+ else
+ dprintf("EcGpeHandler: queuing GPE query handler failed\n");
+ }
+ return (0);
+}
+
+
+static status_t
+EcSpaceSetup(acpi_handle region, uint32 function, void *context,
+ void **regionContext)
+{
+ /*
+ * If deactivating a region, always set the output to NULL. Otherwise,
+ * just pass the context through.
+ */
+ if (function == ACPI_REGION_DEACTIVATE)
+ *regionContext = NULL;
+ else
+ *regionContext = context;
+
+ return B_OK;
+}
+
+
+static status_t
+EcSpaceHandler(uint32 function, acpi_physical_address address, uint32 width,
+ int *value, void *context, void *regionContext)
+{
+ TRACE("enter EcSpaceHandler\n");
+ struct acpi_ec_softc *sc = (struct acpi_ec_softc *)context;
+ status_t status;
+ uint8 ecAddr, ecData;
+ uint32 i;
+
+ if (width % 8 != 0 || value == NULL || context == NULL)
+ return B_BAD_VALUE;
+ if (address + (width / 8) - 1 > 0xFF)
+ return B_BAD_ADDRESS;
+
+ if (function == ACPI_READ)
+ *value = 0;
+ ecAddr = address;
+ status = B_ERROR;
+
+ /*
+ * If booting, check if we need to run the query handler. If so, we
+ * we call it directly here since our thread taskq is not active yet.
+ */
+ /*if (cold || rebooting || sc->ec_suspending) {
+ if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
+ //CTR0(KTR_ACPI, "ec running gpe handler directly");
+ EcGpeQueryHandler(sc);
+ }
+ }*/
+
+ /* Serialize with EcGpeQueryHandler() at transaction granularity. */
+ status = EcLock(sc);
+ if (status != B_OK)
+ return (status);
+
+ /* Perform the transaction(s), based on width. */
+ for (i = 0; i < width; i += 8, ecAddr++) {
+ switch (function) {
+ case ACPI_READ:
+ status = EcRead(sc, ecAddr, &ecData);
+ if (status == B_OK)
+ *value |= ((int)ecData) << i;
+ break;
+ case ACPI_WRITE:
+ ecData = (uint8)((*value) >> i);
+ status = EcWrite(sc, ecAddr, &ecData);
+ break;
+ default:
+ TRACE("invalid EcSpaceHandler function\n");
+ status = B_BAD_VALUE;
+ break;
+ }
+ if (status != B_OK)
+ break;
+ }
+
+ EcUnlock(sc);
+ return (status);
+}
+
+static status_t
+EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event)
+{
+ status_t status = B_ERROR;
+ EC_STATUS ec_status;
+
+ ec_status = EC_GET_CSR(sc);
+ if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) {
+ TRACE("ec burst disabled in waitevent (%s)\n", msg);
+ sc->ec_burstactive = false;
+ }
+ if (EVENT_READY(event, ec_status)) {
+ TRACE("ec %s wait ready, status %#x\n", msg, ec_status);
+ status = B_OK;
+ }
+ return (status);
+}
+
+static status_t
+EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT event, int32 gen_count)
+{
+ status_t status = B_ERROR;
+ int32 count, i;
+
+ // int need_poll = cold || rebooting || ec_polled_mode || sc->ec_suspending;
+ int need_poll = ec_polled_mode || sc->ec_suspending;
+
+ /*
+ * The main CPU should be much faster than the EC. So the status should
+ * be "not ready" when we start waiting. But if the main CPU is really
+ * slow, it's possible we see the current "ready" response. Since that
+ * can't be distinguished from the previous response in polled mode,
+ * this is a potential issue. We really should have interrupts enabled
+ * during boot so there is no ambiguity in polled mode.
+ *
+ * If this occurs, we add an additional delay before actually entering
+ * the status checking loop, hopefully to allow the EC to go to work
+ * and produce a non-stale status.
+ */
+ if (need_poll) {
+ static int once;
+
+ if (EcCheckStatus(sc, "pre-check", event) == B_OK) {
+ if (!once) {
+ TRACE("warning: EC done before starting event wait\n");
+ once = 1;
+ }
+ spin(10);
+ }
+ }
+
+ /* Wait for event by polling or GPE (interrupt). */
+ if (need_poll) {
+ count = (ec_timeout * 1000) / EC_POLL_DELAY;
+ if (count == 0)
+ count = 1;
+ for (i = 0; i < count; i++) {
+ status = EcCheckStatus(sc, "poll", event);
+ if (status == B_OK)
+ break;
+ spin(EC_POLL_DELAY);
+ }
+ } else {
+ // ToDo: scale timeout for slow cpu see BSD code...
+ count = ec_timeout;
+
+ /*
+ * Wait for the GPE to signal the status changed, checking the
+ * status register each time we get one. It's possible to get a
+ * GPE for an event we're not interested in here (i.e., SCI for
+ * EC query).
+ */
+ for (i = 0; i < count; i++) {
+ if (gen_count != sc->ec_gencount) {
+ /*
+ * Record new generation count. It's possible the GPE was
+ * just to notify us that a query is needed and we need to
+ * wait for a second GPE to signal the completion of the
+ * event we are actually waiting for.
+ */
+ gen_count = sc->ec_gencount;
+ status = EcCheckStatus(sc, "sleep", event);
+ if (status == B_OK)
+ break;
+ }
+ sc->ec_condition_var.Wait();
[... truncated: 480 lines follow ...]
More information about the Haiku-commits
mailing list