ec/google: Support Google's Chrome EC on I2C interface.

Google's Chrome EC can be installed on LPC or I2C bus, using different command
protocol.  This commit adds I2C support for devices like Google/Snow.

Note: I2C interface cannot be automatically probed so the bus and chip number
must be explicitly set.

Verified by booting Google/Snow, with following console output:
  Google Chrome EC: Hello got back 11223344 status (0)
  Google Chrome EC: version:
     ro: snow_v1.3.108-30f8374
     rw: snow_v1.3.128-e35f60e
    running image: 1

Change-Id: I8023eb96cf477755d277fd7991bdb7d9392f10f7
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: http://review.coreboot.org/3074
Tested-by: build bot (Jenkins)
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
Hung-Te Lin 2013-04-15 18:27:24 +08:00 committed by Ronald G. Minnich
parent 07e0f1bf1a
commit 6bfbb33a64
6 changed files with 197 additions and 4 deletions

View File

@ -10,9 +10,25 @@ config EC_GOOGLE_API_ROOT
help
Path to the ec API file (ec/ec_commands.h).
config EC_GOOGLE_CHROMEEC_LPC
depends on EC_GOOGLE_CHROMEEC
config EC_GOOGLE_CHROMEEC_I2C
depends on EC_GOOGLE_CHROMEEC && !EC_GOOGLE_CHROMEEC_LPC
bool
default y if ARCH_X86
default y
help
Google's Chrome EC via I2C bus.
config EC_GOOGLE_CHROMEEC_I2C_BUS
depends on EC_GOOGLE_CHROMEEC_I2C
hex "I2C bus for Google's Chrome EC"
config EC_GOOGLE_CHROMEEC_I2C_CHIP
depends on EC_GOOGLE_CHROMEEC_I2C
hex
default 0x1e
config EC_GOOGLE_CHROMEEC_LPC
depends on EC_GOOGLE_CHROMEEC && ARCH_X86 # Needs Plug-and-play.
bool
default y
help
Google Chrome EC via LPC bus.

View File

@ -1,8 +1,11 @@
ramstage-y += ec.c
ramstage-$(CONFIG_EC_GOOGLE_CHROMEEC_I2C) += ec_i2c.c
ramstage-$(CONFIG_EC_GOOGLE_CHROMEEC_LPC) += ec_lpc.c
smm-y += ec.c
smm-$(CONFIG_EC_GOOGLE_CHROMEEC_I2C) += ec_i2c.c
smm-$(CONFIG_EC_GOOGLE_CHROMEEC_LPC) += ec_lpc.c
romstage-y += ec.c
romstage-$(CONFIG_EC_GOOGLE_CHROMEEC_I2C) += ec_i2c.c
romstage-$(CONFIG_EC_GOOGLE_CHROMEEC_LPC) += ec_lpc.c
CFLAGS += -I $(call strip_quotes,$(CONFIG_EC_GOOGLE_API_ROOT))

View File

@ -34,6 +34,15 @@
#include "ec_commands.h"
#include <vendorcode/google/chromeos/chromeos.h>
uint8_t google_chromeec_calc_checksum(const uint8_t *data, int size)
{
int csum;
for (csum = 0; size > 0; data++, size--)
csum += *data;
return (uint8_t)(csum & 0xff);
}
int google_chromeec_kbbacklight(int percent)
{
struct chromeec_command cec_cmd;

View File

@ -32,6 +32,7 @@ int google_ec_running_ro(void);
u16 google_chromeec_get_board_version(void);
#endif
uint8_t google_chromeec_calc_checksum(const uint8_t *data, int size);
u32 google_chromeec_get_events_b(void);
int google_chromeec_kbbacklight(int percent);
void google_chromeec_post(u8 postcode);

View File

@ -0,0 +1,159 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <arch/io.h>
#include <console/console.h>
#include <delay.h>
#include <device/i2c.h>
#include <stdint.h>
#include <string.h>
#include "ec.h"
#include "ec_commands.h"
/* Command (host->device) format for I2C:
* uint8_t version, cmd, len, data[len], checksum;
*
* Response (device->host) format for I2C:
* uint8_t response, len, data[len], checksum;
*
* Note the location of checksum is different from LPC protocol.
*
* The length is 8 bit so maximum data size is 0xff.
* Any I2C command should fit in 0xff + 4 bytes, and max response length
* is 0xff + 3 bytes.
*/
#define MAX_I2C_DATA_SIZE (0xff)
typedef struct {
uint8_t version;
uint8_t command;
uint8_t length;
uint8_t data[MAX_I2C_DATA_SIZE + 1];
} EcCommandI2c;
typedef struct {
uint8_t response;
uint8_t length;
uint8_t data[MAX_I2C_DATA_SIZE + 1];
} EcResponseI2c;
static inline void i2c_dump(int bus, int chip, const uint8_t *data, size_t size)
{
#ifdef TRACE_CHROMEEC
printk(BIOS_INFO, "i2c: bus=%d, chip=%#x, size=%d, data: ", bus, chip,
size);
while (size-- > 0) {
printk(BIOS_INFO, "%02X ", *data++);
}
printk(BIOS_INFO, "\n");
#endif
}
static int ec_verify_checksum(const EcResponseI2c *resp)
{
size_t size = sizeof(*resp) - sizeof(resp->data) + resp->length;
uint8_t calculated = google_chromeec_calc_checksum(
(const uint8_t *)resp, size);
uint8_t received = resp->data[resp->length];
if (calculated != received) {
printk(BIOS_ERR, "%s: Unmatch (rx: %#02x, calc: %#02x)\n",
__func__, received, calculated);
return 0;
}
return 1;
}
static void ec_fill_checksum(EcCommandI2c *cmd)
{
size_t size = sizeof(*cmd) - sizeof(cmd->data) + cmd->length;
cmd->data[cmd->length] = google_chromeec_calc_checksum(
(const uint8_t *)cmd, size);
}
int google_chromeec_command(struct chromeec_command *cec_command)
{
EcCommandI2c cmd;
EcResponseI2c resp;
int bus = CONFIG_EC_GOOGLE_CHROMEEC_I2C_BUS;
int chip = CONFIG_EC_GOOGLE_CHROMEEC_I2C_CHIP;
size_t size_i2c_cmd = (sizeof(cmd) - sizeof(cmd.data) +
cec_command->cmd_size_in + 1),
size_i2c_resp = (sizeof(resp) - sizeof(resp.data) +
cec_command->cmd_size_out + 1);
if (cec_command->cmd_size_in > MAX_I2C_DATA_SIZE ||
cec_command->cmd_size_out > MAX_I2C_DATA_SIZE) {
printk(BIOS_ERR, "%s: Command data size too large (%d,%d)\n",
__func__, cec_command->cmd_size_in,
cec_command->cmd_size_out);
cec_command->cmd_code = EC_RES_INVALID_PARAM;
return 1;
}
/* Construct command. */
cmd.version = EC_CMD_VERSION0 + cec_command->cmd_version;
cmd.command = cec_command->cmd_code;
cmd.length = cec_command->cmd_size_in;
memcpy(cmd.data, cec_command->cmd_data_in, cmd.length);
ec_fill_checksum(&cmd);
/* Start I2C communication */
i2c_dump(bus, chip, (const uint8_t *)&cmd, size_i2c_cmd);
if (i2c_write(bus, chip, 0, 0, (uint8_t *)&cmd, size_i2c_cmd) != 0) {
printk(BIOS_ERR, "%s: Cannot complete write to i2c-%d:%#x\n",
__func__, bus, chip);
cec_command->cmd_code = EC_RES_ERROR;
return 1;
}
if (i2c_read(bus, chip, 0, 0, (uint8_t *)&resp, size_i2c_resp) != 0) {
printk(BIOS_ERR, "%s: Cannot complete read from i2c-%d:%#x\n",
__func__, bus, chip);
cec_command->cmd_code = EC_RES_ERROR;
return 1;
}
/* Verify and return response */
cec_command->cmd_code = resp.response;
if (resp.response != EC_RES_SUCCESS) {
printk(BIOS_DEBUG, "%s: Received bad result code %d\n",
__func__, (int)resp.response);
return 1;
}
if (resp.length > cec_command->cmd_size_out) {
printk(BIOS_ERR, "%s: Received len %#02x too large\n",
__func__, (int)resp.length);
cec_command->cmd_code = EC_RES_INVALID_RESPONSE;
return 1;
}
if (!ec_verify_checksum(&resp)) {
cec_command->cmd_code = EC_RES_INVALID_CHECKSUM;
return 1;
}
cec_command->cmd_size_out = resp.length;
memcpy(cec_command->cmd_data_out, resp.data, resp.length);
return 0;
}
#ifndef __PRE_RAM__
u8 google_chromeec_get_event(void)
{
printk(BIOS_ERR, "%s: Not supported.\n", __func__);
return 0;
}
#endif

View File

@ -24,7 +24,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy
select ARCH_ARMV7
select CPU_SAMSUNG_EXYNOS5
select HAVE_UART_MEMORY_MAPPED
# select EC_GOOGLE_CHROMEEC
select EC_GOOGLE_CHROMEEC
select EC_GOOGLE_CHROMEEC_I2C
select BOARD_ROMSIZE_KB_4096
select DRIVER_MAXIM_MAX77686
# select HAVE_ACPI_TABLES
@ -106,6 +107,10 @@ config CONSOLE_SERIAL_UART_ADDRESS
help
Map the UART names to the respective MMIO address.
config EC_GOOGLE_CHROMEEC_I2C_BUS
hex
default 4
#################################################################
# stuff from smdk5250.h #
# FIXME: can we move some of these to exynos5250's Kconfig? #