Hung-Te Lin (hungte@chromium.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3074
-gerrit
commit 91cdf23e5449dd78f3cbf8bcef93ee64156b22b0 Author: Hung-Te Lin hungte@chromium.org Date: Mon Apr 15 18:27:24 2013 +0800
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 --- src/ec/google/chromeec/Kconfig | 20 ++++- src/ec/google/chromeec/Makefile.inc | 3 + src/ec/google/chromeec/ec.c | 9 ++ src/ec/google/chromeec/ec.h | 1 + src/ec/google/chromeec/ec_i2c.c | 159 ++++++++++++++++++++++++++++++++++++ src/mainboard/google/snow/Kconfig | 7 +- 6 files changed, 196 insertions(+), 3 deletions(-)
diff --git a/src/ec/google/chromeec/Kconfig b/src/ec/google/chromeec/Kconfig index ce96da7..c3f0499 100644 --- a/src/ec/google/chromeec/Kconfig +++ b/src/ec/google/chromeec/Kconfig @@ -10,9 +10,25 @@ config EC_GOOGLE_API_ROOT help Path to the ec API file (ec/ec_commands.h).
+config EC_GOOGLE_CHROMEEC_I2C + depends on EC_GOOGLE_CHROMEEC && !EC_GOOGLE_CHROMEEC_LPC + bool + 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 + depends on EC_GOOGLE_CHROMEEC && ARCH_X86 # Needs Plug-and-play. bool - default y if ARCH_X86 + default y help Google Chrome EC via LPC bus. diff --git a/src/ec/google/chromeec/Makefile.inc b/src/ec/google/chromeec/Makefile.inc index 5fb258f..5bc9268 100644 --- a/src/ec/google/chromeec/Makefile.inc +++ b/src/ec/google/chromeec/Makefile.inc @@ -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)) diff --git a/src/ec/google/chromeec/ec.c b/src/ec/google/chromeec/ec.c index 42115be..d94338f 100644 --- a/src/ec/google/chromeec/ec.c +++ b/src/ec/google/chromeec/ec.c @@ -33,6 +33,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; diff --git a/src/ec/google/chromeec/ec.h b/src/ec/google/chromeec/ec.h index f7512d7..3eb555c 100644 --- a/src/ec/google/chromeec/ec.h +++ b/src/ec/google/chromeec/ec.h @@ -33,6 +33,7 @@ u16 google_chromeec_get_board_version(void); void google_chromeec_init(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); diff --git a/src/ec/google/chromeec/ec_i2c.c b/src/ec/google/chromeec/ec_i2c.c new file mode 100644 index 0000000..a13dde6 --- /dev/null +++ b/src/ec/google/chromeec/ec_i2c.c @@ -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 diff --git a/src/mainboard/google/snow/Kconfig b/src/mainboard/google/snow/Kconfig index f070056..6d3d7c8 100644 --- a/src/mainboard/google/snow/Kconfig +++ b/src/mainboard/google/snow/Kconfig @@ -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? #