From patchwork Wed May 15 10:27:28 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hung-ying Tyan X-Patchwork-Id: 244043 X-Patchwork-Delegate: promsoft@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id D69642C00A3 for ; Wed, 15 May 2013 20:50:15 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 625664A02E; Wed, 15 May 2013 12:50:14 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cgAthte3AKuO; Wed, 15 May 2013 12:50:14 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 93CFB4A026; Wed, 15 May 2013 12:50:08 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4311F4A026 for ; Wed, 15 May 2013 12:50:03 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id HiAdfF4jpOrB for ; Wed, 15 May 2013 12:49:57 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-qa0-f73.google.com (mail-qa0-f73.google.com [209.85.216.73]) by theia.denx.de (Postfix) with ESMTPS id 4658A4A020 for ; Wed, 15 May 2013 12:49:51 +0200 (CEST) Received: by mail-qa0-f73.google.com with SMTP id bs12so178250qab.0 for ; Wed, 15 May 2013 03:49:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=6E44/t6LpUOcr9ez2WONS3hLEqyhl38VC8X6Z97a30k=; b=ijcvXMQ0ZKWWmHuh32UMHzW4BLoJZPWTFcrtHC65bJku2Nf1qTWP+OWaqACZVSaGkr 3DZ8gnzX/LV2fG5DwJv9HcRj1/elUNT0MQWemQo/8uR103FflYmIt+wFFS4rj2Lgv7v7 E8uwJ3aJdrL5HJBEcSCkof5hZf5dMk/+Nzqu7+3pYHkQsvQ6nEEwgXT8BIPaIPfDSAgA CM+UQy1jfxKFoXH37xdpKrxNexVnVClXlzOPVNsq5FQapIPhiSCILVbfVDisMpOCYbVI RO0eBflDIpodaXCbQnYCR4Ij6GvZKrSbrgq6SX+tZxl2JMiH4/PTGMTMLIPfqJi2loMd a2eA== X-Received: by 10.236.59.39 with SMTP id r27mr6062493yhc.47.1368613681903; Wed, 15 May 2013 03:28:01 -0700 (PDT) Received: from corp2gmr1-1.hot.corp.google.com (corp2gmr1-1.hot.corp.google.com [172.24.189.92]) by gmr-mx.google.com with ESMTPS id u47si128635yhe.0.2013.05.15.03.28.01 for (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Wed, 15 May 2013 03:28:01 -0700 (PDT) Received: from tyanh-z620.tpe.corp.google.com (tyanh-z620.tpe.corp.google.com [172.30.210.193]) by corp2gmr1-1.hot.corp.google.com (Postfix) with ESMTP id D253831C23A; Wed, 15 May 2013 03:28:00 -0700 (PDT) Received: by tyanh-z620.tpe.corp.google.com (Postfix, from userid 30259) id B3F9AE091F; Wed, 15 May 2013 18:27:59 +0800 (CST) From: Hung-ying Tyan To: U-Boot Mailing List Date: Wed, 15 May 2013 18:27:28 +0800 Message-Id: <1368613654-15229-2-git-send-email-tyanh@chromium.org> X-Mailer: git-send-email 1.8.2.1 In-Reply-To: <1368613654-15229-1-git-send-email-tyanh@chromium.org> References: <1368613654-15229-1-git-send-email-tyanh@chromium.org> X-Gm-Message-State: ALoCoQlHcWrWngtDW/MF+uOOSV+I83K3s2d7SFyJYrZkR/eFsM8X9GINHmuWGWmfhU+7dYD1zKN7uqwIzjn9Y3+IxRPn+3fS+ETCjtn2LnxOzdZbznrJoJCLtdLz0BEL8Kd71BsrAGz0q5yJiYmtVEEv/DcoVtlS2A3i7jmQpU7UiT9NiVwgPTGM5X4DzrnOIaenpgmV/DxZ Cc: Vincent Palatin , u-boot-review@google.com, Gabe Black , Bill Richardson , Randall Spangler , Sean Paul , Tom Rini , Louis Yung-Chieh Lo Subject: [U-Boot] [PATCH v5 1/7] cros: add cros_ec driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This patch adds the cros_ec driver that implements the protocol for communicating with Google's ChromeOS embedded controller. Signed-off-by: Bernie Thompson Signed-off-by: Bill Richardson Signed-off-by: Che-Liang Chiou Signed-off-by: Doug Anderson Signed-off-by: Gabe Black Signed-off-by: Hung-ying Tyan Signed-off-by: Louis Yung-Chieh Lo Signed-off-by: Randall Spangler Signed-off-by: Sean Paul Signed-off-by: Simon Glass Signed-off-by: Vincent Palatin Acked-by: Simon Glass Tested-by: Simon Glass --- Changes in v5: None Changes in v4: - Removed unrelated exynos-spi.txt. - Moved cros-ec-keyb.txt to the cros-ec-keyb patch. Changes in v3: None Changes in v2: - Fixed warnings of exceeding 80 chars in a line. - Added commit message. - Dropped the period from commit subject. doc/device-tree-bindings/misc/cros-ec.txt | 38 + drivers/misc/Makefile | 1 + drivers/misc/cros_ec.c | 1304 ++++++++++++++++++++++++++ include/cros_ec.h | 449 +++++++++ include/cros_ec_message.h | 44 + include/ec_commands.h | 1440 +++++++++++++++++++++++++++++ include/fdtdec.h | 1 + lib/fdtdec.c | 1 + 8 files changed, 3278 insertions(+) create mode 100644 doc/device-tree-bindings/misc/cros-ec.txt create mode 100644 drivers/misc/cros_ec.c create mode 100644 include/cros_ec.h create mode 100644 include/cros_ec_message.h create mode 100644 include/ec_commands.h diff --git a/doc/device-tree-bindings/misc/cros-ec.txt b/doc/device-tree-bindings/misc/cros-ec.txt new file mode 100644 index 0000000..07ea7cd --- /dev/null +++ b/doc/device-tree-bindings/misc/cros-ec.txt @@ -0,0 +1,38 @@ +Chrome OS CROS_EC Binding +====================== + +The device tree node which describes the operation of the CROS_EC interface +is as follows: + +Required properties : +- compatible = "google,cros-ec" + +Optional properties : +- spi-max-frequency : Sets the maximum frequency (in Hz) for SPI bus + operation +- i2c-max-frequency : Sets the maximum frequency (in Hz) for I2C bus + operation +- ec-interrupt : Selects the EC interrupt, defined as a GPIO according + to the platform +- optimise-flash-write : Boolean property - if present then flash blocks + containing all 0xff will not be written, since we assume that the EC + uses that pattern for erased blocks + +The CROS_EC node should appear as a subnode of the interrupt that connects it +to the EC (e.g. i2c, spi, lpc). The reg property (as usual) will indicate +the unit address on that bus. + + +Example +======= + + spi@131b0000 { + cros-ec@0 { + reg = <0>; + compatible = "google,cros-ec"; + spi-max-frequency = <5000000>; + ec-interrupt = <&gpio 174 1>; + optimise-flash-write; + status = "disabled"; + }; + }; diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 8cdc3b6..33fe822 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_DS4510) += ds4510.o COBJS-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o COBJS-$(CONFIG_GPIO_LED) += gpio_led.o COBJS-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o +COBJS-$(CONFIG_CROS_EC) += cros_ec.o COBJS-$(CONFIG_NS87308) += ns87308.o COBJS-$(CONFIG_PDSP188x) += pdsp188x.o COBJS-$(CONFIG_STATUS_LED) += status_led.o diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c new file mode 100644 index 0000000..6e774d9 --- /dev/null +++ b/drivers/misc/cros_ec.c @@ -0,0 +1,1304 @@ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * 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; either version 2 of + * the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, #b) +#else +#define debug_trace(fmt, b...) +#endif + +enum { + /* Timeout waiting for a flash erase command to complete */ + CROS_EC_CMD_TIMEOUT_MS = 5000, + /* Timeout waiting for a synchronous hash to be recomputed */ + CROS_EC_CMD_HASH_TIMEOUT_MS = 2000, +}; + +static struct cros_ec_dev static_dev, *last_dev; + +DECLARE_GLOBAL_DATA_PTR; + +/* Note: depends on enum ec_current_image */ +static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; + +void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len) +{ +#ifdef DEBUG + int i; + + printf("%s: ", name); + if (cmd != -1) + printf("cmd=%#x: ", cmd); + for (i = 0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +#endif +} + +/* + * Calculate a simple 8-bit checksum of a data block + * + * @param data Data block to checksum + * @param size Size of data block in bytes + * @return checksum value (0 to 255) + */ +int cros_ec_calc_checksum(const uint8_t *data, int size) +{ + int csum, i; + + for (i = csum = 0; i < size; i++) + csum += data[i]; + return csum & 0xff; +} + +static int send_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const void *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int ret; + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + ret = cros_ec_spi_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case CROS_EC_IF_I2C: + ret = cros_ec_i2c_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case CROS_EC_IF_LPC: + ret = cros_ec_lpc_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif + case CROS_EC_IF_NONE: + default: + ret = -1; + } + + return ret; +} + +/** + * Send a command to the CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Response data (may be NULL If din_len=0). + * If not NULL, it will be updated to point to the data + * and will always be double word aligned (64-bits) + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, + int cmd_version, const void *dout, int dout_len, uint8_t **dinp, + int din_len) +{ + uint8_t *din; + int len; + + if (cmd_version != 0 && !dev->cmd_version_is_supported) { + debug("%s: Command version >0 unsupported\n", __func__); + return -1; + } + len = send_command(dev, cmd, cmd_version, dout, dout_len, + &din, din_len); + + /* If the command doesn't complete, wait a while */ + if (len == -EC_RES_IN_PROGRESS) { + struct ec_response_get_comms_status *resp; + ulong start; + + /* Wait for command to complete */ + start = get_timer(0); + do { + int ret; + + mdelay(50); /* Insert some reasonable delay */ + ret = send_command(dev, EC_CMD_GET_COMMS_STATUS, 0, + NULL, 0, + (uint8_t **)&resp, sizeof(*resp)); + if (ret < 0) + return ret; + + if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) { + debug("%s: Command %#02x timeout\n", + __func__, cmd); + return -EC_RES_TIMEOUT; + } + } while (resp->flags & EC_COMMS_STATUS_PROCESSING); + + /* OK it completed, so read the status response */ + /* not sure why it was 0 for the last argument */ + len = send_command(dev, EC_CMD_RESEND_RESPONSE, 0, + NULL, 0, &din, din_len); + } + + debug("%s: len=%d, dinp=%p, *dinp=%p\n", __func__, len, dinp, *dinp); + if (dinp) { + /* If we have any data to return, it must be 64bit-aligned */ + assert(len <= 0 || !((uintptr_t)din & 7)); + *dinp = din; + } + + return len; +} + +/** + * Send a command to the CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param din Response data (may be NULL If din_len=0). + * It not NULL, it is a place for ec_command() to copy the + * data to. + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const void *dout, int dout_len, + void *din, int din_len) +{ + uint8_t *in_buffer; + int len; + + assert((din_len == 0) || din); + len = ec_command_inptr(dev, cmd, cmd_version, dout, dout_len, + &in_buffer, din_len); + if (len > 0) { + /* + * If we were asked to put it somewhere, do so, otherwise just + * disregard the result. + */ + if (din && in_buffer) { + assert(len <= din_len); + memmove(din, in_buffer, len); + } + } + return len; +} + +int cros_ec_scan_keyboard(struct cros_ec_dev *dev, struct mbkp_keyscan *scan) +{ + if (ec_command(dev, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan, + sizeof(scan->data)) < sizeof(scan->data)) + return -1; + + return 0; +} + +int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen) +{ + struct ec_response_get_version *r; + + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)) < sizeof(*r)) + return -1; + + if (maxlen > sizeof(r->version_string_ro)) + maxlen = sizeof(r->version_string_ro); + + switch (r->current_image) { + case EC_IMAGE_RO: + memcpy(id, r->version_string_ro, maxlen); + break; + case EC_IMAGE_RW: + memcpy(id, r->version_string_rw, maxlen); + break; + default: + return -1; + } + + id[maxlen - 1] = '\0'; + return 0; +} + +int cros_ec_read_version(struct cros_ec_dev *dev, + struct ec_response_get_version **versionp) +{ + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)versionp, sizeof(**versionp)) + < sizeof(**versionp)) + return -1; + + return 0; +} + +int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp) +{ + if (ec_command_inptr(dev, EC_CMD_GET_BUILD_INFO, 0, NULL, 0, + (uint8_t **)strp, EC_HOST_PARAM_SIZE) < 0) + return -1; + + return 0; +} + +int cros_ec_read_current_image(struct cros_ec_dev *dev, + enum ec_current_image *image) +{ + struct ec_response_get_version *r; + + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)) < sizeof(*r)) + return -1; + + *image = r->current_image; + return 0; +} + +static int cros_ec_wait_on_hash_done(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash) +{ + struct ec_params_vboot_hash p; + ulong start; + + start = get_timer(0); + while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) { + mdelay(50); /* Insert some reasonable delay */ + + p.cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) { + debug("%s: EC_VBOOT_HASH_GET timeout\n", __func__); + return -EC_RES_TIMEOUT; + } + } + return 0; +} + + +int cros_ec_read_hash(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash) +{ + struct ec_params_vboot_hash p; + int rv; + + p.cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + /* If the EC is busy calculating the hash, fidget until it's done. */ + rv = cros_ec_wait_on_hash_done(dev, hash); + if (rv) + return rv; + + /* If the hash is valid, we're done. Otherwise, we have to kick it off + * again and wait for it to complete. Note that we explicitly assume + * that hashing zero bytes is always wrong, even though that would + * produce a valid hash value. */ + if (hash->status == EC_VBOOT_HASH_STATUS_DONE && hash->size) + return 0; + + debug("%s: No valid hash (status=%d size=%d). Compute one...\n", + __func__, hash->status, hash->size); + + p.cmd = EC_VBOOT_HASH_RECALC; + p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; + p.nonce_size = 0; + p.offset = EC_VBOOT_HASH_OFFSET_RW; + + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + rv = cros_ec_wait_on_hash_done(dev, hash); + if (rv) + return rv; + + debug("%s: hash done\n", __func__); + + return 0; +} + +static int cros_ec_invalidate_hash(struct cros_ec_dev *dev) +{ + struct ec_params_vboot_hash p; + struct ec_response_vboot_hash *hash; + + /* We don't have an explict command for the EC to discard its current + * hash value, so we'll just tell it to calculate one that we know is + * wrong (we claim that hashing zero bytes is always invalid). + */ + p.cmd = EC_VBOOT_HASH_RECALC; + p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; + p.nonce_size = 0; + p.offset = 0; + p.size = 0; + + debug("%s:\n", __func__); + + if (ec_command_inptr(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + (uint8_t **)&hash, sizeof(*hash)) < 0) + return -1; + + /* No need to wait for it to finish */ + return 0; +} + +int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, + uint8_t flags) +{ + struct ec_params_reboot_ec p; + + p.cmd = cmd; + p.flags = flags; + + if (ec_command_inptr(dev, EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0) + < 0) + return -1; + + if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) { + /* + * EC reboot will take place immediately so delay to allow it + * to complete. Note that some reboot types (EC_REBOOT_COLD) + * will reboot the AP as well, in which case we won't actually + * get to this point. + */ + /* + * TODO(rspangler@chromium.org): Would be nice if we had a + * better way to determine when the reboot is complete. Could + * we poll a memory-mapped LPC value? + */ + udelay(50000); + } + + return 0; +} + +int cros_ec_interrupt_pending(struct cros_ec_dev *dev) +{ + /* no interrupt support : always poll */ + if (!fdt_gpio_isvalid(&dev->ec_int)) + return 1; + + return !gpio_get_value(dev->ec_int.gpio); +} + +int cros_ec_info(struct cros_ec_dev *dev, struct ec_response_cros_ec_info *info) +{ + if (ec_command(dev, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info, + sizeof(*info)) < sizeof(*info)) + return -1; + + return 0; +} + +int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr) +{ + struct ec_response_host_event_mask *resp; + + /* + * Use the B copy of the event flags, because the main copy is already + * used by ACPI/SMI. + */ + if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) + return -1; + + if (resp->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) + return -1; + + *events_ptr = resp->mask; + return 0; +} + +int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events) +{ + struct ec_params_host_event_mask params; + + params.mask = events; + + /* + * Use the B copy of the event flags, so it affects the data returned + * by cros_ec_get_host_events(). + */ + if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_CLEAR_B, 0, + ¶ms, sizeof(params), NULL, 0) < 0) + return -1; + + return 0; +} + +int cros_ec_flash_protect(struct cros_ec_dev *dev, + uint32_t set_mask, uint32_t set_flags, + struct ec_response_flash_protect *resp) +{ + struct ec_params_flash_protect params; + + params.mask = set_mask; + params.flags = set_flags; + + if (ec_command(dev, EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT, + ¶ms, sizeof(params), + resp, sizeof(*resp)) < sizeof(*resp)) + return -1; + + return 0; +} + +static int cros_ec_check_version(struct cros_ec_dev *dev) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + +#ifdef CONFIG_CROS_EC_LPC + /* LPC has its own way of doing this */ + if (dev->interface == CROS_EC_IF_LPC) + return cros_ec_lpc_check_version(dev); +#endif + + /* + * TODO(sjg@chromium.org). + * There is a strange oddity here with the EC. We could just ignore + * the response, i.e. pass the last two parameters as NULL and 0. + * In this case we won't read back very many bytes from the EC. + * On the I2C bus the EC gets upset about this and will try to send + * the bytes anyway. This means that we will have to wait for that + * to complete before continuing with a new EC command. + * + * This problem is probably unique to the I2C bus. + * + * So for now, just read all the data anyway. + */ + dev->cmd_version_is_supported = 1; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) > 0) { + /* It appears to understand new version commands */ + dev->cmd_version_is_supported = 1; + } else { + dev->cmd_version_is_supported = 0; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, + sizeof(req), (uint8_t **)&resp, + sizeof(*resp)) < 0) { + debug("%s: Failed both old and new command style\n", + __func__); + return -1; + } + } + + return 0; +} + +int cros_ec_test(struct cros_ec_dev *dev) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + + req.in_data = 0x12345678; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) { + printf("ec_command_inptr() returned error\n"); + return -1; + } + if (resp->out_data != req.in_data + 0x01020304) { + printf("Received invalid handshake %x\n", resp->out_data); + return -1; + } + + return 0; +} + +int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, + uint32_t *offset, uint32_t *size) +{ + struct ec_params_flash_region_info p; + struct ec_response_flash_region_info *r; + int ret; + + p.region = region; + ret = ec_command_inptr(dev, EC_CMD_FLASH_REGION_INFO, + EC_VER_FLASH_REGION_INFO, + &p, sizeof(p), (uint8_t **)&r, sizeof(*r)); + if (ret != sizeof(*r)) + return -1; + + if (offset) + *offset = r->offset; + if (size) + *size = r->size; + + return 0; +} + +int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, uint32_t size) +{ + struct ec_params_flash_erase p; + + p.offset = offset; + p.size = size; + return ec_command_inptr(dev, EC_CMD_FLASH_ERASE, 0, &p, sizeof(p), + NULL, 0); +} + +/** + * Write a single block to the flash + * + * Write a block of data to the EC flash. The size must not exceed the flash + * write block size which you can obtain from cros_ec_flash_write_burst_size(). + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to write for a particular region. + * + * Attempting to write to the region where the EC is currently running from + * will result in an error. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to write + * @param offset Offset within flash to write to. + * @param size Number of bytes to write + * @return 0 if ok, -1 on error + */ +static int cros_ec_flash_write_block(struct cros_ec_dev *dev, + const uint8_t *data, uint32_t offset, uint32_t size) +{ + struct ec_params_flash_write p; + + p.offset = offset; + p.size = size; + assert(data && p.size <= sizeof(p.data)); + memcpy(p.data, data, p.size); + + return ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0, + &p, sizeof(p), NULL, 0) >= 0 ? 0 : -1; +} + +/** + * Return optimal flash write burst size + */ +static int cros_ec_flash_write_burst_size(struct cros_ec_dev *dev) +{ + struct ec_params_flash_write p; + return sizeof(p.data); +} + +/** + * Check if a block of data is erased (all 0xff) + * + * This function is useful when dealing with flash, for checking whether a + * data block is erased and thus does not need to be programmed. + * + * @param data Pointer to data to check (must be word-aligned) + * @param size Number of bytes to check (must be word-aligned) + * @return 0 if erased, non-zero if any word is not erased + */ +static int cros_ec_data_is_erased(const uint32_t *data, int size) +{ + assert(!(size & 3)); + size /= sizeof(uint32_t); + for (; size > 0; size -= 4, data++) + if (*data != -1U) + return 0; + + return 1; +} + +int cros_ec_flash_write(struct cros_ec_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size) +{ + uint32_t burst = cros_ec_flash_write_burst_size(dev); + uint32_t end, off; + int ret; + + /* + * TODO: round up to the nearest multiple of write size. Can get away + * without that on link right now because its write size is 4 bytes. + */ + end = offset + size; + for (off = offset; off < end; off += burst, data += burst) { + uint32_t todo; + + /* If the data is empty, there is no point in programming it */ + todo = min(end - off, burst); + if (dev->optimise_flash_write && + cros_ec_data_is_erased((uint32_t *)data, todo)) + continue; + + ret = cros_ec_flash_write_block(dev, data, off, todo); + if (ret) + return ret; + } + + return 0; +} + +/** + * Read a single block from the flash + * + * Read a block of data from the EC flash. The size must not exceed the flash + * write block size which you can obtain from cros_ec_flash_write_burst_size(). + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to read for a particular region. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to read into + * @param offset Offset within flash to read from + * @param size Number of bytes to read + * @return 0 if ok, -1 on error + */ +static int cros_ec_flash_read_block(struct cros_ec_dev *dev, uint8_t *data, + uint32_t offset, uint32_t size) +{ + struct ec_params_flash_read p; + + p.offset = offset; + p.size = size; + + return ec_command(dev, EC_CMD_FLASH_READ, 0, + &p, sizeof(p), data, size) >= 0 ? 0 : -1; +} + +int cros_ec_flash_read(struct cros_ec_dev *dev, uint8_t *data, uint32_t offset, + uint32_t size) +{ + uint32_t burst = cros_ec_flash_write_burst_size(dev); + uint32_t end, off; + int ret; + + end = offset + size; + for (off = offset; off < end; off += burst, data += burst) { + ret = cros_ec_flash_read_block(dev, data, off, + min(end - off, burst)); + if (ret) + return ret; + } + + return 0; +} + +int cros_ec_flash_update_rw(struct cros_ec_dev *dev, + const uint8_t *image, int image_size) +{ + uint32_t rw_offset, rw_size; + int ret; + + if (cros_ec_flash_offset(dev, EC_FLASH_REGION_RW, &rw_offset, &rw_size)) + return -1; + if (image_size > rw_size) + return -1; + + /* Invalidate the existing hash, just in case the AP reboots + * unexpectedly during the update. If that happened, the EC RW firmware + * would be invalid, but the EC would still have the original hash. + */ + ret = cros_ec_invalidate_hash(dev); + if (ret) + return ret; + + /* + * Erase the entire RW section, so that the EC doesn't see any garbage + * past the new image if it's smaller than the current image. + * + * TODO: could optimize this to erase just the current image, since + * presumably everything past that is 0xff's. But would still need to + * round up to the nearest multiple of erase size. + */ + ret = cros_ec_flash_erase(dev, rw_offset, rw_size); + if (ret) + return ret; + + /* Write the image */ + ret = cros_ec_flash_write(dev, image, rw_offset, image_size); + if (ret) + return ret; + + return 0; +} + +int cros_ec_read_vbnvcontext(struct cros_ec_dev *dev, uint8_t *block) +{ + struct ec_params_vbnvcontext p; + int len; + + p.op = EC_VBNV_CONTEXT_OP_READ; + + len = ec_command(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, + &p, sizeof(p), block, EC_VBNV_BLOCK_SIZE); + if (len < EC_VBNV_BLOCK_SIZE) + return -1; + + return 0; +} + +int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block) +{ + struct ec_params_vbnvcontext p; + int len; + + p.op = EC_VBNV_CONTEXT_OP_WRITE; + memcpy(p.block, block, sizeof(p.block)); + + len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, + &p, sizeof(p), NULL, 0); + if (len < 0) + return -1; + + return 0; +} + +int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state) +{ + struct ec_params_ldo_set params; + + params.index = index; + params.state = state; + + if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, + ¶ms, sizeof(params), + NULL, 0)) + return -1; + + return 0; +} + +int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state) +{ + struct ec_params_ldo_get params; + struct ec_response_ldo_get *resp; + + params.index = index; + + if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, + ¶ms, sizeof(params), + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) + return -1; + + *state = resp->state; + + return 0; +} + +/** + * Decode MBKP details from the device tree and allocate a suitable device. + * + * @param blob Device tree blob + * @param node Node to decode from + * @param devp Returns a pointer to the new allocated device + * @return 0 if ok, -1 on error + */ +static int cros_ec_decode_fdt(const void *blob, int node, + struct cros_ec_dev **devp) +{ + enum fdt_compat_id compat; + struct cros_ec_dev *dev; + int parent; + + /* See what type of parent we are inside (this is expensive) */ + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + dev = &static_dev; + dev->node = node; + dev->parent_node = parent; + + compat = fdtdec_lookup(blob, parent); + switch (compat) { +#ifdef CONFIG_CROS_EC_SPI + case COMPAT_SAMSUNG_EXYNOS_SPI: + dev->interface = CROS_EC_IF_SPI; + if (cros_ec_spi_decode_fdt(dev, blob)) + return -1; + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case COMPAT_SAMSUNG_S3C2440_I2C: + dev->interface = CROS_EC_IF_I2C; + if (cros_ec_i2c_decode_fdt(dev, blob)) + return -1; + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case COMPAT_INTEL_LPC: + dev->interface = CROS_EC_IF_LPC; + break; +#endif + default: + debug("%s: Unknown compat id %d\n", __func__, compat); + return -1; + } + + fdtdec_decode_gpio(blob, node, "ec-interrupt", &dev->ec_int); + dev->optimise_flash_write = fdtdec_get_bool(blob, node, + "optimise-flash-write"); + *devp = dev; + + return 0; +} + +int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp) +{ + char id[MSG_BYTES]; + struct cros_ec_dev *dev; + int node = 0; + + *cros_ecp = NULL; + do { + node = fdtdec_next_compatible(blob, node, + COMPAT_GOOGLE_CROS_EC); + if (node < 0) { + debug("%s: Node not found\n", __func__); + return 0; + } + } while (!fdtdec_get_is_enabled(blob, node)); + + if (cros_ec_decode_fdt(blob, node, &dev)) { + debug("%s: Failed to decode device.\n", __func__); + return -CROS_EC_ERR_FDT_DECODE; + } + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + if (cros_ec_spi_init(dev, blob)) { + debug("%s: Could not setup SPI interface\n", __func__); + return -CROS_EC_ERR_DEV_INIT; + } + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case CROS_EC_IF_I2C: + if (cros_ec_i2c_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case CROS_EC_IF_LPC: + if (cros_ec_lpc_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif + case CROS_EC_IF_NONE: + default: + return 0; + } + + /* we will poll the EC interrupt line */ + fdtdec_setup_gpio(&dev->ec_int); + if (fdt_gpio_isvalid(&dev->ec_int)) + gpio_direction_input(dev->ec_int.gpio); + + if (cros_ec_check_version(dev)) { + debug("%s: Could not detect CROS-EC version\n", __func__); + return -CROS_EC_ERR_CHECK_VERSION; + } + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return -CROS_EC_ERR_READ_ID; + } + + /* Remember this device for use by the cros_ec command */ + last_dev = *cros_ecp = dev; + debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id); + + return 0; +} + +#ifdef CONFIG_CMD_CROS_EC +int cros_ec_decode_region(int argc, char * const argv[]) +{ + if (argc > 0) { + if (0 == strcmp(*argv, "rw")) + return EC_FLASH_REGION_RW; + else if (0 == strcmp(*argv, "ro")) + return EC_FLASH_REGION_RO; + + debug("%s: Invalid region '%s'\n", __func__, *argv); + } else { + debug("%s: Missing region parameter\n", __func__); + } + + return -1; +} + +/** + * Perform a flash read or write command + * + * @param dev CROS-EC device to read/write + * @param is_write 1 do to a write, 0 to do a read + * @param argc Number of arguments + * @param argv Arguments (2 is region, 3 is address) + * @return 0 for ok, 1 for a usage error or -ve for ec command error + * (negative EC_RES_...) + */ +static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc, + char * const argv[]) +{ + uint32_t offset, size = -1U, region_size; + unsigned long addr; + char *endp; + int region; + int ret; + + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == -1) + return 1; + if (argc < 4) + return 1; + addr = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return 1; + if (argc > 4) { + size = simple_strtoul(argv[4], &endp, 16); + if (*argv[4] == 0 || *endp != 0) + return 1; + } + + ret = cros_ec_flash_offset(dev, region, &offset, ®ion_size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + return ret; + } + if (size == -1U) + size = region_size; + + ret = is_write ? + cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) : + cros_ec_flash_read(dev, (uint8_t *)addr, offset, size); + if (ret) { + debug("%s: Could not %s region\n", __func__, + is_write ? "write" : "read"); + return ret; + } + + return 0; +} + +static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct cros_ec_dev *dev = last_dev; + const char *cmd; + int ret = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = argv[1]; + if (0 == strcmp("init", cmd)) { + ret = cros_ec_init(gd->fdt_blob, &dev); + if (ret) { + printf("Could not init cros_ec device (err %d)\n", ret); + return 1; + } + return 0; + } + + /* Just use the last allocated device; there should be only one */ + if (!last_dev) { + printf("No CROS-EC device available\n"); + return 1; + } + if (0 == strcmp("id", cmd)) { + char id[MSG_BYTES]; + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return 1; + } + printf("%s\n", id); + } else if (0 == strcmp("info", cmd)) { + struct ec_response_cros_ec_info info; + + if (cros_ec_info(dev, &info)) { + debug("%s: Could not read KBC info\n", __func__); + return 1; + } + printf("rows = %u\n", info.rows); + printf("cols = %u\n", info.cols); + printf("switches = %#x\n", info.switches); + } else if (0 == strcmp("curimage", cmd)) { + enum ec_current_image image; + + if (cros_ec_read_current_image(dev, &image)) { + debug("%s: Could not read KBC image\n", __func__); + return 1; + } + printf("%d\n", image); + } else if (0 == strcmp("hash", cmd)) { + struct ec_response_vboot_hash hash; + int i; + + if (cros_ec_read_hash(dev, &hash)) { + debug("%s: Could not read KBC hash\n", __func__); + return 1; + } + + if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256) + printf("type: SHA-256\n"); + else + printf("type: %d\n", hash.hash_type); + + printf("offset: 0x%08x\n", hash.offset); + printf("size: 0x%08x\n", hash.size); + + printf("digest: "); + for (i = 0; i < hash.digest_size; i++) + printf("%02x", hash.hash_digest[i]); + printf("\n"); + } else if (0 == strcmp("reboot", cmd)) { + int region; + enum ec_reboot_cmd cmd; + + if (argc >= 3 && !strcmp(argv[2], "cold")) + cmd = EC_REBOOT_COLD; + else { + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == EC_FLASH_REGION_RO) + cmd = EC_REBOOT_JUMP_RO; + else if (region == EC_FLASH_REGION_RW) + cmd = EC_REBOOT_JUMP_RW; + else + return CMD_RET_USAGE; + } + + if (cros_ec_reboot(dev, cmd, 0)) { + debug("%s: Could not reboot KBC\n", __func__); + return 1; + } + } else if (0 == strcmp("events", cmd)) { + uint32_t events; + + if (cros_ec_get_host_events(dev, &events)) { + debug("%s: Could not read host events\n", __func__); + return 1; + } + printf("0x%08x\n", events); + } else if (0 == strcmp("clrevents", cmd)) { + uint32_t events = 0x7fffffff; + + if (argc >= 3) + events = simple_strtol(argv[2], NULL, 0); + + if (cros_ec_clear_host_events(dev, events)) { + debug("%s: Could not clear host events\n", __func__); + return 1; + } + } else if (0 == strcmp("read", cmd)) { + ret = do_read_write(dev, 0, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("write", cmd)) { + ret = do_read_write(dev, 1, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("erase", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + if (cros_ec_flash_offset(dev, region, &offset, &size)) { + debug("%s: Could not read region info\n", __func__); + ret = -1; + } else { + ret = cros_ec_flash_erase(dev, offset, size); + if (ret) { + debug("%s: Could not erase region\n", + __func__); + } + } + } else if (0 == strcmp("regioninfo", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + ret = cros_ec_flash_offset(dev, region, &offset, &size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + } else { + printf("Region: %s\n", region == EC_FLASH_REGION_RO ? + "RO" : "RW"); + printf("Offset: %x\n", offset); + printf("Size: %x\n", size); + } + } else if (0 == strcmp("vbnvcontext", cmd)) { + uint8_t block[EC_VBNV_BLOCK_SIZE]; + char buf[3]; + int i, len; + unsigned long result; + + if (argc <= 2) { + ret = cros_ec_read_vbnvcontext(dev, block); + if (!ret) { + printf("vbnv_block: "); + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) + printf("%02x", block[i]); + putc('\n'); + } + } else { + /* + * TODO(clchiou): Move this to a utility function as + * cmd_spi might want to call it. + */ + memset(block, 0, EC_VBNV_BLOCK_SIZE); + len = strlen(argv[2]); + buf[2] = '\0'; + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) { + if (i * 2 >= len) + break; + buf[0] = argv[2][i * 2]; + if (i * 2 + 1 >= len) + buf[1] = '0'; + else + buf[1] = argv[2][i * 2 + 1]; + strict_strtoul(buf, 16, &result); + block[i] = result; + } + ret = cros_ec_write_vbnvcontext(dev, block); + } + if (ret) { + debug("%s: Could not %s VbNvContext\n", __func__, + argc <= 2 ? "read" : "write"); + } + } else if (0 == strcmp("test", cmd)) { + int result = cros_ec_test(dev); + + if (result) + printf("Test failed with error %d\n", result); + else + puts("Test passed\n"); + } else if (0 == strcmp("version", cmd)) { + struct ec_response_get_version *p; + char *build_string; + + ret = cros_ec_read_version(dev, &p); + if (!ret) { + /* Print versions */ + printf("RO version: %1.*s\n", + sizeof(p->version_string_ro), + p->version_string_ro); + printf("RW version: %1.*s\n", + sizeof(p->version_string_rw), + p->version_string_rw); + printf("Firmware copy: %s\n", + (p->current_image < + ARRAY_SIZE(ec_current_image_name) ? + ec_current_image_name[p->current_image] : + "?")); + ret = cros_ec_read_build_info(dev, &build_string); + if (!ret) + printf("Build info: %s\n", build_string); + } + } else if (0 == strcmp("ldo", cmd)) { + uint8_t index, state; + char *endp; + + if (argc < 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], &endp, 10); + if (*argv[2] == 0 || *endp != 0) + return CMD_RET_USAGE; + if (argc > 3) { + state = simple_strtoul(argv[3], &endp, 10); + if (*argv[3] == 0 || *endp != 0) + return CMD_RET_USAGE; + ret = cros_ec_set_ldo(dev, index, state); + } else { + ret = cros_ec_get_ldo(dev, index, &state); + if (!ret) { + printf("LDO%d: %s\n", index, + state == EC_LDO_STATE_ON ? + "on" : "off"); + } + } + + if (ret) { + debug("%s: Could not access LDO%d\n", __func__, index); + return ret; + } + } else { + return CMD_RET_USAGE; + } + + if (ret < 0) { + printf("Error: CROS-EC command failed (error %d)\n", ret); + ret = 1; + } + + return ret; +} + +U_BOOT_CMD( + crosec, 5, 1, do_cros_ec, + "CROS-EC utility command", + "init Re-init CROS-EC (done on startup automatically)\n" + "crosec id Read CROS-EC ID\n" + "crosec info Read CROS-EC info\n" + "crosec curimage Read CROS-EC current image\n" + "crosec hash Read CROS-EC hash\n" + "crosec reboot [rw | ro | cold] Reboot CROS-EC\n" + "crosec events Read CROS-EC host events\n" + "crosec clrevents [mask] Clear CROS-EC host events\n" + "crosec regioninfo Read image info\n" + "crosec erase Erase EC image\n" + "crosec read [] Read EC image\n" + "crosec write [] Write EC image\n" + "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" + "crosec ldo [] Switch/Read LDO state\n" + "crosec test run tests on cros_ec\n" + "crosec version Read CROS-EC version" +); +#endif diff --git a/include/cros_ec.h b/include/cros_ec.h new file mode 100644 index 0000000..335d5b4 --- /dev/null +++ b/include/cros_ec.h @@ -0,0 +1,449 @@ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * 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; either version 2 of + * the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _CROS_EC_H +#define _CROS_EC_H + +#include +#include +#include +#include + +/* Which interface is the device on? */ +enum cros_ec_interface_t { + CROS_EC_IF_NONE, + CROS_EC_IF_SPI, + CROS_EC_IF_I2C, + CROS_EC_IF_LPC, /* Intel Low Pin Count interface */ +}; + +/* Our configuration information */ +struct cros_ec_dev { + enum cros_ec_interface_t interface; + struct spi_slave *spi; /* Our SPI slave, if using SPI */ + int node; /* Our node */ + int parent_node; /* Our parent node (interface) */ + unsigned int cs; /* Our chip select */ + unsigned int addr; /* Device address (for I2C) */ + unsigned int bus_num; /* Bus number (for I2C) */ + unsigned int max_frequency; /* Maximum interface frequency */ + struct fdt_gpio_state ec_int; /* GPIO used as EC interrupt line */ + int cmd_version_is_supported; /* Device supports command versions */ + int optimise_flash_write; /* Don't write erased flash blocks */ + + /* + * These two buffers will always be dword-aligned and include enough + * space for up to 7 word-alignment bytes also, so we can ensure that + * the body of the message is always dword-aligned (64-bit). + * + * We use this alignment to keep ARM and x86 happy. Probably word + * alignment would be OK, there might be a small performance advantage + * to using dword. + */ + uint8_t din[ALIGN(MSG_BYTES + sizeof(int64_t), sizeof(int64_t))] + __aligned(sizeof(int64_t)); + uint8_t dout[ALIGN(MSG_BYTES + sizeof(int64_t), sizeof(int64_t))] + __aligned(sizeof(int64_t)); +}; + +/* + * Hard-code the number of columns we happen to know we have right now. It + * would be more correct to call cros_ec_info() at startup and determine the + * actual number of keyboard cols from there. + */ +#define CROS_EC_KEYSCAN_COLS 13 + +/* Information returned by a key scan */ +struct mbkp_keyscan { + uint8_t data[CROS_EC_KEYSCAN_COLS]; +}; + +/** + * Read the ID of the CROS-EC device + * + * The ID is a string identifying the CROS-EC device. + * + * @param dev CROS-EC device + * @param id Place to put the ID + * @param maxlen Maximum length of the ID field + * @return 0 if ok, -1 on error + */ +int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen); + +/** + * Read a keyboard scan from the CROS-EC device + * + * Send a message requesting a keyboard scan and return the result + * + * @param dev CROS-EC device + * @param scan Place to put the scan results + * @return 0 if ok, -1 on error + */ +int cros_ec_scan_keyboard(struct cros_ec_dev *dev, struct mbkp_keyscan *scan); + +/** + * Read which image is currently running on the CROS-EC device. + * + * @param dev CROS-EC device + * @param image Destination for image identifier + * @return 0 if ok, <0 on error + */ +int cros_ec_read_current_image(struct cros_ec_dev *dev, + enum ec_current_image *image); + +/** + * Read the hash of the CROS-EC device firmware. + * + * @param dev CROS-EC device + * @param hash Destination for hash information + * @return 0 if ok, <0 on error + */ +int cros_ec_read_hash(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash); + +/** + * Send a reboot command to the CROS-EC device. + * + * Note that some reboot commands (such as EC_REBOOT_COLD) also reboot the AP. + * + * @param dev CROS-EC device + * @param cmd Reboot command + * @param flags Flags for reboot command (EC_REBOOT_FLAG_*) + * @return 0 if ok, <0 on error + */ +int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, + uint8_t flags); + +/** + * Check if the CROS-EC device has an interrupt pending. + * + * Read the status of the external interrupt connected to the CROS-EC device. + * If no external interrupt is configured, this always returns 1. + * + * @param dev CROS-EC device + * @return 0 if no interrupt is pending + */ +int cros_ec_interrupt_pending(struct cros_ec_dev *dev); + +enum { + CROS_EC_OK, + CROS_EC_ERR = 1, + CROS_EC_ERR_FDT_DECODE, + CROS_EC_ERR_CHECK_VERSION, + CROS_EC_ERR_READ_ID, + CROS_EC_ERR_DEV_INIT, +}; + +/** + * Set up the Chromium OS matrix keyboard protocol + * + * @param blob Device tree blob containing setup information + * @param cros_ecp Returns pointer to the cros_ec device, or NULL if none + * @return 0 if we got an cros_ec device and all is well (or no cros_ec is + * expected), -ve if we should have an cros_ec device but failed to find + * one, or init failed (-CROS_EC_ERR_...). + */ +int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp); + +/** + * Read information about the keyboard matrix + * + * @param dev CROS-EC device + * @param info Place to put the info structure + */ +int cros_ec_info(struct cros_ec_dev *dev, + struct ec_response_cros_ec_info *info); + +/** + * Read the host event flags + * + * @param dev CROS-EC device + * @param events_ptr Destination for event flags. Not changed on error. + * @return 0 if ok, <0 on error + */ +int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr); + +/** + * Clear the specified host event flags + * + * @param dev CROS-EC device + * @param events Event flags to clear + * @return 0 if ok, <0 on error + */ +int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events); + +/** + * Get/set flash protection + * + * @param dev CROS-EC device + * @param set_mask Mask of flags to set; if 0, just retrieves existing + * protection state without changing it. + * @param set_flags New flag values; only bits in set_mask are applied; + * ignored if set_mask=0. + * @param prot Destination for updated protection state from EC. + * @return 0 if ok, <0 on error + */ +int cros_ec_flash_protect(struct cros_ec_dev *dev, + uint32_t set_mask, uint32_t set_flags, + struct ec_response_flash_protect *resp); + + +/** + * Run internal tests on the cros_ec interface. + * + * @param dev CROS-EC device + * @return 0 if ok, <0 if the test failed + */ +int cros_ec_test(struct cros_ec_dev *dev); + +/** + * Update the EC RW copy. + * + * @param dev CROS-EC device + * @param image the content to write + * @param imafge_size content length + * @return 0 if ok, <0 if the test failed + */ +int cros_ec_flash_update_rw(struct cros_ec_dev *dev, + const uint8_t *image, int image_size); + +/** + * Return a pointer to the board's CROS-EC device + * + * This should be implemented by board files. + * + * @return pointer to CROS-EC device, or NULL if none is available + */ +struct cros_ec_dev *board_get_cros_ec_dev(void); + + +/* Internal interfaces */ +int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob); +int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob); +int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob); + +/** + * Read information from the fdt for the i2c cros_ec interface + * + * @param dev CROS-EC device + * @param blob Device tree blob + * @return 0 if ok, -1 if we failed to read all required information + */ +int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob); + +/** + * Read information from the fdt for the spi cros_ec interface + * + * @param dev CROS-EC device + * @param blob Device tree blob + * @return 0 if ok, -1 if we failed to read all required information + */ +int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob); + +/** + * Check whether the LPC interface supports new-style commands. + * + * LPC has its own way of doing this, which involves checking LPC values + * visible to the host. Do this, and update dev->cmd_version_is_supported + * accordingly. + * + * @param dev CROS-EC device to check + */ +int cros_ec_lpc_check_version(struct cros_ec_dev *dev); + +/** + * Send a command to an I2C CROS-EC device and return the reply. + * + * This rather complicated function deals with sending both old-style and + * new-style commands. The old ones have just a command byte and arguments. + * The new ones have version, command, arg-len, [args], chksum so are 3 bytes + * longer. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Returns pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len); + +/** + * Send a command to a LPC CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Returns pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len); + +int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len); + +/** + * Dump a block of data for a command. + * + * @param name Name for data (e.g. 'in', 'out') + * @param cmd Command number associated with data, or -1 for none + * @param data Data block to dump + * @param len Length of data block to dump + */ +void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len); + +/** + * Calculate a simple 8-bit checksum of a data block + * + * @param data Data block to checksum + * @param size Size of data block in bytes + * @return checksum value (0 to 255) + */ +int cros_ec_calc_checksum(const uint8_t *data, int size); + +/** + * Decode a flash region parameter + * + * @param argc Number of params remaining + * @param argv List of remaining parameters + * @return flash region (EC_FLASH_REGION_...) or -1 on error + */ +int cros_ec_decode_region(int argc, char * const argv[]); + +int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, + uint32_t size); + +/** + * Read data from the flash + * + * Read an arbitrary amount of data from the EC flash, by repeatedly reading + * small blocks. + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to read for a particular region. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to read into + * @param offset Offset within flash to read from + * @param size Number of bytes to read + * @return 0 if ok, -1 on error + */ +int cros_ec_flash_read(struct cros_ec_dev *dev, uint8_t *data, uint32_t offset, + uint32_t size); + +/** + * Write data to the flash + * + * Write an arbitrary amount of data to the EC flash, by repeatedly writing + * small blocks. + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to write for a particular region. + * + * Attempting to write to the region where the EC is currently running from + * will result in an error. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to write + * @param offset Offset within flash to write to. + * @param size Number of bytes to write + * @return 0 if ok, -1 on error + */ +int cros_ec_flash_write(struct cros_ec_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size); + +/** + * Obtain position and size of a flash region + * + * @param dev CROS-EC device + * @param region Flash region to query + * @param offset Returns offset of flash region in EC flash + * @param size Returns size of flash region + * @return 0 if ok, -1 on error + */ +int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, + uint32_t *offset, uint32_t *size); + +/** + * Read/write VbNvContext from/to a CROS-EC device. + * + * @param dev CROS-EC device + * @param block Buffer of VbNvContext to be read/write + * @return 0 if ok, -1 on error + */ +int cros_ec_read_vbnvcontext(struct cros_ec_dev *dev, uint8_t *block); +int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block); + +/** + * Read the version information for the EC images + * + * @param dev CROS-EC device + * @param versionp This is set to point to the version information + * @return 0 if ok, -1 on error + */ +int cros_ec_read_version(struct cros_ec_dev *dev, + struct ec_response_get_version **versionp); + +/** + * Read the build information for the EC + * + * @param dev CROS-EC device + * @param versionp This is set to point to the build string + * @return 0 if ok, -1 on error + */ +int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp); + +/** + * Switch on/off a LDO / FET. + * + * @param dev CROS-EC device + * @param index index of the LDO/FET to switch + * @param state new state of the LDO/FET : EC_LDO_STATE_ON|OFF + * @return 0 if ok, -1 on error + */ +int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state); + +/** + * Read back a LDO / FET current state. + * + * @param dev CROS-EC device + * @param index index of the LDO/FET to switch + * @param state current state of the LDO/FET : EC_LDO_STATE_ON|OFF + * @return 0 if ok, -1 on error + */ +int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state); +#endif diff --git a/include/cros_ec_message.h b/include/cros_ec_message.h new file mode 100644 index 0000000..a2421c7 --- /dev/null +++ b/include/cros_ec_message.h @@ -0,0 +1,44 @@ +/* + * Chromium OS Matrix Keyboard Message Protocol definitions + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * 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; either version 2 of + * the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _CROS_MESSAGE_H +#define _CROS_MESSAGE_H + +/* + * Command interface between EC and AP, for LPC, I2C and SPI interfaces. + * + * This is copied from the Chromium OS Open Source Embedded Controller code. + */ +enum { + /* The header byte, which follows the preamble */ + MSG_HEADER = 0xec, + + MSG_HEADER_BYTES = 3, + MSG_TRAILER_BYTES = 2, + MSG_PROTO_BYTES = MSG_HEADER_BYTES + MSG_TRAILER_BYTES, + + /* Max length of messages */ + MSG_BYTES = EC_HOST_PARAM_SIZE + MSG_PROTO_BYTES, +}; + +#endif diff --git a/include/ec_commands.h b/include/ec_commands.h new file mode 100644 index 0000000..12811cc --- /dev/null +++ b/include/ec_commands.h @@ -0,0 +1,1440 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Host communication command constants for Chrome EC */ + +#ifndef __CROS_EC_COMMANDS_H +#define __CROS_EC_COMMANDS_H + +/* + * Protocol overview + * + * request: CMD [ P0 P1 P2 ... Pn S ] + * response: ERR [ P0 P1 P2 ... Pn S ] + * + * where the bytes are defined as follow : + * - CMD is the command code. (defined by EC_CMD_ constants) + * - ERR is the error code. (defined by EC_RES_ constants) + * - Px is the optional payload. + * it is not sent if the error code is not success. + * (defined by ec_params_ and ec_response_ structures) + * - S is the checksum which is the sum of all payload bytes. + * + * On LPC, CMD and ERR are sent/received at EC_LPC_ADDR_KERNEL|USER_CMD + * and the payloads are sent/received at EC_LPC_ADDR_KERNEL|USER_PARAM. + * On I2C, all bytes are sent serially in the same message. + */ + +/* Current version of this protocol */ +#define EC_PROTO_VERSION 0x00000002 + +/* Command version mask */ +#define EC_VER_MASK(version) (1UL << (version)) + +/* I/O addresses for ACPI commands */ +#define EC_LPC_ADDR_ACPI_DATA 0x62 +#define EC_LPC_ADDR_ACPI_CMD 0x66 + +/* I/O addresses for host command */ +#define EC_LPC_ADDR_HOST_DATA 0x200 +#define EC_LPC_ADDR_HOST_CMD 0x204 + +/* I/O addresses for host command args and params */ +#define EC_LPC_ADDR_HOST_ARGS 0x800 +#define EC_LPC_ADDR_HOST_PARAM 0x804 +#define EC_HOST_PARAM_SIZE 0x0fc /* Size of param area in bytes */ + +/* I/O addresses for host command params, old interface */ +#define EC_LPC_ADDR_OLD_PARAM 0x880 +#define EC_OLD_PARAM_SIZE 0x080 /* Size of param area in bytes */ + +/* EC command register bit functions */ +#define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */ +#define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */ +#define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */ +#define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */ +#define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */ +#define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */ +#define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */ + +#define EC_LPC_ADDR_MEMMAP 0x900 +#define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */ +#define EC_MEMMAP_TEXT_MAX 8 /* Size of a string in the memory map */ + +/* The offset address of each type of data in mapped memory. */ +#define EC_MEMMAP_TEMP_SENSOR 0x00 /* Temp sensors */ +#define EC_MEMMAP_FAN 0x10 /* Fan speeds */ +#define EC_MEMMAP_TEMP_SENSOR_B 0x18 /* Temp sensors (second set) */ +#define EC_MEMMAP_ID 0x20 /* 'E' 'C' */ +#define EC_MEMMAP_ID_VERSION 0x22 /* Version of data in 0x20 - 0x2f */ +#define EC_MEMMAP_THERMAL_VERSION 0x23 /* Version of data in 0x00 - 0x1f */ +#define EC_MEMMAP_BATTERY_VERSION 0x24 /* Version of data in 0x40 - 0x7f */ +#define EC_MEMMAP_SWITCHES_VERSION 0x25 /* Version of data in 0x30 - 0x33 */ +#define EC_MEMMAP_EVENTS_VERSION 0x26 /* Version of data in 0x34 - 0x3f */ +#define EC_MEMMAP_HOST_CMD_FLAGS 0x27 /* Host command interface flags */ +#define EC_MEMMAP_SWITCHES 0x30 +#define EC_MEMMAP_HOST_EVENTS 0x34 +#define EC_MEMMAP_BATT_VOLT 0x40 /* Battery Present Voltage */ +#define EC_MEMMAP_BATT_RATE 0x44 /* Battery Present Rate */ +#define EC_MEMMAP_BATT_CAP 0x48 /* Battery Remaining Capacity */ +#define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, defined below */ +#define EC_MEMMAP_BATT_DCAP 0x50 /* Battery Design Capacity */ +#define EC_MEMMAP_BATT_DVLT 0x54 /* Battery Design Voltage */ +#define EC_MEMMAP_BATT_LFCC 0x58 /* Battery Last Full Charge Capacity */ +#define EC_MEMMAP_BATT_CCNT 0x5c /* Battery Cycle Count */ +#define EC_MEMMAP_BATT_MFGR 0x60 /* Battery Manufacturer String */ +#define EC_MEMMAP_BATT_MODEL 0x68 /* Battery Model Number String */ +#define EC_MEMMAP_BATT_SERIAL 0x70 /* Battery Serial Number String */ +#define EC_MEMMAP_BATT_TYPE 0x78 /* Battery Type String */ + +/* Number of temp sensors at EC_MEMMAP_TEMP_SENSOR */ +#define EC_TEMP_SENSOR_ENTRIES 16 +/* + * Number of temp sensors at EC_MEMMAP_TEMP_SENSOR_B. + * + * Valid only if EC_MEMMAP_THERMAL_VERSION returns >= 2. + */ +#define EC_TEMP_SENSOR_B_ENTRIES 8 +#define EC_TEMP_SENSOR_NOT_PRESENT 0xff +#define EC_TEMP_SENSOR_ERROR 0xfe +#define EC_TEMP_SENSOR_NOT_POWERED 0xfd +#define EC_TEMP_SENSOR_NOT_CALIBRATED 0xfc +/* + * The offset of temperature value stored in mapped memory. This allows + * reporting a temperature range of 200K to 454K = -73C to 181C. + */ +#define EC_TEMP_SENSOR_OFFSET 200 + +#define EC_FAN_SPEED_ENTRIES 4 /* Number of fans at EC_MEMMAP_FAN */ +#define EC_FAN_SPEED_NOT_PRESENT 0xffff /* Entry not present */ +#define EC_FAN_SPEED_STALLED 0xfffe /* Fan stalled */ + +/* Battery bit flags at EC_MEMMAP_BATT_FLAG. */ +#define EC_BATT_FLAG_AC_PRESENT 0x01 +#define EC_BATT_FLAG_BATT_PRESENT 0x02 +#define EC_BATT_FLAG_DISCHARGING 0x04 +#define EC_BATT_FLAG_CHARGING 0x08 +#define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 + +/* Switch flags at EC_MEMMAP_SWITCHES */ +#define EC_SWITCH_LID_OPEN 0x01 +#define EC_SWITCH_POWER_BUTTON_PRESSED 0x02 +#define EC_SWITCH_WRITE_PROTECT_DISABLED 0x04 +/* Recovery requested via keyboard */ +#define EC_SWITCH_KEYBOARD_RECOVERY 0x08 +/* Recovery requested via dedicated signal (from servo board) */ +#define EC_SWITCH_DEDICATED_RECOVERY 0x10 +/* Was fake developer mode switch; now unused. Remove in next refactor. */ +#define EC_SWITCH_IGNORE0 0x20 + +/* Host command interface flags */ +/* Host command interface supports LPC args (LPC interface only) */ +#define EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED 0x01 + +/* Wireless switch flags */ +#define EC_WIRELESS_SWITCH_WLAN 0x01 +#define EC_WIRELESS_SWITCH_BLUETOOTH 0x02 + +/* + * This header file is used in coreboot both in C and ACPI code. The ACPI code + * is pre-processed to handle constants but the ASL compiler is unable to + * handle actual C code so keep it separate. + */ +#ifndef __ACPI__ + +/* + * Define __packed if someone hasn't beat us to it. Linux kernel style + * checking prefers __packed over __attribute__((packed)). + */ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +/* LPC command status byte masks */ +/* EC has written a byte in the data register and host hasn't read it yet */ +#define EC_LPC_STATUS_TO_HOST 0x01 +/* Host has written a command/data byte and the EC hasn't read it yet */ +#define EC_LPC_STATUS_FROM_HOST 0x02 +/* EC is processing a command */ +#define EC_LPC_STATUS_PROCESSING 0x04 +/* Last write to EC was a command, not data */ +#define EC_LPC_STATUS_LAST_CMD 0x08 +/* EC is in burst mode. Unsupported by Chrome EC, so this bit is never set */ +#define EC_LPC_STATUS_BURST_MODE 0x10 +/* SCI event is pending (requesting SCI query) */ +#define EC_LPC_STATUS_SCI_PENDING 0x20 +/* SMI event is pending (requesting SMI query) */ +#define EC_LPC_STATUS_SMI_PENDING 0x40 +/* (reserved) */ +#define EC_LPC_STATUS_RESERVED 0x80 + +/* + * EC is busy. This covers both the EC processing a command, and the host has + * written a new command but the EC hasn't picked it up yet. + */ +#define EC_LPC_STATUS_BUSY_MASK \ + (EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) + +/* Host command response codes */ +enum ec_status { + EC_RES_SUCCESS = 0, + EC_RES_INVALID_COMMAND = 1, + EC_RES_ERROR = 2, + EC_RES_INVALID_PARAM = 3, + EC_RES_ACCESS_DENIED = 4, + EC_RES_INVALID_RESPONSE = 5, + EC_RES_INVALID_VERSION = 6, + EC_RES_INVALID_CHECKSUM = 7, + EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */ + EC_RES_UNAVAILABLE = 9, /* No response available */ + EC_RES_TIMEOUT = 10, /* We got a timeout */ + EC_RES_OVERFLOW = 11, /* Table / data overflow */ +}; + +/* + * Host event codes. Note these are 1-based, not 0-based, because ACPI query + * EC command uses code 0 to mean "no event pending". We explicitly specify + * each value in the enum listing so they won't change if we delete/insert an + * item or rearrange the list (it needs to be stable across platforms, not + * just within a single compiled instance). + */ +enum host_event_code { + EC_HOST_EVENT_LID_CLOSED = 1, + EC_HOST_EVENT_LID_OPEN = 2, + EC_HOST_EVENT_POWER_BUTTON = 3, + EC_HOST_EVENT_AC_CONNECTED = 4, + EC_HOST_EVENT_AC_DISCONNECTED = 5, + EC_HOST_EVENT_BATTERY_LOW = 6, + EC_HOST_EVENT_BATTERY_CRITICAL = 7, + EC_HOST_EVENT_BATTERY = 8, + EC_HOST_EVENT_THERMAL_THRESHOLD = 9, + EC_HOST_EVENT_THERMAL_OVERLOAD = 10, + EC_HOST_EVENT_THERMAL = 11, + EC_HOST_EVENT_USB_CHARGER = 12, + EC_HOST_EVENT_KEY_PRESSED = 13, + /* + * EC has finished initializing the host interface. The host can check + * for this event following sending a EC_CMD_REBOOT_EC command to + * determine when the EC is ready to accept subsequent commands. + */ + EC_HOST_EVENT_INTERFACE_READY = 14, + /* Keyboard recovery combo has been pressed */ + EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, + + /* Shutdown due to thermal overload */ + EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, + /* Shutdown due to battery level too low */ + EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, + + /* + * The high bit of the event mask is not used as a host event code. If + * it reads back as set, then the entire event mask should be + * considered invalid by the host. This can happen when reading the + * raw event status via EC_MEMMAP_HOST_EVENTS but the LPC interface is + * not initialized on the EC, or improperly configured on the host. + */ + EC_HOST_EVENT_INVALID = 32 +}; +/* Host event mask */ +#define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) + +/* Arguments at EC_LPC_ADDR_HOST_ARGS */ +struct ec_lpc_host_args { + uint8_t flags; + uint8_t command_version; + uint8_t data_size; + /* + * Checksum; sum of command + flags + command_version + data_size + + * all params/response data bytes. + */ + uint8_t checksum; +} __packed; + +/* Flags for ec_lpc_host_args.flags */ +/* + * Args are from host. Data area at EC_LPC_ADDR_HOST_PARAM contains command + * params. + * + * If EC gets a command and this flag is not set, this is an old-style command. + * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with + * unknown length. EC must respond with an old-style response (that is, + * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). + */ +#define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 +/* + * Args are from EC. Data area at EC_LPC_ADDR_HOST_PARAM contains response. + * + * If EC responds to a command and this flag is not set, this is an old-style + * response. Command version is 0 and response data from EC is at + * EC_LPC_ADDR_OLD_PARAM with unknown length. + */ +#define EC_HOST_ARGS_FLAG_TO_HOST 0x02 + +/* + * Notes on commands: + * + * Each command is an 8-byte command value. Commands which take params or + * return response data specify structs for that data. If no struct is + * specified, the command does not input or output data, respectively. + * Parameter/response length is implicit in the structs. Some underlying + * communication protocols (I2C, SPI) may add length or checksum headers, but + * those are implementation-dependent and not defined here. + */ + +/*****************************************************************************/ +/* General / test commands */ + +/* + * Get protocol version, used to deal with non-backward compatible protocol + * changes. + */ +#define EC_CMD_PROTO_VERSION 0x00 + +struct ec_response_proto_version { + uint32_t version; +} __packed; + +/* + * Hello. This is a simple command to test the EC is responsive to + * commands. + */ +#define EC_CMD_HELLO 0x01 + +struct ec_params_hello { + uint32_t in_data; /* Pass anything here */ +} __packed; + +struct ec_response_hello { + uint32_t out_data; /* Output will be in_data + 0x01020304 */ +} __packed; + +/* Get version number */ +#define EC_CMD_GET_VERSION 0x02 + +enum ec_current_image { + EC_IMAGE_UNKNOWN = 0, + EC_IMAGE_RO, + EC_IMAGE_RW +}; + +struct ec_response_get_version { + /* Null-terminated version strings for RO, RW */ + char version_string_ro[32]; + char version_string_rw[32]; + char reserved[32]; /* Was previously RW-B string */ + uint32_t current_image; /* One of ec_current_image */ +} __packed; + +/* Read test */ +#define EC_CMD_READ_TEST 0x03 + +struct ec_params_read_test { + uint32_t offset; /* Starting value for read buffer */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +struct ec_response_read_test { + uint32_t data[32]; +} __packed; + +/* + * Get build information + * + * Response is null-terminated string. + */ +#define EC_CMD_GET_BUILD_INFO 0x04 + +/* Get chip info */ +#define EC_CMD_GET_CHIP_INFO 0x05 + +struct ec_response_get_chip_info { + /* Null-terminated strings */ + char vendor[32]; + char name[32]; + char revision[32]; /* Mask version */ +} __packed; + +/* Get board HW version */ +#define EC_CMD_GET_BOARD_VERSION 0x06 + +struct ec_response_board_version { + uint16_t board_version; /* A monotonously incrementing number. */ +} __packed; + +/* + * Read memory-mapped data. + * + * This is an alternate interface to memory-mapped data for bus protocols + * which don't support direct-mapped memory - I2C, SPI, etc. + * + * Response is params.size bytes of data. + */ +#define EC_CMD_READ_MEMMAP 0x07 + +struct ec_params_read_memmap { + uint8_t offset; /* Offset in memmap (EC_MEMMAP_*) */ + uint8_t size; /* Size to read in bytes */ +} __packed; + +/* Read versions supported for a command */ +#define EC_CMD_GET_CMD_VERSIONS 0x08 + +struct ec_params_get_cmd_versions { + uint8_t cmd; /* Command to check */ +} __packed; + +struct ec_response_get_cmd_versions { + /* + * Mask of supported versions; use EC_VER_MASK() to compare with a + * desired version. + */ + uint32_t version_mask; +} __packed; + +/* + * Check EC communcations status (busy). This is needed on i2c/spi but not + * on lpc since it has its own out-of-band busy indicator. + * + * lpc must read the status from the command register. Attempting this on + * lpc will overwrite the args/parameter space and corrupt its data. + */ +#define EC_CMD_GET_COMMS_STATUS 0x09 + +/* Avoid using ec_status which is for return values */ +enum ec_comms_status { + EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */ +}; + +struct ec_response_get_comms_status { + uint32_t flags; /* Mask of enum ec_comms_status */ +} __packed; + + +/*****************************************************************************/ +/* Flash commands */ + +/* Get flash info */ +#define EC_CMD_FLASH_INFO 0x10 + +struct ec_response_flash_info { + /* Usable flash size, in bytes */ + uint32_t flash_size; + /* + * Write block size. Write offset and size must be a multiple + * of this. + */ + uint32_t write_block_size; + /* + * Erase block size. Erase offset and size must be a multiple + * of this. + */ + uint32_t erase_block_size; + /* + * Protection block size. Protection offset and size must be a + * multiple of this. + */ + uint32_t protect_block_size; +} __packed; + +/* + * Read flash + * + * Response is params.size bytes of data. + */ +#define EC_CMD_FLASH_READ 0x11 + +struct ec_params_flash_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write flash */ +#define EC_CMD_FLASH_WRITE 0x12 + +struct ec_params_flash_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + /* + * Data to write. Could really use EC_PARAM_SIZE - 8, but tidiest to + * use a power of 2 so writes stay aligned. + */ + uint8_t data[64]; +} __packed; + +/* Erase flash */ +#define EC_CMD_FLASH_ERASE 0x13 + +struct ec_params_flash_erase { + uint32_t offset; /* Byte offset to erase */ + uint32_t size; /* Size to erase in bytes */ +} __packed; + +/* + * Get/set flash protection. + * + * If mask!=0, sets/clear the requested bits of flags. Depending on the + * firmware write protect GPIO, not all flags will take effect immediately; + * some flags require a subsequent hard reset to take effect. Check the + * returned flags bits to see what actually happened. + * + * If mask=0, simply returns the current flags state. + */ +#define EC_CMD_FLASH_PROTECT 0x15 +#define EC_VER_FLASH_PROTECT 1 /* Command version 1 */ + +/* Flags for flash protection */ +/* RO flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_RO_AT_BOOT (1 << 0) +/* + * RO flash code protected now. If this bit is set, at-boot status cannot + * be changed. + */ +#define EC_FLASH_PROTECT_RO_NOW (1 << 1) +/* Entire flash code protected now, until reboot. */ +#define EC_FLASH_PROTECT_ALL_NOW (1 << 2) +/* Flash write protect GPIO is asserted now */ +#define EC_FLASH_PROTECT_GPIO_ASSERTED (1 << 3) +/* Error - at least one bank of flash is stuck locked, and cannot be unlocked */ +#define EC_FLASH_PROTECT_ERROR_STUCK (1 << 4) +/* + * Error - flash protection is in inconsistent state. At least one bank of + * flash which should be protected is not protected. Usually fixed by + * re-requesting the desired flags, or by a hard reset if that fails. + */ +#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5) +/* Entile flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_ALL_AT_BOOT (1 << 6) + +struct ec_params_flash_protect { + uint32_t mask; /* Bits in flags to apply */ + uint32_t flags; /* New flags to apply */ +} __packed; + +struct ec_response_flash_protect { + /* Current value of flash protect flags */ + uint32_t flags; + /* + * Flags which are valid on this platform. This allows the caller + * to distinguish between flags which aren't set vs. flags which can't + * be set on this platform. + */ + uint32_t valid_flags; + /* Flags which can be changed given the current protection state */ + uint32_t writable_flags; +} __packed; + +/* + * Note: commands 0x14 - 0x19 version 0 were old commands to get/set flash + * write protect. These commands may be reused with version > 0. + */ + +/* Get the region offset/size */ +#define EC_CMD_FLASH_REGION_INFO 0x16 +#define EC_VER_FLASH_REGION_INFO 1 + +enum ec_flash_region { + /* Region which holds read-only EC image */ + EC_FLASH_REGION_RO, + /* Region which holds rewritable EC image */ + EC_FLASH_REGION_RW, + /* + * Region which should be write-protected in the factory (a superset of + * EC_FLASH_REGION_RO) + */ + EC_FLASH_REGION_WP_RO, +}; + +struct ec_params_flash_region_info { + uint32_t region; /* enum ec_flash_region */ +} __packed; + +struct ec_response_flash_region_info { + uint32_t offset; + uint32_t size; +} __packed; + +/* Read/write VbNvContext */ +#define EC_CMD_VBNV_CONTEXT 0x17 +#define EC_VER_VBNV_CONTEXT 1 +#define EC_VBNV_BLOCK_SIZE 16 + +enum ec_vbnvcontext_op { + EC_VBNV_CONTEXT_OP_READ, + EC_VBNV_CONTEXT_OP_WRITE, +}; + +struct ec_params_vbnvcontext { + uint32_t op; + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +struct ec_response_vbnvcontext { + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +/*****************************************************************************/ +/* PWM commands */ + +/* Get fan target RPM */ +#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 + +struct ec_response_pwm_get_fan_rpm { + uint32_t rpm; +} __packed; + +/* Set target fan RPM */ +#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 + +struct ec_params_pwm_set_fan_target_rpm { + uint32_t rpm; +} __packed; + +/* Get keyboard backlight */ +#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 + +struct ec_response_pwm_get_keyboard_backlight { + uint8_t percent; + uint8_t enabled; +} __packed; + +/* Set keyboard backlight */ +#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 + +struct ec_params_pwm_set_keyboard_backlight { + uint8_t percent; +} __packed; + +/* Set target fan PWM duty cycle */ +#define EC_CMD_PWM_SET_FAN_DUTY 0x24 + +struct ec_params_pwm_set_fan_duty { + uint32_t percent; +} __packed; + +/*****************************************************************************/ +/* + * Lightbar commands. This looks worse than it is. Since we only use one HOST + * command to say "talk to the lightbar", we put the "and tell it to do X" part + * into a subcommand. We'll make separate structs for subcommands with + * different input args, so that we know how much to expect. + */ +#define EC_CMD_LIGHTBAR_CMD 0x28 + +struct rgb_s { + uint8_t r, g, b; +}; + +#define LB_BATTERY_LEVELS 4 +/* List of tweakable parameters. NOTE: It's __packed so it can be sent in a + * host command, but the alignment is the same regardless. Keep it that way. + */ +struct lightbar_params { + /* Timing */ + int google_ramp_up; + int google_ramp_down; + int s3s0_ramp_up; + int s0_tick_delay[2]; /* AC=0/1 */ + int s0a_tick_delay[2]; /* AC=0/1 */ + int s0s3_ramp_down; + int s3_sleep_for; + int s3_ramp_up; + int s3_ramp_down; + + /* Oscillation */ + uint8_t new_s0; + uint8_t osc_min[2]; /* AC=0/1 */ + uint8_t osc_max[2]; /* AC=0/1 */ + uint8_t w_ofs[2]; /* AC=0/1 */ + + /* Brightness limits based on the backlight and AC. */ + uint8_t bright_bl_off_fixed[2]; /* AC=0/1 */ + uint8_t bright_bl_on_min[2]; /* AC=0/1 */ + uint8_t bright_bl_on_max[2]; /* AC=0/1 */ + + /* Battery level thresholds */ + uint8_t battery_threshold[LB_BATTERY_LEVELS - 1]; + + /* Map [AC][battery_level] to color index */ + uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */ + uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */ + + /* Color palette */ + struct rgb_s color[8]; /* 0-3 are Google colors */ +} __packed; + +struct ec_params_lightbar { + uint8_t cmd; /* Command (see enum lightbar_command) */ + union { + struct { + /* no args */ + } dump, off, on, init, get_seq, get_params; + + struct num { + uint8_t num; + } brightness, seq, demo; + + struct reg { + uint8_t ctrl, reg, value; + } reg; + + struct rgb { + uint8_t led, red, green, blue; + } rgb; + + struct lightbar_params set_params; + }; +} __packed; + +struct ec_response_lightbar { + union { + struct dump { + struct { + uint8_t reg; + uint8_t ic0; + uint8_t ic1; + } vals[23]; + } dump; + + struct get_seq { + uint8_t num; + } get_seq; + + struct lightbar_params get_params; + + struct { + /* no return params */ + } off, on, init, brightness, seq, reg, rgb, demo, set_params; + }; +} __packed; + +/* Lightbar commands */ +enum lightbar_command { + LIGHTBAR_CMD_DUMP = 0, + LIGHTBAR_CMD_OFF = 1, + LIGHTBAR_CMD_ON = 2, + LIGHTBAR_CMD_INIT = 3, + LIGHTBAR_CMD_BRIGHTNESS = 4, + LIGHTBAR_CMD_SEQ = 5, + LIGHTBAR_CMD_REG = 6, + LIGHTBAR_CMD_RGB = 7, + LIGHTBAR_CMD_GET_SEQ = 8, + LIGHTBAR_CMD_DEMO = 9, + LIGHTBAR_CMD_GET_PARAMS = 10, + LIGHTBAR_CMD_SET_PARAMS = 11, + LIGHTBAR_NUM_CMDS +}; + +/*****************************************************************************/ +/* Verified boot commands */ + +/* + * Note: command code 0x29 version 0 was VBOOT_CMD in Link EVT; it may be + * reused for other purposes with version > 0. + */ + +/* Verified boot hash command */ +#define EC_CMD_VBOOT_HASH 0x2A + +struct ec_params_vboot_hash { + uint8_t cmd; /* enum ec_vboot_hash_cmd */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t nonce_size; /* Nonce size; may be 0 */ + uint8_t reserved0; /* Reserved; set 0 */ + uint32_t offset; /* Offset in flash to hash */ + uint32_t size; /* Number of bytes to hash */ + uint8_t nonce_data[64]; /* Nonce data; ignored if nonce_size=0 */ +} __packed; + +struct ec_response_vboot_hash { + uint8_t status; /* enum ec_vboot_hash_status */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t digest_size; /* Size of hash digest in bytes */ + uint8_t reserved0; /* Ignore; will be 0 */ + uint32_t offset; /* Offset in flash which was hashed */ + uint32_t size; /* Number of bytes hashed */ + uint8_t hash_digest[64]; /* Hash digest data */ +} __packed; + +enum ec_vboot_hash_cmd { + EC_VBOOT_HASH_GET = 0, /* Get current hash status */ + EC_VBOOT_HASH_ABORT = 1, /* Abort calculating current hash */ + EC_VBOOT_HASH_START = 2, /* Start computing a new hash */ + EC_VBOOT_HASH_RECALC = 3, /* Synchronously compute a new hash */ +}; + +enum ec_vboot_hash_type { + EC_VBOOT_HASH_TYPE_SHA256 = 0, /* SHA-256 */ +}; + +enum ec_vboot_hash_status { + EC_VBOOT_HASH_STATUS_NONE = 0, /* No hash (not started, or aborted) */ + EC_VBOOT_HASH_STATUS_DONE = 1, /* Finished computing a hash */ + EC_VBOOT_HASH_STATUS_BUSY = 2, /* Busy computing a hash */ +}; + +/* + * Special values for offset for EC_VBOOT_HASH_START and EC_VBOOT_HASH_RECALC. + * If one of these is specified, the EC will automatically update offset and + * size to the correct values for the specified image (RO or RW). + */ +#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe +#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd + +/*****************************************************************************/ +/* USB charging control commands */ + +/* Set USB port charging mode */ +#define EC_CMD_USB_CHARGE_SET_MODE 0x30 + +struct ec_params_usb_charge_set_mode { + uint8_t usb_port_id; + uint8_t mode; +} __packed; + +/*****************************************************************************/ +/* Persistent storage for host */ + +/* Maximum bytes that can be read/written in a single command */ +#define EC_PSTORE_SIZE_MAX 64 + +/* Get persistent storage info */ +#define EC_CMD_PSTORE_INFO 0x40 + +struct ec_response_pstore_info { + /* Persistent storage size, in bytes */ + uint32_t pstore_size; + /* Access size; read/write offset and size must be a multiple of this */ + uint32_t access_size; +} __packed; + +/* + * Read persistent storage + * + * Response is params.size bytes of data. + */ +#define EC_CMD_PSTORE_READ 0x41 + +struct ec_params_pstore_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write persistent storage */ +#define EC_CMD_PSTORE_WRITE 0x42 + +struct ec_params_pstore_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + uint8_t data[EC_PSTORE_SIZE_MAX]; +} __packed; + +/*****************************************************************************/ +/* Real-time clock */ + +/* RTC params and response structures */ +struct ec_params_rtc { + uint32_t time; +} __packed; + +struct ec_response_rtc { + uint32_t time; +} __packed; + +/* These use ec_response_rtc */ +#define EC_CMD_RTC_GET_VALUE 0x44 +#define EC_CMD_RTC_GET_ALARM 0x45 + +/* These all use ec_params_rtc */ +#define EC_CMD_RTC_SET_VALUE 0x46 +#define EC_CMD_RTC_SET_ALARM 0x47 + +/*****************************************************************************/ +/* Port80 log access */ + +/* Get last port80 code from previous boot */ +#define EC_CMD_PORT80_LAST_BOOT 0x48 + +struct ec_response_port80_last_boot { + uint16_t code; +} __packed; + +/*****************************************************************************/ +/* Thermal engine commands */ + +/* Set thershold value */ +#define EC_CMD_THERMAL_SET_THRESHOLD 0x50 + +struct ec_params_thermal_set_threshold { + uint8_t sensor_type; + uint8_t threshold_id; + uint16_t value; +} __packed; + +/* Get threshold value */ +#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 + +struct ec_params_thermal_get_threshold { + uint8_t sensor_type; + uint8_t threshold_id; +} __packed; + +struct ec_response_thermal_get_threshold { + uint16_t value; +} __packed; + +/* Toggle automatic fan control */ +#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 + +/* Get TMP006 calibration data */ +#define EC_CMD_TMP006_GET_CALIBRATION 0x53 + +struct ec_params_tmp006_get_calibration { + uint8_t index; +} __packed; + +struct ec_response_tmp006_get_calibration { + float s0; + float b0; + float b1; + float b2; +} __packed; + +/* Set TMP006 calibration data */ +#define EC_CMD_TMP006_SET_CALIBRATION 0x54 + +struct ec_params_tmp006_set_calibration { + uint8_t index; + uint8_t reserved[3]; /* Reserved; set 0 */ + float s0; + float b0; + float b1; + float b2; +} __packed; + +/*****************************************************************************/ +/* CROS_EC - Matrix KeyBoard Protocol */ + +/* + * Read key state + * + * Returns raw data for keyboard cols; see ec_response_cros_ec_info.cols for + * expected response size. + */ +#define EC_CMD_CROS_EC_STATE 0x60 + +/* Provide information about the matrix : number of rows and columns */ +#define EC_CMD_CROS_EC_INFO 0x61 + +struct ec_response_cros_ec_info { + uint32_t rows; + uint32_t cols; + uint8_t switches; +} __packed; + +/* Simulate key press */ +#define EC_CMD_CROS_EC_SIMULATE_KEY 0x62 + +struct ec_params_cros_ec_simulate_key { + uint8_t col; + uint8_t row; + uint8_t pressed; +} __packed; + +/* Configure keyboard scanning */ +#define EC_CMD_CROS_EC_SET_CONFIG 0x64 +#define EC_CMD_CROS_EC_GET_CONFIG 0x65 + +/* flags */ +enum cros_ec_config_flags { + EC_CROS_EC_FLAGS_ENABLE = 1, /* Enable keyboard scanning */ +}; + +enum cros_ec_config_valid { + EC_CROS_EC_VALID_SCAN_PERIOD = 1 << 0, + EC_CROS_EC_VALID_POLL_TIMEOUT = 1 << 1, + EC_CROS_EC_VALID_MIN_POST_SCAN_DELAY = 1 << 3, + EC_CROS_EC_VALID_OUTPUT_SETTLE = 1 << 4, + EC_CROS_EC_VALID_DEBOUNCE_DOWN = 1 << 5, + EC_CROS_EC_VALID_DEBOUNCE_UP = 1 << 6, + EC_CROS_EC_VALID_FIFO_MAX_DEPTH = 1 << 7, +}; + +/* Configuration for our key scanning algorithm */ +struct ec_cros_ec_config { + uint32_t valid_mask; /* valid fields */ + uint8_t flags; /* some flags (enum cros_ec_config_flags) */ + uint8_t valid_flags; /* which flags are valid */ + uint16_t scan_period_us; /* period between start of scans */ + /* revert to interrupt mode after no activity for this long */ + uint32_t poll_timeout_us; + /* + * minimum post-scan relax time. Once we finish a scan we check + * the time until we are due to start the next one. If this time is + * shorter this field, we use this instead. + */ + uint16_t min_post_scan_delay_us; + /* delay between setting up output and waiting for it to settle */ + uint16_t output_settle_us; + uint16_t debounce_down_us; /* time for debounce on key down */ + uint16_t debounce_up_us; /* time for debounce on key up */ + /* maximum depth to allow for fifo (0 = no keyscan output) */ + uint8_t fifo_max_depth; +} __packed; + +struct ec_params_cros_ec_set_config { + struct ec_cros_ec_config config; +} __packed; + +struct ec_response_cros_ec_get_config { + struct ec_cros_ec_config config; +} __packed; + +/* Run the key scan emulation */ +#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66 + +enum ec_keyscan_seq_cmd { + EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */ + EC_KEYSCAN_SEQ_CLEAR = 1, /* Clear sequence */ + EC_KEYSCAN_SEQ_ADD = 2, /* Add item to sequence */ + EC_KEYSCAN_SEQ_START = 3, /* Start running sequence */ + EC_KEYSCAN_SEQ_COLLECT = 4, /* Collect sequence summary data */ +}; + +enum ec_collect_flags { + /* + * Indicates this scan was processed by the EC. Due to timing, some + * scans may be skipped. + */ + EC_KEYSCAN_SEQ_FLAG_DONE = 1 << 0, +}; + +struct ec_collect_item { + uint8_t flags; /* some flags (enum ec_collect_flags) */ +}; + +struct ec_params_keyscan_seq_ctrl { + uint8_t cmd; /* Command to send (enum ec_keyscan_seq_cmd) */ + union { + struct { + uint8_t active; /* still active */ + uint8_t num_items; /* number of items */ + /* Current item being presented */ + uint8_t cur_item; + } status; + struct { + /* + * Absolute time for this scan, measured from the + * start of the sequence. + */ + uint32_t time_us; + uint8_t scan[0]; /* keyscan data */ + } add; + struct { + uint8_t start_item; /* First item to return */ + uint8_t num_items; /* Number of items to return */ + } collect; + }; +} __packed; + +struct ec_result_keyscan_seq_ctrl { + union { + struct { + uint8_t num_items; /* Number of items */ + /* Data for each item */ + struct ec_collect_item item[0]; + } collect; + }; +} __packed; + +/*****************************************************************************/ +/* Temperature sensor commands */ + +/* Read temperature sensor info */ +#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70 + +struct ec_params_temp_sensor_get_info { + uint8_t id; +} __packed; + +struct ec_response_temp_sensor_get_info { + char sensor_name[32]; + uint8_t sensor_type; +} __packed; + +/*****************************************************************************/ + +/* + * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI + * commands accidentally sent to the wrong interface. See the ACPI section + * below. + */ + +/*****************************************************************************/ +/* Host event commands */ + +/* + * Host event mask params and response structures, shared by all of the host + * event commands below. + */ +struct ec_params_host_event_mask { + uint32_t mask; +} __packed; + +struct ec_response_host_event_mask { + uint32_t mask; +} __packed; + +/* These all use ec_response_host_event_mask */ +#define EC_CMD_HOST_EVENT_GET_B 0x87 +#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x88 +#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x89 +#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d + +/* These all use ec_params_host_event_mask */ +#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x8a +#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x8b +#define EC_CMD_HOST_EVENT_CLEAR 0x8c +#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e +#define EC_CMD_HOST_EVENT_CLEAR_B 0x8f + +/*****************************************************************************/ +/* Switch commands */ + +/* Enable/disable LCD backlight */ +#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90 + +struct ec_params_switch_enable_backlight { + uint8_t enabled; +} __packed; + +/* Enable/disable WLAN/Bluetooth */ +#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91 + +struct ec_params_switch_enable_wireless { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* GPIO commands. Only available on EC if write protect has been disabled. */ + +/* Set GPIO output value */ +#define EC_CMD_GPIO_SET 0x92 + +struct ec_params_gpio_set { + char name[32]; + uint8_t val; +} __packed; + +/* Get GPIO value */ +#define EC_CMD_GPIO_GET 0x93 + +struct ec_params_gpio_get { + char name[32]; +} __packed; +struct ec_response_gpio_get { + uint8_t val; +} __packed; + +/*****************************************************************************/ +/* I2C commands. Only available when flash write protect is unlocked. */ + +/* Read I2C bus */ +#define EC_CMD_I2C_READ 0x94 + +struct ec_params_i2c_read { + uint16_t addr; + uint8_t read_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; +struct ec_response_i2c_read { + uint16_t data; +} __packed; + +/* Write I2C bus */ +#define EC_CMD_I2C_WRITE 0x95 + +struct ec_params_i2c_write { + uint16_t data; + uint16_t addr; + uint8_t write_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; + +/*****************************************************************************/ +/* Charge state commands. Only available when flash write protect unlocked. */ + +/* Force charge state machine to stop in idle mode */ +#define EC_CMD_CHARGE_FORCE_IDLE 0x96 + +struct ec_params_force_idle { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* Console commands. Only available when flash write protect is unlocked. */ + +/* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */ +#define EC_CMD_CONSOLE_SNAPSHOT 0x97 + +/* + * Read next chunk of data from saved snapshot. + * + * Response is null-terminated string. Empty string, if there is no more + * remaining output. + */ +#define EC_CMD_CONSOLE_READ 0x98 + +/*****************************************************************************/ + +/* + * Cut off battery power output if the battery supports. + * + * For unsupported battery, just don't implement this command and lets EC + * return EC_RES_INVALID_COMMAND. + */ +#define EC_CMD_BATTERY_CUT_OFF 0x99 + +/*****************************************************************************/ +/* USB port mux control. */ + +/* + * Switch USB mux or return to automatic switching. + */ +#define EC_CMD_USB_MUX 0x9a + +struct ec_params_usb_mux { + uint8_t mux; +} __packed; + +/*****************************************************************************/ +/* LDOs / FETs control. */ + +enum ec_ldo_state { + EC_LDO_STATE_OFF = 0, /* the LDO / FET is shut down */ + EC_LDO_STATE_ON = 1, /* the LDO / FET is ON / providing power */ +}; + +/* + * Switch on/off a LDO. + */ +#define EC_CMD_LDO_SET 0x9b + +struct ec_params_ldo_set { + uint8_t index; + uint8_t state; +} __packed; + +/* + * Get LDO state. + */ +#define EC_CMD_LDO_GET 0x9c + +struct ec_params_ldo_get { + uint8_t index; +} __packed; + +struct ec_response_ldo_get { + uint8_t state; +} __packed; + +/*****************************************************************************/ +/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */ + +/* + * Dump charge state machine context. + * + * Response is a binary dump of charge state machine context. + */ +#define EC_CMD_CHARGE_DUMP 0xa0 + +/* + * Set maximum battery charging current. + */ +#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1 + +struct ec_params_current_limit { + uint32_t limit; +} __packed; + +/*****************************************************************************/ +/* Smart battery pass-through */ + +/* Get / Set 16-bit smart battery registers */ +#define EC_CMD_SB_READ_WORD 0xb0 +#define EC_CMD_SB_WRITE_WORD 0xb1 + +/* Get / Set string smart battery parameters + * formatted as SMBUS "block". + */ +#define EC_CMD_SB_READ_BLOCK 0xb2 +#define EC_CMD_SB_WRITE_BLOCK 0xb3 + +struct ec_params_sb_rd { + uint8_t reg; +} __packed; + +struct ec_response_sb_rd_word { + uint16_t value; +} __packed; + +struct ec_params_sb_wr_word { + uint8_t reg; + uint16_t value; +} __packed; + +struct ec_response_sb_rd_block { + uint8_t data[32]; +} __packed; + +struct ec_params_sb_wr_block { + uint8_t reg; + uint16_t data[32]; +} __packed; + +/*****************************************************************************/ +/* System commands */ + +/* + * TODO: this is a confusing name, since it doesn't necessarily reboot the EC. + * Rename to "set image" or something similar. + */ +#define EC_CMD_REBOOT_EC 0xd2 + +/* Command */ +enum ec_reboot_cmd { + EC_REBOOT_CANCEL = 0, /* Cancel a pending reboot */ + EC_REBOOT_JUMP_RO = 1, /* Jump to RO without rebooting */ + EC_REBOOT_JUMP_RW = 2, /* Jump to RW without rebooting */ + /* (command 3 was jump to RW-B) */ + EC_REBOOT_COLD = 4, /* Cold-reboot */ + EC_REBOOT_DISABLE_JUMP = 5, /* Disable jump until next reboot */ + EC_REBOOT_HIBERNATE = 6 /* Hibernate EC */ +}; + +/* Flags for ec_params_reboot_ec.reboot_flags */ +#define EC_REBOOT_FLAG_RESERVED0 (1 << 0) /* Was recovery request */ +#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1) /* Reboot after AP shutdown */ + +struct ec_params_reboot_ec { + uint8_t cmd; /* enum ec_reboot_cmd */ + uint8_t flags; /* See EC_REBOOT_FLAG_* */ +} __packed; + +/* + * Get information on last EC panic. + * + * Returns variable-length platform-dependent panic information. See panic.h + * for details. + */ +#define EC_CMD_GET_PANIC_INFO 0xd3 + +/*****************************************************************************/ +/* + * ACPI commands + * + * These are valid ONLY on the ACPI command/data port. + */ + +/* + * ACPI Read Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_DATA bit to set + * - Read value from EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_READ 0x80 + +/* + * ACPI Write Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write value to EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_WRITE 0x81 + +/* + * ACPI Query Embedded Controller + * + * This clears the lowest-order bit in the currently pending host events, and + * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, + * event 0x80000000 = 32), or 0 if no event was pending. + */ +#define EC_CMD_ACPI_QUERY_EVENT 0x84 + +/* Valid addresses in ACPI memory space, for read/write commands */ +/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ +#define EC_ACPI_MEM_VERSION 0x00 +/* + * Test location; writing value here updates test compliment byte to (0xff - + * value). + */ +#define EC_ACPI_MEM_TEST 0x01 +/* Test compliment; writes here are ignored. */ +#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 +/* Keyboard backlight brightness percent (0 - 100) */ +#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 + +/* Current version of ACPI memory address space */ +#define EC_ACPI_MEM_VERSION_CURRENT 1 + + +/*****************************************************************************/ +/* + * Special commands + * + * These do not follow the normal rules for commands. See each command for + * details. + */ + +/* + * Reboot NOW + * + * This command will work even when the EC LPC interface is busy, because the + * reboot command is processed at interrupt level. Note that when the EC + * reboots, the host will reboot too, so there is no response to this command. + * + * Use EC_CMD_REBOOT_EC to reboot the EC more politely. + */ +#define EC_CMD_REBOOT 0xd1 /* Think "die" */ + +/* + * Resend last response (not supported on LPC). + * + * Returns EC_RES_UNAVAILABLE if there is no response available - for example, + * there was no previous command, or the previous command's response was too + * big to save. + */ +#define EC_CMD_RESEND_RESPONSE 0xdb + +/* + * This header byte on a command indicate version 0. Any header byte less + * than this means that we are talking to an old EC which doesn't support + * versioning. In that case, we assume version 0. + * + * Header bytes greater than this indicate a later version. For example, + * EC_CMD_VERSION0 + 1 means we are using version 1. + * + * The old EC interface must not use commands 0dc or higher. + */ +#define EC_CMD_VERSION0 0xdc + +#endif /* !__ACPI__ */ + +#endif /* __CROS_EC_COMMANDS_H */ diff --git a/include/fdtdec.h b/include/fdtdec.h index 42e9048..ca7cd04 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -84,6 +84,7 @@ enum fdt_compat_id { COMPAT_SAMSUNG_EXYNOS5_SOUND, /* Exynos Sound */ COMPAT_WOLFSON_WM8994_CODEC, /* Wolfson WM8994 Sound Codec */ COMPAT_SAMSUNG_EXYNOS_SPI, /* Exynos SPI */ + COMPAT_GOOGLE_CROS_EC, /* Google CROS_EC Protocol */ COMPAT_SAMSUNG_EXYNOS_EHCI, /* Exynos EHCI controller */ COMPAT_SAMSUNG_EXYNOS_USB_PHY, /* Exynos phy controller for usb2.0 */ COMPAT_SAMSUNG_EXYNOS_TMU, /* Exynos TMU */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 49bec81..21ed4f1 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -57,6 +57,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SAMSUNG_EXYNOS5_SOUND, "samsung,exynos-sound"), COMPAT(WOLFSON_WM8994_CODEC, "wolfson,wm8994-codec"), COMPAT(SAMSUNG_EXYNOS_SPI, "samsung,exynos-spi"), + COMPAT(GOOGLE_CROS_EC, "google,cros-ec"), COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"), COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"), COMPAT(SAMSUNG_EXYNOS_TMU, "samsung,exynos-tmu"),