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:
parent
07e0f1bf1a
commit
6bfbb33a64
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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? #
|
||||
|
|
Loading…
Reference in New Issue