From 6bfbb33a64f95bcfdf46f8a35c342177886bb594 Mon Sep 17 00:00:00 2001 From: Hung-Te Lin Date: Mon, 15 Apr 2013 18:27:24 +0800 Subject: 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 Reviewed-on: http://review.coreboot.org/3074 Tested-by: build bot (Jenkins) Reviewed-by: Paul Menzel Reviewed-by: Ronald G. Minnich --- 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(-) create mode 100644 src/ec/google/chromeec/ec_i2c.c (limited to 'src') diff --git a/src/ec/google/chromeec/Kconfig b/src/ec/google/chromeec/Kconfig index ce96da7b99..c3f04997de 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 5fb258f1a9..5bc9268067 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 d8305292c1..e068bed6dc 100644 --- a/src/ec/google/chromeec/ec.c +++ b/src/ec/google/chromeec/ec.c @@ -34,6 +34,15 @@ #include "ec_commands.h" #include +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 c78560f973..a4c196056f 100644 --- a/src/ec/google/chromeec/ec.h +++ b/src/ec/google/chromeec/ec.h @@ -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); diff --git a/src/ec/google/chromeec/ec_i2c.c b/src/ec/google/chromeec/ec_i2c.c new file mode 100644 index 0000000000..a13dde6a7b --- /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 +#include +#include +#include +#include +#include +#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 f0700560af..6d3d7c8bce 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? # -- cgit v1.2.3