From patchwork Wed May 15 10:27:31 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: 244009 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 5A8112C0098 for ; Wed, 15 May 2013 20:35:17 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C6FD64A029; Wed, 15 May 2013 12:35:15 +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 hTLEsHWieiRQ; Wed, 15 May 2013 12:35:15 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 83E024A030; Wed, 15 May 2013 12:35:13 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id AE8A24A030 for ; Wed, 15 May 2013 12:35:11 +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 c2qmXTlNTPUk for ; Wed, 15 May 2013 12:35:05 +0200 (CEST) X-Greylist: delayed 388 seconds by postgrey-1.27 at theia; Wed, 15 May 2013 12:34:54 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-vc0-f202.google.com (mail-vc0-f202.google.com [209.85.220.202]) by theia.denx.de (Postfix) with ESMTPS id 798BA4A029 for ; Wed, 15 May 2013 12:34:54 +0200 (CEST) Received: by mail-vc0-f202.google.com with SMTP id gf12so165559vcb.3 for ; Wed, 15 May 2013 03:34:53 -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=az6WSWPTJwIcckGKcB55blGkH/rD7U1zNJ9w8J+8C/g=; b=GDeWsEzRs4jo/2H2QwF/quIA2kjjr4rl/0Jcsmf5HOv8pAqIng8KDab1thTJwK24+l Y2MiPKoV1Tqcupbwq+YxPdMBvjNXuF/4CuVbmbIiUCUVbb98qtB1HUBcVMxQ+HG1cXdv Mo/a9wQ8f9TOQSICIB5CjfPuMfBr+9uIySAcLfPywjx67TMCiS4QKy756Yl8lbYMyVnN 76JFA9M4EebY+W6QfP2hNiQkJi5MJxIFBqfs1vMVoeZuaGmmwx1zEdq/90EPdjMACnpp gnZa8tu1shq7SPk8vCh8yqlnOEgRZ62pDKHBmf/7AyvvUKAAeIu53KvPEoBV/79N6HhV lc7A== X-Received: by 10.236.59.39 with SMTP id r27mr6062711yhc.47.1368613704888; Wed, 15 May 2013 03:28:24 -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 h2si127468yhj.2.2013.05.15.03.28.24 for (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Wed, 15 May 2013 03:28:24 -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 6ECE031C23A; Wed, 15 May 2013 03:28:24 -0700 (PDT) Received: by tyanh-z620.tpe.corp.google.com (Postfix, from userid 30259) id BCA6AE091F; Wed, 15 May 2013 18:28:23 +0800 (CST) From: Hung-ying Tyan To: U-Boot Mailing List Date: Wed, 15 May 2013 18:27:31 +0800 Message-Id: <1368613654-15229-5-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: ALoCoQmpPgvzlPvvhuA+GCNc87J9F6hga9y+ZDwcWQJIFZemB4dbiwDpaB6GedkZz9RPSC3j1yEWZHIf2PhwC3/Di/VDjnflHCcE+g8Z49GKH7cVU4hPJwqaA/ReMzsk6x1c2dSIWB65kKS7v0A/ePBLDJsSVRPcLXVB7oj36jO4Ju8Rp8tEQW1XvNjgJ4hxB+VOQF0myV7A Cc: Tom Rini , u-boot-review@google.com, Randall Spangler Subject: [U-Boot] [PATCH v5 4/7] cros: add LPC support for cros_ec 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 LPC support for carrying out the cros_ec protocol. Signed-off-by: Randall Spangler Signed-off-by: Simon Glass Signed-off-by: Hung-ying Tyan Acked-by: Simon Glass Tested-by: Simon Glass --- Changes in v5: None Changes in v4: None 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. drivers/misc/Makefile | 1 + drivers/misc/cros_ec_lpc.c | 283 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 drivers/misc/cros_ec_lpc.c diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 18209ec..3553ff6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -31,6 +31,7 @@ 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_CROS_EC_LPC) += cros_ec_lpc.o COBJS-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o COBJS-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o COBJS-$(CONFIG_NS87308) += ns87308.o diff --git a/drivers/misc/cros_ec_lpc.c b/drivers/misc/cros_ec_lpc.c new file mode 100644 index 0000000..cf0435b --- /dev/null +++ b/drivers/misc/cros_ec_lpc.c @@ -0,0 +1,283 @@ +/* + * Chromium OS cros_ec driver - LPC interface + * + * 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 + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, ##b) +#else +#define debug_trace(fmt, b...) +#endif + +static int wait_for_sync(struct cros_ec_dev *dev) +{ + unsigned long start; + + start = get_timer(0); + while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { + if (get_timer(start) > 1000) { + debug("%s: Timeout waiting for CROS_EC sync\n", + __func__); + return -1; + } + } + + return 0; +} + +/** + * 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 Place to put pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int old_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int ret, i; + + if (dout_len > EC_OLD_PARAM_SIZE) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + if (din_len > EC_OLD_PARAM_SIZE) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -1; + } + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + debug_trace("cmd: %02x, ", cmd); + for (i = 0; i < dout_len; i++) { + debug_trace("%02x ", dout[i]); + outb(dout[i], EC_LPC_ADDR_OLD_PARAM + i); + } + outb(cmd, EC_LPC_ADDR_HOST_CMD); + debug_trace("\n"); + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + ret = inb(EC_LPC_ADDR_HOST_DATA); + if (ret) { + debug("%s: CROS_EC result code %d\n", __func__, ret); + return -ret; + } + + debug_trace("resp: %02x, ", ret); + for (i = 0; i < din_len; i++) { + dev->din[i] = inb(EC_LPC_ADDR_OLD_PARAM + i); + debug_trace("%02x ", dev->din[i]); + } + debug_trace("\n"); + *dinp = dev->din; + + return din_len; +} + +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) +{ + const int cmd_addr = EC_LPC_ADDR_HOST_CMD; + const int data_addr = EC_LPC_ADDR_HOST_DATA; + const int args_addr = EC_LPC_ADDR_HOST_ARGS; + const int param_addr = EC_LPC_ADDR_HOST_PARAM; + + struct ec_lpc_host_args args; + uint8_t *d; + int csum; + int i; + + /* Fall back to old-style command interface if args aren't supported */ + if (!dev->cmd_version_is_supported) + return old_lpc_command(dev, cmd, dout, dout_len, dinp, + din_len); + + if (dout_len > EC_HOST_PARAM_SIZE) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + /* Fill in args */ + args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; + args.command_version = cmd_version; + args.data_size = dout_len; + + /* Calculate checksum */ + csum = cmd + args.flags + args.command_version + args.data_size; + for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) + csum += *d; + + args.checksum = (uint8_t)csum; + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + /* Write args */ + for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) + outb(*d, args_addr + i); + + /* Write data, if any */ + debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version); + for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) { + outb(*d, param_addr + i); + debug_trace("%02x ", *d); + } + + outb(cmd, cmd_addr); + debug_trace("\n"); + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting for response\n", __func__); + return -1; + } + + /* Check result */ + i = inb(data_addr); + if (i) { + debug("%s: CROS_EC result code %d\n", __func__, i); + return -i; + } + + /* Read back args */ + for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) + *d = inb(args_addr + i); + + /* + * If EC didn't modify args flags, then somehow we sent a new-style + * command to an old EC, which means it would have read its params + * from the wrong place. + */ + if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { + debug("%s: CROS_EC protocol mismatch\n", __func__); + return -EC_RES_INVALID_RESPONSE; + } + + if (args.data_size > din_len) { + debug("%s: CROS_EC returned too much data %d > %d\n", + __func__, args.data_size, din_len); + return -EC_RES_INVALID_RESPONSE; + } + + /* Read data, if any */ + for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) { + *d = inb(param_addr + i); + debug_trace("%02x ", *d); + } + debug_trace("\n"); + + /* Verify checksum */ + csum = cmd + args.flags + args.command_version + args.data_size; + for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) + csum += *d; + + if (args.checksum != (uint8_t)csum) { + debug("%s: CROS_EC response has invalid checksum\n", __func__); + return -EC_RES_INVALID_CHECKSUM; + } + *dinp = dev->din; + + /* Return actual amount of data received */ + return args.data_size; +} + +/** + * Initialize LPC protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) +{ + int byte, i; + + /* See if we can find an EC at the other end */ + byte = 0xff; + byte &= inb(EC_LPC_ADDR_HOST_CMD); + byte &= inb(EC_LPC_ADDR_HOST_DATA); + for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++) + byte &= inb(EC_LPC_ADDR_HOST_PARAM + i); + if (byte == 0xff) { + debug("%s: CROS_EC device not found on LPC bus\n", + __func__); + return -1; + } + + return 0; +} + +/* + * Test if LPC command args are supported. + * + * The cheapest way to do this is by looking for the memory-mapped + * flag. This is faster than sending a new-style 'hello' command and + * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag + * in args when it responds. + */ +int cros_ec_lpc_check_version(struct cros_ec_dev *dev) +{ + if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' && + inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) + == 'C' && + (inb(EC_LPC_ADDR_MEMMAP + + EC_MEMMAP_HOST_CMD_FLAGS) & + EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { + dev->cmd_version_is_supported = 1; + } else { + /* We are going to use the old IO ports */ + dev->cmd_version_is_supported = 0; + } + debug("lpc: version %s\n", dev->cmd_version_is_supported ? + "new" : "old"); + + return 0; +}