* Copyright 2013, Jérôme Duval, korli@users.berlios.de.
* 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 "EmbeddedController.h"
#include <kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <condition_variable.h>
#include <Errors.h>
#include <KernelExport.h>
#include <drivers/PCI.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"
#define ACPI_EC_BASENAME "power/embedded_controller/%d"
#define ACPI_EC_PATHID_GENERATOR "embedded_controller/path_id"
uint8
bus_space_read_1(int address)
{
return gPCIManager->read_io_8(address);
}
void
bus_space_write_1(int address, uint8 value)
{
gPCIManager->write_io_8(address, value);
}
status_t
acpi_GetInteger(acpi_device_module_info* acpi, acpi_device& acpiCookie,
const char* path, int* number)
{
acpi_data buf;
acpi_object_type object;
buf.pointer = &object;
buf.length = sizeof(acpi_object_type);
status_t status = acpi->evaluate_method(acpiCookie, path, NULL, &buf);
if (status == B_OK) {
if (object.object_type == ACPI_TYPE_INTEGER)
*number = object.integer.integer;
else
status = B_BAD_VALUE;
}
return status;
}
acpi_handle
acpi_GetReference(acpi_module_info* acpi, acpi_handle scope,
acpi_object_type* obj)
{
if (obj == NULL)
return NULL;
switch (obj->object_type) {
case ACPI_TYPE_LOCAL_REFERENCE:
case ACPI_TYPE_ANY:
return obj->reference.handle;
case ACPI_TYPE_STRING:
{
acpi_handle handle;
if (acpi->get_handle(scope, obj->string.string, &handle)
== B_OK)
return handle;
}
}
return NULL;
}
status_t
acpi_PkgInt(acpi_object_type* res, int idx, int* dst)
{
acpi_object_type* obj = &res->package.objects[idx];
if (obj == NULL || obj->object_type != ACPI_TYPE_INTEGER)
return B_BAD_VALUE;
*dst = obj->integer.integer;
return B_OK;
}
status_t
acpi_PkgInt32(acpi_object_type* res, int idx, uint32* dst)
{
int tmp;
status_t status = acpi_PkgInt(res, idx, &tmp);
if (status == B_OK)
*dst = (uint32) tmp;
return status;
}
acpi_status
embedded_controller_io_ports_parse_callback(ACPI_RESOURCE* resource,
void* _context)
{
acpi_ec_cookie* sc = (acpi_ec_cookie*)_context;
if (resource->Type != ACPI_RESOURCE_TYPE_IO)
return AE_OK;
if (sc->ec_data_pci_address == 0) {
sc->ec_data_pci_address = resource->Data.Io.Minimum;
} else if (sc->ec_csr_pci_address == 0) {
sc->ec_csr_pci_address = resource->Data.Io.Minimum;
} else {
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
static status_t
embedded_controller_open(void* initCookie, const char* path, int flags,
void** cookie)
{
acpi_ec_cookie* device = (acpi_ec_cookie*) 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;
}
static 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)
{
TRACE("embedded_controller_support()\n");
if (acpi_get_type(dev) != ACPI_TYPE_DEVICE)
return 0.0;
const char* name;
if (gDeviceManager->get_attr_string(dev, ACPI_DEVICE_HID_ITEM, &name, false)
!= B_OK)
return 0.0;
static const char* kEmbeddedControllerIDs[] = { "PNP0C09" };
for (size_t i = 0; i < sizeof(kEmbeddedControllerIDs)
/ sizeof(kEmbeddedControllerIDs[0]); i++) {
if (!strcmp(name, kEmbeddedControllerIDs[i])) {
TRACE("supported device found %s\n", name);
return 0.6;
}
}
return 0.0;
}
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_cookie* sc;
sc = (acpi_ec_cookie*)malloc(sizeof(acpi_ec_cookie));
if (sc == NULL)
return B_NO_MEMORY;
memset(sc, 0, sizeof(acpi_ec_cookie));
*_driverCookie = sc;
sc->ec_dev = dev;
sc->ec_condition_var.Init(NULL, "ec condition variable");
mutex_init(&sc->ec_lock, "ec lock");
device_node* parent = gDeviceManager->get_parent_node(dev);
gDeviceManager->get_driver(parent, (driver_module_info**)&sc->ec_acpi,
(void**)&sc->ec_handle);
gDeviceManager->put_node(parent);
if (get_module(B_ACPI_MODULE_NAME, (module_info**)&sc->ec_acpi_module)
!= B_OK)
return B_ERROR;
acpi_data buf;
buf.pointer = NULL;
buf.length = ACPI_ALLOCATE_BUFFER;
status_t 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;
status = sc->ec_acpi->evaluate_method(sc->ec_handle, "_GPE", NULL, &buf);
if (status != B_OK) {
ERROR("can't evaluate _GPE %s\n", strerror(status));
goto error2;
}
acpi_object_type* obj;
obj = (acpi_object_type*)buf.pointer;
if (obj == NULL)
goto error2;
switch (obj->object_type) {
case ACPI_TYPE_INTEGER:
sc->ec_gpehandle = NULL;
sc->ec_gpebit = obj->integer.integer;
break;
case ACPI_TYPE_PACKAGE:
if (!ACPI_PKG_VALID(obj, 2))
goto error2;
sc->ec_gpehandle = acpi_GetReference(sc->ec_acpi_module, NULL,
&obj->package.objects[0]);
if (sc->ec_gpehandle == NULL
|| acpi_PkgInt32(obj, 1, (uint32*)&sc->ec_gpebit) != B_OK)
goto error2;
break;
default:
ERROR("_GPE has invalid type %i\n", int(obj->object_type));
goto error2;
}
sc->ec_suspending = FALSE;
status = sc->ec_acpi->walk_resources(sc->ec_handle, (ACPI_STRING)"_CRS",
embedded_controller_io_ports_parse_callback, sc);
if (status != B_OK) {
ERROR("Error while getting IO ports addresses\n");
goto error2;
}
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 error1;
}
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) {
ERROR("can't install address space handler\n");
goto error1;
}
status = sc->ec_acpi_module->enable_gpe(sc->ec_gpehandle, sc->ec_gpebit);
if (status != B_OK) {
ERROR("AcpiEnableGpe failed.\n");
goto error1;
}
return 0;
error1:
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);
error2:
free(buf.pointer);
device_node *child = NULL;
const device_attr attrs[] = { { NULL } };
while (gDeviceManager->get_next_child_node(dev, attrs, &child) == B_OK)
gDeviceManager->unregister_node(child);
return ENXIO;
}
static void
embedded_controller_uninit_driver(void* driverCookie)
{
acpi_ec_cookie* sc = (struct acpi_ec_cookie*)driverCookie;
mutex_destroy(&sc->ec_lock);
free(sc);
put_module(B_ACPI_MODULE_NAME);
}
static status_t
embedded_controller_register_child_devices(void* _cookie)
{
device_node* node = ((acpi_ec_cookie*)_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)
{
*cookie = driverCookie;
return B_OK;
}
static void
embedded_controller_uninit_device(void* _cookie)
{
}
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,
NULL,
};
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
};
static acpi_status
EcCheckStatus(struct acpi_ec_cookie* sc, const char* msg, EC_EVENT event)
{
acpi_status status = AE_NO_HARDWARE_RESPONSE;
EC_STATUS ec_status = EC_GET_CSR(sc);
if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) {
TRACE("burst disabled in waitevent (%s)\n", msg);
sc->ec_burstactive = false;
}
if (EVENT_READY(event, ec_status)) {
TRACE("%s wait ready, status %#x\n", msg, ec_status);
status = AE_OK;
}
return status;
}
static void
EcGpeQueryHandlerSub(struct acpi_ec_cookie *sc)
{
status_t status = EcLock(sc);
if (status != B_OK) {
TRACE("GpeQuery lock error.\n");
return;
}
acpi_status acpi_status = AE_ERROR;
for (uint8 retry = 0; retry < 2; retry++) {
acpi_status = EcCommand(sc, EC_COMMAND_QUERY);
if (acpi_status == AE_OK)
break;
if (EcCheckStatus(sc, "retr_check",
EC_EVENT_INPUT_BUFFER_EMPTY) != AE_OK)
break;
}
if (acpi_status != AE_OK) {
EcUnlock(sc);
TRACE("GPE query failed.\n");
return;
}
uint8 data = EC_GET_DATA(sc);
EcUnlock(sc);
TRACE("query ok,%s running _Q%02X\n", data ? "" : " not", data);
if (data == 0)
return;
char qxx[5];
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);
}
}
static void
EcGpeQueryHandler(void* context)
{
struct acpi_ec_cookie* sc = (struct acpi_ec_cookie*)context;
int32 pending;
ASSERT(context != NULL);
do {
pending = atomic_get(&sc->ec_sci_pending);
EcGpeQueryHandlerSub(sc);
} while (atomic_test_and_set(&sc->ec_sci_pending, 0, pending));
}
called from an unknown lock context.
*/
static uint32
EcGpeHandler(acpi_handle gpeDevice, uint32 gpeNumber, void* context)
{
struct acpi_ec_cookie* sc = (acpi_ec_cookie*)context;
ASSERT(context != NULL);
TRACE("gpe handler start\n");
atomic_add(&sc->ec_gencount, 1);
sc->ec_condition_var.NotifyAll();
EC_STATUS ecStatus = EC_GET_CSR(sc);
if ((ecStatus & EC_EVENT_SCI) && atomic_add(&sc->ec_sci_pending, 1) == 0) {
TRACE("gpe queueing query handler\n");
acpi_status status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler,
context);
if (status != AE_OK) {
dprintf("EcGpeHandler: queuing GPE query handler failed\n");
atomic_add(&sc->ec_sci_pending, -1);
}
}
return ACPI_REENABLE_GPE;
}
static acpi_status
EcSpaceSetup(acpi_handle region, uint32 function, void* context,
void** regionContext)
{
if (function == ACPI_REGION_DEACTIVATE)
*regionContext = NULL;
else
*regionContext = context;
return AE_OK;
}
static acpi_status
EcSpaceHandler(uint32 function, acpi_physical_address address, uint32 width,
int* value, void* context, void* regionContext)
{
TRACE("enter EcSpaceHandler\n");
struct acpi_ec_cookie* sc = (struct acpi_ec_cookie*)context;
if (function != ACPI_READ && function != ACPI_WRITE)
return AE_BAD_PARAMETER;
if (width % 8 != 0 || value == NULL || context == NULL)
return AE_BAD_PARAMETER;
if (address + width / 8 > 256)
return AE_BAD_ADDRESS;
if (gKernelStartup || gKernelShutdown || sc->ec_suspending) {
if ((EC_GET_CSR(sc) & EC_EVENT_SCI) &&
atomic_add(&sc->ec_sci_pending, 1) == 0) {
EcGpeQueryHandler(sc);
}
}
acpi_status status = EcLock(sc);
if (status != B_OK)
return AE_NOT_ACQUIRED;
status = EcCommand(sc, EC_COMMAND_BURST_ENABLE);
if (status == B_OK) {
if (EC_GET_DATA(sc) == EC_BURST_ACK) {
TRACE("burst enabled.\n");
sc->ec_burstactive = TRUE;
}
}
ACPI_PHYSICAL_ADDRESS ecAddr = address;
uint8* ecData = (uint8 *) value;
if (function == ACPI_READ)
*value = 0;
do {
switch (function) {
case ACPI_READ:
status = EcRead(sc, ecAddr, ecData);
break;
case ACPI_WRITE:
status = EcWrite(sc, ecAddr, *ecData);
break;
}
if (status != AE_OK)
break;
ecAddr++;
ecData++;
} while (ecAddr < address + width / 8);
if (sc->ec_burstactive) {
sc->ec_burstactive = FALSE;
if (EcCommand(sc, EC_COMMAND_BURST_DISABLE) == AE_OK)
TRACE("disabled burst ok.");
}
EcUnlock(sc);
return status;
}
static acpi_status
EcWaitEvent(struct acpi_ec_cookie* sc, EC_EVENT event, int32 generationCount)
{
static int32 noIntr = 0;
acpi_status status = AE_NO_HARDWARE_RESPONSE;
int32 count, i;
int needPoll = ec_polled_mode || sc->ec_suspending
|| gKernelStartup || gKernelShutdown;
if (needPoll) {
count = (ec_timeout * 1000) / EC_POLL_DELAY;
if (count == 0)
count = 1;
spin(10);
for (i = 0; i < count; i++) {
status = EcCheckStatus(sc, "poll", event);
if (status == AE_OK)
break;
spin(EC_POLL_DELAY);
}
} else {
for (i = 0; i < ec_timeout; i++) {
if (generationCount == sc->ec_gencount) {
sc->ec_condition_var.Wait(B_RELATIVE_TIMEOUT, 1000);
}
* 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.
*/
status = EcCheckStatus(sc, "sleep", event);
if (status == AE_OK) {
if (generationCount == sc->ec_gencount)
noIntr++;
else
noIntr = 0;
break;
}
generationCount = sc->ec_gencount;
}
* 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.
*/
if (status != AE_OK)
status = EcCheckStatus(sc, "sleep_end", event);
}
if (!needPoll && noIntr > 10) {
TRACE("not getting interrupts, switched to polled mode\n");
ec_polled_mode = true;
}
if (status != AE_OK)
TRACE("error: ec wait timed out\n");
return status;
}
static acpi_status
EcCommand(struct acpi_ec_cookie* sc, EC_COMMAND cmd)
{
if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE)
return AE_ERROR;
EC_EVENT event;
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 AE_BAD_PARAMETER;
}
acpi_status status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, 0);
if (status != AE_OK)
return status;
TRACE("running command %#x\n", cmd);
int32 generationCount = sc->ec_gencount;
EC_SET_CSR(sc, cmd);
status = EcWaitEvent(sc, event, generationCount);
if (status == AE_OK) {
if (cmd == EC_COMMAND_BURST_ENABLE) {
EC_STATUS ec_status = EC_GET_CSR(sc);
if ((ec_status & EC_FLAG_BURST_MODE) == 0)
status = AE_ERROR;
}
} else
TRACE("EcCommand: no response to %#x\n", cmd);
return status;
}
static acpi_status
EcRead(struct acpi_ec_cookie* sc, uint8 address, uint8* readData)
{
TRACE("read from %#x\n", address);
acpi_status status;
for (uint8 retry = 0; retry < 2; retry++) {
status = EcCommand(sc, EC_COMMAND_READ);
if (status != AE_OK)
return status;
int32 generationCount = sc->ec_gencount;
EC_SET_DATA(sc, address);
status = EcWaitEvent(sc, EC_EVENT_OUTPUT_BUFFER_FULL, generationCount);
if (status == AE_OK) {
*readData = EC_GET_DATA(sc);
return AE_OK;
}
if (EcCheckStatus(sc, "retr_check", EC_EVENT_INPUT_BUFFER_EMPTY)
!= AE_OK) {
break;
}
}
TRACE("EcRead: failed waiting to get data\n");
return status;
}
static acpi_status
EcWrite(struct acpi_ec_cookie* sc, uint8 address, uint8 writeData)
{
acpi_status status = EcCommand(sc, EC_COMMAND_WRITE);
if (status != AE_OK)
return status;
int32 generationCount = sc->ec_gencount;
EC_SET_DATA(sc, address);
status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, generationCount);
if (status != AE_OK) {
TRACE("EcWrite: failed waiting for sent address\n");
return status;
}
generationCount = sc->ec_gencount;
EC_SET_DATA(sc, writeData);
status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, generationCount);
if (status != AE_OK) {
TRACE("EcWrite: failed waiting for sent data\n");
return status;
}
return AE_OK;
}