root/src/add-ons/kernel/bus_managers/acpi/EmbeddedController.h
/*-
 * 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_EMBEDDED_CONTROLLER_H
#define ACPI_EMBEDDED_CONTROLLER_H


#include <ctype.h>

#include <ACPI.h>
#include <condition_variable.h>

#include <Drivers.h>
#include <KernelExport.h>
#include <lock.h>

extern "C" {
#       include "acpi.h"
#       include "accommon.h"
#       include "acnamesp.h"
#       include "actypes.h"
#       include "ACPIPrivate.h"
}


// #define TRACE_EMBEDDED_CONTROLLER
#ifdef TRACE_EMBEDDED_CONTROLLER
#       define TRACE(x...) dprintf("EC: " x)
#else
#       define TRACE(x...)
#endif

#define ERROR(x...) dprintf("EC: " x)


typedef uint8                                                   EC_COMMAND;

#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                   ((EC_STATUS) 0x01)
#define EC_FLAG_INPUT_BUFFER                    ((EC_STATUS) 0x02)
#define EC_FLAG_DATA_IS_CMD                             ((EC_STATUS) 0x08)
#define EC_FLAG_BURST_MODE                              ((EC_STATUS) 0x10)

/*
 * EC_EVENT:
 * ---------
 */
typedef uint8                                                   EC_EVENT;

#define EC_EVENT_UNKNOWN                                ((EC_EVENT) 0x00)
#define EC_EVENT_OUTPUT_BUFFER_FULL             ((EC_EVENT) 0x01)
#define EC_EVENT_INPUT_BUFFER_EMPTY             ((EC_EVENT) 0x02)
#define EC_EVENT_SCI                                    ((EC_EVENT) 0x20)
#define EC_EVENT_SMI                                    ((EC_EVENT) 0x40)

/* Data byte returned after burst enable indicating it was successful. */
#define EC_BURST_ACK                                    0x90


/*
 * 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)->package.count >= (size))


/*
 * Driver cookie.
 */
struct acpi_ec_cookie {
        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_pci_address;
        int                                                     ec_csr_pci_address;

        int                                                     ec_glk;
        uint32                                          ec_glkhandle;
        mutex                                           ec_lock;
        int                                                     ec_burstactive;
        int32                                           ec_sci_pending;
        int32                                           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   50

/* 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 bool             ec_burst_mode = true;
static bool             ec_polled_mode = false;

static int              ec_timeout = EC_TIMEOUT;


static status_t
EcLock(struct acpi_ec_cookie *sc)
{
        /* If _GLK is non-zero, acquire the global lock. */
        if (sc->ec_glk) {
                status_t status = sc->ec_acpi_module->acquire_global_lock(EC_LOCK_TIMEOUT,
                        &sc->ec_glkhandle);
                if (status != B_OK)
                        return status;
        }
        return mutex_lock(&sc->ec_lock);
}


static void
EcUnlock(struct acpi_ec_cookie *sc)
{
        mutex_unlock(&sc->ec_lock);
        if (sc->ec_glk)
                sc->ec_acpi_module->release_global_lock(sc->ec_glkhandle);
}


static uint32                   EcGpeHandler(acpi_handle gpeDevice, uint32 gpeNumber,
                                                        void *context);

static acpi_status              EcSpaceSetup(acpi_handle region, uint32 function,
                                                        void *context, void **return_Context);
static acpi_status              EcSpaceHandler(uint32 function,
                                                        acpi_physical_address address,
                                                        uint32 width, int *value,
                                                        void *context, void *regionContext);
static acpi_status              EcWaitEvent(struct acpi_ec_cookie *sc, EC_EVENT event,
                                                        int32 gen_count);
static acpi_status              EcCommand(struct acpi_ec_cookie *sc, EC_COMMAND cmd);
static acpi_status              EcRead(struct acpi_ec_cookie *sc, uint8 address,
                                                        uint8 *readData);
static acpi_status              EcWrite(struct acpi_ec_cookie *sc, uint8 address,
                                                        uint8 writeData);


#endif  // ACPI_EMBEDDED_CONTROLLER_H