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 :-) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31102 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -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 ;
|
||||
|
||||
@@ -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 ;
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2009, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Clemens Zeidler, [email protected]
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2009, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Clemens Zeidler, [email protected]
|
||||
*/
|
||||
|
||||
#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
|
||||
+937
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
* We finished waiting for the GPE and it never arrived. Try to
|
||||
* read the register once and trust whatever value we got. This is
|
||||
* the best we can do at this point. Then, force polled mode on
|
||||
* since this system doesn't appear to generate GPEs.
|
||||
*/
|
||||
if (status != B_OK) {
|
||||
status = EcCheckStatus(sc, "sleep_end", event);
|
||||
TRACE("wait timed out (%sresponse), forcing polled mode\n",
|
||||
Status == B_OK ? "" : "no ");
|
||||
ec_polled_mode = TRUE;
|
||||
}
|
||||
}
|
||||
if (status != B_OK)
|
||||
TRACE("error: ec wait timed out\n");
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
static status_t
|
||||
EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd)
|
||||
{
|
||||
status_t status;
|
||||
EC_EVENT event;
|
||||
EC_STATUS ec_status;
|
||||
u_int gen_count;
|
||||
|
||||
/* Don't use burst mode if user disabled it. */
|
||||
if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE)
|
||||
return (B_ERROR);
|
||||
|
||||
/* Decide what to wait for based on command type. */
|
||||
switch (cmd) {
|
||||
case EC_COMMAND_READ:
|
||||
case EC_COMMAND_WRITE:
|
||||
case EC_COMMAND_BURST_DISABLE:
|
||||
event = EC_EVENT_INPUT_BUFFER_EMPTY;
|
||||
break;
|
||||
case EC_COMMAND_QUERY:
|
||||
case EC_COMMAND_BURST_ENABLE:
|
||||
event = EC_EVENT_OUTPUT_BUFFER_FULL;
|
||||
break;
|
||||
default:
|
||||
TRACE("EcCommand: invalid command %#x\n", cmd);
|
||||
return (B_BAD_VALUE);
|
||||
}
|
||||
|
||||
/* Run the command and wait for the chosen event. */
|
||||
TRACE("ec running command %#x\n", cmd);
|
||||
gen_count = sc->ec_gencount;
|
||||
EC_SET_CSR(sc, cmd);
|
||||
status = EcWaitEvent(sc, event, gen_count);
|
||||
if (status == B_OK) {
|
||||
/* If we succeeded, burst flag should now be present. */
|
||||
if (cmd == EC_COMMAND_BURST_ENABLE) {
|
||||
ec_status = EC_GET_CSR(sc);
|
||||
if ((ec_status & EC_FLAG_BURST_MODE) == 0)
|
||||
status = B_ERROR;
|
||||
}
|
||||
} else
|
||||
TRACE("EcCommand: no response to %#x\n", cmd);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
static status_t
|
||||
EcRead(struct acpi_ec_softc *sc, uint8 address, uint8 *readData)
|
||||
{
|
||||
status_t status;
|
||||
uint8 data;
|
||||
u_int gen_count;
|
||||
|
||||
TRACE("ec read from %#x\n", Address);
|
||||
|
||||
/* If we can't start burst mode, continue anyway. */
|
||||
status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
|
||||
if (status == B_OK) {
|
||||
data = EC_GET_DATA(sc);
|
||||
if (data == EC_BURST_ACK) {
|
||||
TRACE("ec burst enabled\n");
|
||||
sc->ec_burstactive = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
status = EcCommand(sc, EC_COMMAND_READ);
|
||||
if (status != B_OK)
|
||||
return (status);
|
||||
|
||||
gen_count = sc->ec_gencount;
|
||||
|
||||
EC_SET_DATA(sc, address);
|
||||
status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, gen_count);
|
||||
if (status != B_OK) {
|
||||
TRACE("EcRead: failed waiting to get data\n");
|
||||
return (status);
|
||||
}
|
||||
*readData = EC_GET_DATA(sc);
|
||||
|
||||
if (sc->ec_burstactive) {
|
||||
sc->ec_burstactive = FALSE;
|
||||
status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
|
||||
if (status != B_OK)
|
||||
return (status);
|
||||
TRACE("ec disabled burst ok\n");
|
||||
}
|
||||
|
||||
return (B_OK);
|
||||
}
|
||||
|
||||
static status_t
|
||||
EcWrite(struct acpi_ec_softc *sc, uint8 address, uint8 *writeData)
|
||||
{
|
||||
status_t status;
|
||||
uint8 data;
|
||||
u_int gen_count;
|
||||
|
||||
/* If we can't start burst mode, continue anyway. */
|
||||
status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
|
||||
if (status == B_OK) {
|
||||
data = EC_GET_DATA(sc);
|
||||
if (data == EC_BURST_ACK) {
|
||||
TRACE("ec burst enabled\n");
|
||||
sc->ec_burstactive = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
status = EcCommand(sc, EC_COMMAND_WRITE);
|
||||
if (status != B_OK)
|
||||
return (status);
|
||||
|
||||
gen_count = sc->ec_gencount;
|
||||
EC_SET_DATA(sc, address);
|
||||
status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
|
||||
if (status != B_OK) {
|
||||
TRACE("EcRead: failed waiting for sent address\n");
|
||||
return (status);
|
||||
}
|
||||
|
||||
gen_count = sc->ec_gencount;
|
||||
EC_SET_DATA(sc, *writeData);
|
||||
status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, gen_count);
|
||||
if (status != B_OK) {
|
||||
TRACE("EcWrite: failed waiting for sent data\n");
|
||||
return (status);
|
||||
}
|
||||
|
||||
if (sc->ec_burstactive) {
|
||||
sc->ec_burstactive = FALSE;
|
||||
status = EcCommand(sc, EC_COMMAND_BURST_DISABLE);
|
||||
if (status != B_OK)
|
||||
return (status);
|
||||
TRACE("ec disabled burst ok");
|
||||
}
|
||||
|
||||
return (B_OK);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ACPI_BATTERY_H
|
||||
#define ACPI_BATTERY_H
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <condition_variable.h>
|
||||
#include <dpc.h>
|
||||
#include <Drivers.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
|
||||
// #define TRACE_EMBEDDED_CONTROLLER
|
||||
#ifdef TRACE_EMBEDDED_CONTROLLER
|
||||
# define TRACE(x...) dprintf("EC: " x)
|
||||
#else
|
||||
# define TRACE(x...)
|
||||
#endif
|
||||
|
||||
|
||||
dpc_module_info *gDPC = NULL;
|
||||
void *gDPCHandle;
|
||||
|
||||
|
||||
#define ACPI_GPE_EDGE_TRIGGERED (uint8) 0x00
|
||||
#define ACPI_GPE_TYPE_RUNTIME (uint8) 0x04 /* Default */
|
||||
|
||||
#define ACPI_NOT_ISR 0x1
|
||||
#define ACPI_ISR 0x0
|
||||
|
||||
enum {
|
||||
OSL_GLOBAL_LOCK_HANDLER,
|
||||
OSL_NOTIFY_HANDLER,
|
||||
OSL_GPE_HANDLER,
|
||||
OSL_DEBUGGER_THREAD,
|
||||
OSL_EC_POLL_HANDLER,
|
||||
OSL_EC_BURST_HANDLER
|
||||
};
|
||||
|
||||
typedef void (*ACPI_OSD_EXEC_CALLBACK)(void* Context);
|
||||
|
||||
|
||||
// ToDo: Maybe also put this acpi function into the acpi module?
|
||||
status_t
|
||||
AcpiOsExecute(uint32 Type, ACPI_OSD_EXEC_CALLBACK Function,
|
||||
void *Context)
|
||||
{
|
||||
switch (Type) {
|
||||
case OSL_GLOBAL_LOCK_HANDLER:
|
||||
case OSL_NOTIFY_HANDLER:
|
||||
case OSL_GPE_HANDLER:
|
||||
case OSL_DEBUGGER_THREAD:
|
||||
case OSL_EC_POLL_HANDLER:
|
||||
case OSL_EC_BURST_HANDLER:
|
||||
break;
|
||||
}
|
||||
|
||||
if (gDPC->queue_dpc(gDPCHandle, Function, Context) != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
/* copied from utmisc.c don't want to put this simple function into the acpi
|
||||
module */
|
||||
void
|
||||
AcpiUtStrupr(char *SrcString)
|
||||
{
|
||||
char *String;
|
||||
|
||||
if (!SrcString)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Walk entire string, uppercasing the letters */
|
||||
|
||||
for (String = SrcString; *String; String++)
|
||||
{
|
||||
*String = (char) toupper(*String);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#define ACPI_REGION_DEACTIVATE 1
|
||||
|
||||
#define ACPI_READ 0
|
||||
#define ACPI_WRITE 1
|
||||
|
||||
|
||||
#define EC_COMMAND_UNKNOWN ((EC_COMMAND) 0x00)
|
||||
#define EC_COMMAND_READ ((EC_COMMAND) 0x80)
|
||||
#define EC_COMMAND_WRITE ((EC_COMMAND) 0x81)
|
||||
#define EC_COMMAND_BURST_ENABLE ((EC_COMMAND) 0x82)
|
||||
#define EC_COMMAND_BURST_DISABLE ((EC_COMMAND) 0x83)
|
||||
#define EC_COMMAND_QUERY ((EC_COMMAND) 0x84)
|
||||
|
||||
/*
|
||||
* EC_STATUS:
|
||||
* ----------
|
||||
* The encoding of the EC status register is illustrated below.
|
||||
* Note that a set bit (1) indicates the property is TRUE
|
||||
* (e.g. if bit 0 is set then the output buffer is full).
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |7|6|5|4|3|2|1|0|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* | | | | | | | |
|
||||
* | | | | | | | +- Output Buffer Full?
|
||||
* | | | | | | +--- Input Buffer Full?
|
||||
* | | | | | +----- <reserved>
|
||||
* | | | | +------- Data Register is Command Byte?
|
||||
* | | | +--------- Burst Mode Enabled?
|
||||
* | | +----------- SCI Event?
|
||||
* | +------------- SMI Event?
|
||||
* +--------------- <reserved>
|
||||
*
|
||||
*/
|
||||
|
||||
typedef uint8 EC_STATUS;
|
||||
|
||||
#define EC_FLAG_OUTPUT_BUFFER ((uint8) 0x01)
|
||||
#define EC_FLAG_INPUT_BUFFER ((uint8) 0x02)
|
||||
#define EC_FLAG_DATA_IS_CMD ((uint8) 0x08)
|
||||
#define EC_FLAG_BURST_MODE ((uint8) 0x10)
|
||||
|
||||
/*
|
||||
* EC_EVENT:
|
||||
* ---------
|
||||
*/
|
||||
|
||||
#define EC_EVENT_UNKNOWN ((uint8) 0x00)
|
||||
#define EC_EVENT_OUTPUT_BUFFER_FULL ((uint8) 0x01)
|
||||
#define EC_EVENT_INPUT_BUFFER_EMPTY ((uint8) 0x02)
|
||||
#define EC_EVENT_SCI ((uint8) 0x20)
|
||||
#define EC_EVENT_SMI ((uint8) 0x40)
|
||||
|
||||
/* Data byte returned after burst enable indicating it was successful. */
|
||||
#define EC_BURST_ACK 0x90
|
||||
|
||||
/* Total time in ms spent waiting for a response from EC. */
|
||||
#define EC_TIMEOUT 750
|
||||
|
||||
static int ec_burst_mode = 1;
|
||||
static int ec_polled_mode = 0;
|
||||
|
||||
static int ec_timeout = EC_TIMEOUT;
|
||||
|
||||
/*
|
||||
* Register access primitives
|
||||
*/
|
||||
#define EC_GET_DATA(sc) \
|
||||
bus_space_read_1((sc)->ec_data_pci_address)
|
||||
|
||||
#define EC_SET_DATA(sc, v) \
|
||||
bus_space_write_1((sc)->ec_data_pci_address, (v))
|
||||
|
||||
#define EC_GET_CSR(sc) \
|
||||
bus_space_read_1((sc)->ec_csr_pci_address)
|
||||
|
||||
#define EC_SET_CSR(sc, v) \
|
||||
bus_space_write_1((sc)->ec_csr_pci_address, (v))
|
||||
|
||||
|
||||
#define ACPI_PKG_VALID(pkg, size) \
|
||||
((pkg) != NULL && (pkg)->object_type == ACPI_TYPE_PACKAGE && \
|
||||
(pkg)->data.package.count >= (size))
|
||||
|
||||
int32 acpi_get_type(device_node* dev);
|
||||
|
||||
/*
|
||||
* Driver softc.
|
||||
*/
|
||||
struct acpi_ec_softc {
|
||||
device_node* ec_dev;
|
||||
acpi_module_info* ec_acpi_module;
|
||||
acpi_device_module_info* ec_acpi;
|
||||
acpi_device ec_handle;
|
||||
int ec_uid;
|
||||
acpi_handle ec_gpehandle;
|
||||
uint8 ec_gpebit;
|
||||
|
||||
int ec_data_rid;
|
||||
int ec_data_pci_address;
|
||||
|
||||
int ec_csr_rid;
|
||||
int ec_csr_pci_address;
|
||||
|
||||
int ec_glk;
|
||||
uint32 ec_glkhandle;
|
||||
int ec_burstactive;
|
||||
int ec_sci_pend;
|
||||
vint32 ec_gencount;
|
||||
ConditionVariable ec_condition_var;
|
||||
int ec_suspending;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* XXX njl
|
||||
* I couldn't find it in the spec but other implementations also use a
|
||||
* value of 1 ms for the time to acquire global lock.
|
||||
*/
|
||||
#define EC_LOCK_TIMEOUT 1000
|
||||
|
||||
/* Default delay in microseconds between each run of the status polling loop. */
|
||||
#define EC_POLL_DELAY 5
|
||||
|
||||
/* Total time in ms spent waiting for a response from EC. */
|
||||
#define EC_TIMEOUT 750
|
||||
|
||||
#define EVENT_READY(event, status) \
|
||||
(((event) == EC_EVENT_OUTPUT_BUFFER_FULL && \
|
||||
((status) & EC_FLAG_OUTPUT_BUFFER) != 0) || \
|
||||
((event) == EC_EVENT_INPUT_BUFFER_EMPTY && \
|
||||
((status) & EC_FLAG_INPUT_BUFFER) == 0))
|
||||
|
||||
|
||||
static status_t
|
||||
EcLock(struct acpi_ec_softc *sc)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
/* If _GLK is non-zero, acquire the global lock. */
|
||||
status = B_OK;
|
||||
if (sc->ec_glk) {
|
||||
status = sc->ec_acpi_module->acquire_global_lock(EC_LOCK_TIMEOUT,
|
||||
&sc->ec_glkhandle);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
EcUnlock(struct acpi_ec_softc *sc)
|
||||
{
|
||||
if (sc->ec_glk)
|
||||
sc->ec_acpi_module->release_global_lock(sc->ec_glkhandle);
|
||||
}
|
||||
|
||||
typedef unsigned int EC_EVENT;
|
||||
typedef unsigned int EC_COMMAND;
|
||||
|
||||
static uint32 EcGpeHandler(void *context);
|
||||
static status_t EcSpaceSetup(acpi_handle region, uint32 function,
|
||||
void *context, void **return_Context);
|
||||
static status_t EcSpaceHandler(uint32 function,
|
||||
acpi_physical_address address,
|
||||
uint32 width, int *value,
|
||||
void *context, void *regionContext);
|
||||
static status_t EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT event,
|
||||
int32 gen_count);
|
||||
static status_t EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd);
|
||||
static status_t EcRead(struct acpi_ec_softc *sc, uint8 address,
|
||||
uint8 *readData);
|
||||
static status_t EcWrite(struct acpi_ec_softc *sc, uint8 address,
|
||||
uint8 *writeData);
|
||||
|
||||
|
||||
/*
|
||||
* XXX njl
|
||||
* I couldn't find it in the spec but other implementations also use a
|
||||
* value of 1 ms for the time to acquire global lock.
|
||||
*/
|
||||
#define EC_LOCK_TIMEOUT 1000
|
||||
|
||||
/* Default delay in microseconds between each run of the status polling loop. */
|
||||
#define EC_POLL_DELAY 5
|
||||
|
||||
/* Total time in ms spent waiting for a response from EC. */
|
||||
#define EC_TIMEOUT 750
|
||||
|
||||
#define EVENT_READY(event, status) \
|
||||
(((event) == EC_EVENT_OUTPUT_BUFFER_FULL && \
|
||||
((status) & EC_FLAG_OUTPUT_BUFFER) != 0) || \
|
||||
((event) == EC_EVENT_INPUT_BUFFER_EMPTY && \
|
||||
((status) & EC_FLAG_INPUT_BUFFER) == 0))
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user