From patchwork Tue Apr 2 10:01:51 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: 232930 X-Patchwork-Delegate: trini@ti.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 C04672C0144 for ; Tue, 2 Apr 2013 21:03:07 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 683D74A200; Tue, 2 Apr 2013 12:03:06 +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 wrPY8JBDYfci; Tue, 2 Apr 2013 12:03:06 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4C1584A209; Tue, 2 Apr 2013 12:03:00 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C39A24A20A for ; Tue, 2 Apr 2013 12:02:57 +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 gXMSgdb7xJe1 for ; Tue, 2 Apr 2013 12:02:56 +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-oa0-f73.google.com (mail-oa0-f73.google.com [209.85.219.73]) by theia.denx.de (Postfix) with ESMTPS id 453524A200 for ; Tue, 2 Apr 2013 12:02:54 +0200 (CEST) Received: by mail-oa0-f73.google.com with SMTP id o6so44773oag.2 for ; Tue, 02 Apr 2013 03:02: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=6owBAZXRBAtgFVgIDyjdMxlNk1ZIU2I0LiJWnwnSxMY=; b=Ukbfz9LewFLoatpcLkeeIxHnkXB26zMppgJVlOrRlKF9k242xxd/9bbgrMWtG2DRAo LL+0We5Y2dANugaoEaV5x+9agyGbxh+6tilQTqeBuWtIQ+vvKTCnKyTEytrIM+uHDj3M 0b7+MbadT/q6CO2vhjHUdaTHjLnhX/lee0uCEwn+SaO1l36ToHu7Q+3hMJ4BcIevSJ2O 0ryPcGb8WfictdxPVweuqwmDVTi5AVXK2dBw27EWYEPfocPUtOho0EbylF0Z55PDkbZq ek10XsZyT9YSsq8GraJboFzZNn2U81wqJxn4FWERsjXagXQ4IuDFFMDQnOb39I+aZLnB r+SQ== X-Received: by 10.50.170.100 with SMTP id al4mr10037509igc.6.1364896973340; Tue, 02 Apr 2013 03:02:53 -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 iw8si495886igc.1.2013.04.02.03.02.53 (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Tue, 02 Apr 2013 03:02:53 -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 49F9D31C1C6; Tue, 2 Apr 2013 03:02:52 -0700 (PDT) Received: by tyanh-z620.tpe.corp.google.com (Postfix, from userid 30259) id 72671E18FC; Tue, 2 Apr 2013 18:02:51 +0800 (CST) From: Hung-ying Tyan To: U-Boot Mailing List Date: Tue, 2 Apr 2013 18:01:51 +0800 Message-Id: <1364896914-17868-5-git-send-email-tyanh@chromium.org> X-Mailer: git-send-email 1.8.1.3 In-Reply-To: <1364896914-17868-1-git-send-email-tyanh@chromium.org> References: <1364896914-17868-1-git-send-email-tyanh@chromium.org> X-Gm-Message-State: ALoCoQlrEOGhN8TR3cVLqPmlFH/TwaJIOHwWfDTCYateO1UQs7li1DrxoixOIJtTMJ5ZTLDq5rftXc5mU7m6ikVaOpRsQA0Co3OTO01Jl0WwEGHH/t18hSMCkIGdG6P7VNjww8xQfnXfa9D6Us6pGgCFoPIJmlKn8IFEz1Nm0FYnA7nUnZjvlsScCBeFz2eN1duCUlkiLlKg Cc: Tom Rini , u-boot-review@google.com, Randall Spangler Subject: [U-Boot] [PATCH v4 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 --- 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; +}