chromeec: Add support for v3 commands on LPC

In order to talk to the PD controller with a passthru command
coreboot needs to be able to use v3 commands.

The command version is automatically detected based on the
advertized flags from the EC.

BUG=chrome-os-partner:30079
BRANCH=none
TEST=boot on samus EVT

Change-Id: I032eb185d80d5b68c82609910045e21d4521afcc
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
Original-Commit-Id: 4f664b22645f0def87a73e9255297b3edccf436e
Original-Change-Id: I94ace7741c9cd592921625fb793787247a5ca2aa
Original-Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/218902
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/9203
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
Duncan Laurie 2014-09-18 12:51:07 -07:00 committed by Patrick Georgi
parent fc0f5175fb
commit 60e6bf80db
1 changed files with 167 additions and 2 deletions

View File

@ -29,6 +29,30 @@
#include "ec.h"
#include "ec_commands.h"
static int google_chromeec_command_version(void)
{
u8 id1, id2, flags;
id1 = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID);
id2 = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1);
flags = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS);
if (id1 != 'E' || id2 != 'C') {
printk(BIOS_ERR, "Missing Chromium EC memory map.\n");
return -1;
}
if (flags & EC_HOST_CMD_FLAG_VERSION_3) {
return EC_HOST_CMD_FLAG_VERSION_3;
} else if (flags & EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED) {
return EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED;
} else {
printk(BIOS_ERR,
"Chromium EC command version unsupported\n");
return -1;
}
}
static int google_chromeec_wait_ready(u16 port)
{
u8 ec_status = inb(port);
@ -51,7 +75,119 @@ static int google_chromeec_wait_ready(u16 port)
return 0;
}
int google_chromeec_command(struct chromeec_command *cec_command)
static int google_chromeec_command_v3(struct chromeec_command *cec_command)
{
struct ec_host_request rq;
struct ec_host_response rs;
const u8 *d;
u8 *dout;
int csum = 0;
int i;
if (cec_command->cmd_size_in + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE) {
printk(BIOS_ERR, "EC cannot send %ld bytes\n",
cec_command->cmd_size_in + sizeof(rq));
return -1;
}
if (cec_command->cmd_size_out > EC_LPC_HOST_PACKET_SIZE) {
printk(BIOS_ERR, "EC cannot receive %d bytes\n",
cec_command->cmd_size_out);
return -1;
}
if (google_chromeec_wait_ready(EC_LPC_ADDR_HOST_CMD)) {
printk(BIOS_ERR, "Timeout waiting for EC start command %d!\n",
cec_command->cmd_code);
return -1;
}
/* Fill in request packet */
rq.struct_version = EC_HOST_REQUEST_VERSION;
rq.checksum = 0;
rq.command = cec_command->cmd_code |
EC_CMD_PASSTHRU_OFFSET(cec_command->cmd_dev_index);
rq.command_version = cec_command->cmd_version;
rq.reserved = 0;
rq.data_len = cec_command->cmd_size_in;
/* Copy data and start checksum */
for (i = 0, d = (const u8 *)cec_command->cmd_data_in;
i < cec_command->cmd_size_in; i++, d++) {
outb(*d, EC_LPC_ADDR_HOST_PACKET + sizeof(rq) + i);
csum += *d;
}
/* Finish checksum */
for (i = 0, d = (const u8 *)&rq; i < sizeof(rq); i++, d++)
csum += *d;
/* Write checksum field so the entire packet sums to 0 */
rq.checksum = (u8)(-csum);
/* Copy header */
for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++)
outb(*d, EC_LPC_ADDR_HOST_PACKET + i);
/* Start the command */
outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
if (google_chromeec_wait_ready(EC_LPC_ADDR_HOST_CMD)) {
printk(BIOS_ERR, "Timeout waiting for EC process command %d!\n",
cec_command->cmd_code);
return -1;
}
/* Check result */
cec_command->cmd_code = inb(EC_LPC_ADDR_HOST_DATA);
if (cec_command->cmd_code) {
printk(BIOS_ERR, "EC returned error result code %d\n",
cec_command->cmd_code);
return -i;
}
/* Read back response header and start checksum */
csum = 0;
for (i = 0, dout = (u8 *)&rs; i < sizeof(rs); i++, dout++) {
*dout = inb(EC_LPC_ADDR_HOST_PACKET + i);
csum += *dout;
}
if (rs.struct_version != EC_HOST_RESPONSE_VERSION) {
printk(BIOS_ERR, "EC response version mismatch (%d != %d)\n",
rs.struct_version, EC_HOST_RESPONSE_VERSION);
return -1;
}
if (rs.reserved) {
printk(BIOS_ERR, "EC response reserved is %d, should be 0\n",
rs.reserved);
return -1;
}
if (rs.data_len > cec_command->cmd_size_out) {
printk(BIOS_ERR, "EC returned too much data (%d > %d)\n",
rs.data_len, cec_command->cmd_size_out);
return -1;
}
/* Read back data and update checksum */
for (i = 0, dout = (uint8_t *)cec_command->cmd_data_out;
i < rs.data_len; i++, dout++) {
*dout = inb(EC_LPC_ADDR_HOST_PACKET + sizeof(rs) + i);
csum += *dout;
}
/* Verify checksum */
if ((u8)csum) {
printk(BIOS_ERR, "EC response has invalid checksum\n");
return -1;
}
return 0;
}
static int google_chromeec_command_v1(struct chromeec_command *cec_command)
{
struct ec_lpc_host_args args;
const u8 *d;
@ -134,7 +270,36 @@ int google_chromeec_command(struct chromeec_command *cec_command)
return 0;
}
#ifndef __PRE_RAM__
#ifdef __PRE_RAM__
int google_chromeec_command(struct chromeec_command *cec_command)
{
switch (google_chromeec_command_version()) {
case EC_HOST_CMD_FLAG_VERSION_3:
return google_chromeec_command_v3(cec_command);
case EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED:
return google_chromeec_command_v1(cec_command);
}
return -1;
}
#else /* !__PRE_RAM__ */
int google_chromeec_command(struct chromeec_command *cec_command)
{
static int command_version = 0;
if (command_version <= 0)
command_version = google_chromeec_command_version();
switch (command_version) {
case EC_HOST_CMD_FLAG_VERSION_3:
return google_chromeec_command_v3(cec_command);
case EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED:
return google_chromeec_command_v1(cec_command);
}
return -1;
}
#ifndef __SMM__
static void lpc_ec_init(struct device *dev)