coreboot/src/ec/acpi/ec.c
Bill XIE eb4ded6925 ec/acpi: add mechanisms to clear EC output queue
EC's output could be considered as a queue, and sometimes (observed on
Thinkpad T400s during cold boot) a few (only one observed) garbage bytes
may detained in such queue after power up. Those garbage bytes should be
checked and discarded first before real interactions, otherwise they may
disrupt the interaction during EC's enablement, causing a locked rfkill.

Change-Id: Iee031306c02f5211a4512c6b4ec90f7f0db196ae
Signed-off-by: Bill XIE <persmule@gmail.com>
Reviewed-on: https://review.coreboot.org/22180
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Patrick Rudolph <siro@das-labor.org>
2017-11-07 12:26:42 +00:00

179 lines
3.2 KiB
C

/*
* This file is part of the coreboot project.
*
* Copyright (C) 2008-2009 coresystems GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <console/console.h>
#include <device/device.h>
#include <arch/io.h>
#include <delay.h>
#include "ec.h"
#ifdef __PRE_RAM__
static const int ec_cmd_reg = EC_SC;
static const int ec_data_reg = EC_DATA;
#else
static int ec_cmd_reg = EC_SC;
static int ec_data_reg = EC_DATA;
#endif
int send_ec_command(u8 command)
{
int timeout;
timeout = 0x7ff;
while ((inb(ec_cmd_reg) & EC_IBF) && --timeout) {
udelay(10);
if ((timeout & 0xff) == 0)
printk(BIOS_SPEW, ".");
}
if (!timeout) {
printk(BIOS_DEBUG, "Timeout while sending command 0x%02x to EC!\n",
command);
// return -1;
}
udelay(10);
outb(command, ec_cmd_reg);
return 0;
}
int send_ec_data(u8 data)
{
int timeout;
timeout = 0x7ff;
while ((inb(ec_cmd_reg) & EC_IBF) && --timeout) { // wait for IBF = 0
udelay(10);
if ((timeout & 0xff) == 0)
printk(BIOS_SPEW, ".");
}
if (!timeout) {
printk(BIOS_DEBUG, "Timeout while sending data 0x%02x to EC!\n",
data);
// return -1;
}
udelay(10);
outb(data, ec_data_reg);
return 0;
}
int send_ec_data_nowait(u8 data)
{
outb(data, ec_data_reg);
return 0;
}
u8 recv_ec_data(void)
{
int timeout;
u8 data;
timeout = 0x7fff;
while (--timeout) { // Wait for OBF = 1
if (inb(ec_cmd_reg) & EC_OBF) {
break;
}
udelay(10);
if ((timeout & 0xff) == 0)
printk(BIOS_SPEW, ".");
}
if (!timeout) {
printk(BIOS_DEBUG, "\nTimeout while receiving data from EC!\n");
// return -1;
}
udelay(10);
data = inb(ec_data_reg);
printk(BIOS_SPEW, "recv_ec_data: 0x%02x\n", data);
return data;
}
void ec_clear_out_queue(void)
{
int timeout = 0x7fff;
printk(BIOS_SPEW, "Clearing EC output queue...\n");
while (--timeout && (inb(ec_cmd_reg) & EC_OBF)) {
u8 data = inb(ec_data_reg);
printk(BIOS_SPEW, "Discarding a garbage byte: 0x%02x\n", data);
udelay(10);
}
if (!timeout)
printk(BIOS_ERR, "Timeout while clearing EC output queue!\n");
else
printk(BIOS_SPEW, "EC output queue has been cleared.\n");
}
u8 ec_read(u8 addr)
{
send_ec_command(0x80);
send_ec_data(addr);
return recv_ec_data();
}
int ec_write(u8 addr, u8 data)
{
send_ec_command(0x81);
send_ec_data(addr);
return send_ec_data(data);
}
u8 ec_status(void)
{
return inb(ec_cmd_reg);
}
u8 ec_query(void)
{
send_ec_command(0x84);
return recv_ec_data();
}
void ec_set_bit(u8 addr, u8 bit)
{
ec_write(addr, ec_read(addr) | (1 << bit));
}
void ec_clr_bit(u8 addr, u8 bit)
{
ec_write(addr, ec_read(addr) & ~(1 << bit));
}
#ifndef __PRE_RAM__
void ec_set_ports(u16 cmd_reg, u16 data_reg)
{
ec_cmd_reg = cmd_reg;
ec_data_reg = data_reg;
}
#endif
#if !defined(__SMM__) && !defined(__PRE_RAM__)
struct chip_operations ec_acpi_ops = {
CHIP_NAME("ACPI Embedded Controller")
};
#endif