From patchwork Fri Jul 19 08:47:41 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Danin X-Patchwork-Id: 260240 X-Patchwork-Delegate: twarren@nvidia.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 671A92C007E for ; Fri, 19 Jul 2013 20:57:57 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 446D14A040; Fri, 19 Jul 2013 12:57:47 +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 2AJiqyIXbTWR; Fri, 19 Jul 2013 12:57:47 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 04F7E4A041; Fri, 19 Jul 2013 12:57:18 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5B0BA4A01B for ; Fri, 19 Jul 2013 10:58:35 +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 KMVt5FiE8g3c for ; Fri, 19 Jul 2013 10:58:30 +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 fallback7.mail.ru (fallback7.mail.ru [94.100.176.135]) by theia.denx.de (Postfix) with ESMTP id 82AE04A01E for ; Fri, 19 Jul 2013 10:58:20 +0200 (CEST) Received: from smtp41.i.mail.ru (smtp41.i.mail.ru [94.100.177.101]) by fallback7.mail.ru (mPOP.Fallback_MX) with ESMTP id 7EE4FDB09382 for ; Fri, 19 Jul 2013 12:51:04 +0400 (MSK) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=0twwKg1jMGw1LKIZt/Nsh4RTFOgwQc8XOpwMJY7fxDg=; b=EyKVaA1jlXG+yLJp1EcKug7FvpLFoaZmiUThd6fM3s9GGL4AhCZgY4NS0c3n6UCYK5Ts4H9UjMtDBZFLhc8Yzq1JAQv/snPRrqG1SLyfu9LLI/pq9XwoU8Hz8sHjeoKuo2ki4FAIvoTEI5L+vr7ieAAzSg8XD80jVCph/STKsds=; Received: from [195.214.232.10] (port=41257 helo=rhel62vi.sw.ru) by smtp41.i.mail.ru with esmtpa (envelope-from ) id 1V06Ow-0002nc-2F; Fri, 19 Jul 2013 12:50:58 +0400 From: Andrey Danin To: Tom Warren , u-boot@lists.denx.de Date: Fri, 19 Jul 2013 12:47:41 +0400 Message-Id: <1374223663-8576-2-git-send-email-danindrey@mail.ru> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1374223663-8576-1-git-send-email-danindrey@mail.ru> References: <1374223663-8576-1-git-send-email-danindrey@mail.ru> X-Spam: Not detected X-Mras: Ok X-Mailman-Approved-At: Fri, 19 Jul 2013 12:57:12 +0200 Cc: Julian Andres Klode , Stephen Warren , ac100@lists.launchpad.net Subject: [U-Boot] [PATCH 1/3] ARM: tegra: add nvec 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 Adopted version of nvec driver from linux kernel with keyboard support only. Signed-off-by: Andrey Danin --- arch/arm/include/asm/arch-tegra/tegra_nvec.h | 117 +++++ .../arm/include/asm/arch-tegra/tegra_nvec_events.h | 31 ++ .../include/asm/arch-tegra/tegra_nvec_keyboard.h | 36 ++ .../include/asm/arch-tegra/tegra_nvec_keytable.h | 313 +++++++++++++ board/nvidia/common/board.c | 12 + drivers/i2c/Makefile | 1 + drivers/i2c/tegra_nvec.c | 462 ++++++++++++++++++++ drivers/i2c/tegra_nvec_keyboard.c | 108 +++++ include/fdtdec.h | 1 + lib/Makefile | 1 + lib/fdtdec.c | 1 + 11 files changed, 1083 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec.h create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec_events.h create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec_keytable.h create mode 100644 drivers/i2c/tegra_nvec.c create mode 100644 drivers/i2c/tegra_nvec_keyboard.c diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec.h b/arch/arm/include/asm/arch-tegra/tegra_nvec.h new file mode 100644 index 0000000..1c30028 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec.h @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2013 + * Andrey Danin + * + * 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 _TEGRA_NVEC_H_ +#define _TEGRA_NVEC_H_ + +#define I2C_CNFG 0x00 +#define I2C_CNFG_PACKET_MODE_EN (1<<10) +#define I2C_CNFG_NEW_MASTER_SFM (1<<11) +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 + +#define I2C_SL_CNFG 0x20 +#define I2C_SL_NEWSL (1<<2) +#define I2C_SL_NACK (1<<1) +#define I2C_SL_RESP (1<<0) +#define I2C_SL_IRQ (1<<3) +#define END_TRANS (1<<4) +#define RCVD (1<<2) +#define RNW (1<<1) + +#define I2C_SL_RCVD 0x24 +#define I2C_SL_STATUS 0x28 +#define I2C_SL_ADDR1 0x2c +#define I2C_SL_ADDR2 0x30 +#define I2C_SL_DELAY_COUNT 0x3c + + +enum nvec_msg_type { + NVEC_KEYBOARD = 0, + NVEC_SYS = 1, + NVEC_BAT, + NVEC_GPIO, + NVEC_SLEEP, + NVEC_KBD, + NVEC_PS2, + NVEC_CNTL, + NVEC_OEM0 = 0x0d, + NVEC_KB_EVT = 0x80, + NVEC_PS2_EVT, +}; + +enum nvec_event_size { + NVEC_2BYTES, + NVEC_3BYTES, + NVEC_VAR_SIZE, +}; + +enum sys_subcmds { + SYS_GET_STATUS, + SYS_CNFG_EVENT_REPORTING, + SYS_ACK_STATUS, + SYS_CNFG_WAKE = 0xfd, +}; + +enum kbd_subcmds { + CNFG_WAKE = 3, + CNFG_WAKE_KEY_REPORTING, + SET_LEDS = 0xed, + ENABLE_KBD = 0xf4, + DISABLE_KBD, +}; + +enum cntl_subcmds { + CNTL_RESET_EC = 0x00, + CNTL_SELF_TEST = 0x01, + CNTL_NOOP = 0x02, + CNTL_GET_EC_SPEC_VER = 0x10, + CNTL_GET_FIRMWARE_VERSION = 0x15, +}; + +enum nvec_sleep_subcmds { + GLOBAL_EVENTS, + AP_PWR_DOWN, + AP_SUSPEND, +}; + +#define MOUSE_SEND_CMD 0x01 +#define MOUSE_RESET 0xff + + +int board_nvec_init(void); + +int nvec_msg_is_event(const unsigned char *msg); +int nvec_msg_event_type(const unsigned char *msg); + +/** + * Send request and read response. If write or read failed + * operation will be repeated NVEC_ATTEMPTS_MAX times. + * + * @param buf request data + * @param size request data size + * @return 0 if ok, -1 on error + */ +int nvec_do_request(char *buf, int size); + + +#endif /* _TEGRA_NVEC_H_ */ diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec_events.h b/arch/arm/include/asm/arch-tegra/tegra_nvec_events.h new file mode 100644 index 0000000..7d65921 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec_events.h @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2013 + * Andrey Danin + * + * 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 _TEGRA_NVEC_EVENTS_H_ +#define _TEGRA_NVEC_EVENTS_H_ + + +int nvec_read_events(void); + + +#endif /* _TEGRA_NVEC_EVENTS_H_ */ diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h b/arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h new file mode 100644 index 0000000..b25a1d8 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec_keyboard.h @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2013 + * Andrey Danin + * + * 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 _TEGRA_NVEC_KEYBOARD_H_ +#define _TEGRA_NVEC_KEYBOARD_H_ + + +#define NVEC_KEYS_QUEUE_SIZE 256 + +void nvec_enable_kbd_events(void); +void nvec_process_keyboard_msg(const unsigned char *msg); +int nvec_pop_key(void); +int nvec_have_keys(void); + + +#endif /* _TEGRA_NVEC_KEYBOARD_H_ */ diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec_keytable.h b/arch/arm/include/asm/arch-tegra/tegra_nvec_keytable.h new file mode 100644 index 0000000..8fbf96c --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec_keytable.h @@ -0,0 +1,313 @@ +/* + * Keyboard class input driver for keyboards connected to an NvEc compliant + * embedded controller + * + * Copyright (c) 2009, NVIDIA Corporation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _TEGRA_NVEC_KEYTABLE_H_ +#define _TEGRA_NVEC_KEYTABLE_H_ + +#include + + +static unsigned short code_tab_102us[] = { + /* 0x00 */ + KEY_GRAVE, + KEY_ESC, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_0, + KEY_MINUS, + KEY_EQUAL, + KEY_BACKSPACE, + KEY_TAB, + /* 0x10 */ + KEY_Q, + KEY_W, + KEY_E, + KEY_R, + KEY_T, + KEY_Y, + KEY_U, + KEY_I, + KEY_O, + KEY_P, + KEY_LEFTBRACE, + KEY_RIGHTBRACE, + KEY_ENTER, + KEY_LEFTCTRL, + KEY_A, + KEY_S, + /* 0x20 */ + KEY_D, + KEY_F, + KEY_G, + KEY_H, + KEY_J, + KEY_K, + KEY_L, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_GRAVE, + KEY_LEFTSHIFT, + KEY_BACKSLASH, + KEY_Z, + KEY_X, + KEY_C, + KEY_V, + /* 0x30 */ + KEY_B, + KEY_N, + KEY_M, + KEY_COMMA, + KEY_DOT, + KEY_SLASH, + KEY_RIGHTSHIFT, + KEY_KPASTERISK, + KEY_LEFTALT, + KEY_SPACE, + KEY_CAPSLOCK, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + /* 0x40 */ + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_FN, + /* VK_SCROLL */ + 0, + KEY_KP7, + KEY_KP8, + KEY_KP9, + KEY_KPMINUS, + KEY_KP4, + KEY_KP5, + KEY_KP6, + KEY_KPPLUS, + KEY_KP1, + /* 0x50 */ + KEY_KP2, + KEY_KP3, + KEY_KP0, + KEY_KPDOT, + /* VK_SNAPSHOT */ + 0, /* KEY_MENU, */ + KEY_POWER, + /* VK_OEM_102 */ + KEY_102ND, + KEY_F11, + KEY_F12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + /* 0x60 */ + 0, + 0, + 0, + 0, /* KEY_SEARCH, */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + /* 0x70 */ + 0, + 0, + 0, + KEY_KP5, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + KEY_KP9, +}; + +static unsigned short extcode_tab_us102[] = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + /* 0x10 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + /* VK_MEDIA_NEXT_TRACK */ + 0, + 0, + 0, + /* VK_RETURN */ + 0, + KEY_RIGHTCTRL, + 0, + 0, + /* 0x20 */ + KEY_MUTE, + /* VK_LAUNCH_APP1 */ + 0, + /* VK_MEDIA_PLAY_PAUSE */ + 0, + 0, + /* VK_MEDIA_STOP */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + /* 0x30 */ + KEY_VOLUMEUP, + 0, + /* VK_BROWSER_HOME */ + 0, + 0, + 0, + /* VK_DIVIDE */ + KEY_KPSLASH, + 0, + /* VK_SNAPSHOT */ + KEY_SYSRQ, + /* VK_RMENU */ + KEY_RIGHTALT, + /* VK_OEM_NV_BACKLIGHT_UP */ + 0, + /* VK_OEM_NV_BACKLIGHT_DN */ + 0, + /* VK_OEM_NV_BACKLIGHT_AUTOTOGGLE */ + 0, + /* VK_OEM_NV_POWER_INFO */ + 0, + /* VK_OEM_NV_WIFI_TOGGLE */ + 0, + /* VK_OEM_NV_DISPLAY_SELECT */ + 0, + /* VK_OEM_NV_AIRPLANE_TOGGLE */ + 0, + /* 0x40 */ + 0, + KEY_LEFT, + 0, + 0, + 0, + 0, + 0, /* KEY_CANCEL, */ + KEY_HOME, + KEY_UP, + KEY_PAGEUP, + 0, + KEY_LEFT, + 0, + KEY_RIGHT, + 0, + KEY_END, + /* 0x50 */ + KEY_DOWN, + KEY_PAGEDOWN, + KEY_INSERT, + KEY_DELETE, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + KEY_LEFTMETA, + 0, + KEY_ESC, + KEY_KPMINUS, + 0, + 0, + 0, + 0, + 0, + 0, + /* VK_BROWSER_SEARCH */ + 0, + /* VK_BROWSER_FAVORITES */ + 0, + /* VK_BROWSER_REFRESH */ + 0, + /* VK_BROWSER_STOP */ + 0, + /* VK_BROWSER_FORWARD */ + 0, + /* VK_BROWSER_BACK */ + 0, + /* VK_LAUNCH_APP2 */ + 0, + /* VK_LAUNCH_MAIL */ + 0, + /* VK_LAUNCH_MEDIA_SELECT */ + 0, +}; + +static unsigned short *code_tabs[] = { code_tab_102us, extcode_tab_us102 }; + +#endif /* _TEGRA_NVEC_KEYTABLE_H_ */ diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index f60f21f..e5da061 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -53,6 +53,9 @@ #include #include #endif +#ifdef CONFIG_TEGRA_NVEC +#include +#endif #include #include #include "emc.h" @@ -217,10 +220,19 @@ int board_early_init_f(void) int board_late_init(void) { + __maybe_unused int err; + #ifdef CONFIG_LCD /* Make sure we finish initing the LCD */ tegra_lcd_check_next_stage(gd->fdt_blob, 1); #endif + +#ifdef CONFIG_TEGRA_NVEC + err = board_nvec_init(); + if (err) + debug("NVEC controller init failed: %d\n", err); +#endif + return 0; } diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 72e85a3..6ff3b22 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -42,6 +42,7 @@ COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o +COBJS-$(CONFIG_TEGRA_NVEC) += tegra_nvec.o tegra_nvec_keyboard.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o diff --git a/drivers/i2c/tegra_nvec.c b/drivers/i2c/tegra_nvec.c new file mode 100644 index 0000000..1589003 --- /dev/null +++ b/drivers/i2c/tegra_nvec.c @@ -0,0 +1,462 @@ +/* + * (C) Copyright 2013 + * Andrey Danin + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_TEGRA_NVEC +#error "You should enable CONFIG_TEGRA_NVEC" +#endif + +DECLARE_GLOBAL_DATA_PTR; + + +/* Nvec perfroms io interval is beteween 20 and 500 ms, +no response in 600 ms means error */ +enum { + NVEC_TIMEOUT_MIN = 20, + NVEC_TIMEOUT_MAX = 600, +}; +enum { + NVEC_WAIT_FOR_EC = 1, + NVEC_DONT_WAIT_FOR_EC = 0, + NVEC_ATTEMPTS_MAX = 10, +}; + +enum { + nvec_io_error = -1, + nvec_io_timeout, + nvec_io_read_ok, + nvec_io_write_ok, + nvec_io_not_ready, + nvec_io_retry, +}; + +enum { + NVST_BEGIN = 0, + NVST_CMD = 1, + NVST_SUBCMD = 2, + NVST_READ = 3, + NVST_WRITE_SIZE = 4, + NVST_WRITE = 5, +}; + +struct nvec_t { + int gpio; + int i2c_addr; + int i2c_clk; + void __iomem *base; + int state; + char rx_buf[34]; + int rx_pos; + char *tx_buf; + int tx_pos; + int tx_size; +} nvec_data; + +struct fdt_nvec_config { + int gpio; + int i2c_addr; + int i2c_clk; + fdt_addr_t base_addr; + struct fdt_gpio_state request_gpio; +}; + + +/* nvec commands */ +char noop[] = { NVEC_CNTL, CNTL_NOOP }; + + +int nvec_msg_is_event(const unsigned char *msg) +{ + return msg[0] >> 7; +} + + +int nvec_msg_event_type(const unsigned char *msg) +{ + return msg[0] & 0x0f; +} + + +/** + * Process incoming io message. + * If message is keyboard event then key code will + * be added to keys buffer. + * + * See: nvec_push_key, nvec_pop_key, nvec_have_key + * + * @param nvec nvec state struct + */ +void nvec_process_msg(struct nvec_t *nvec) +{ + const unsigned char *msg = (const unsigned char *)nvec->rx_buf; + int event_type; + + if (!nvec_msg_is_event(msg)) + return; + + event_type = nvec_msg_event_type(msg); + if (event_type == NVEC_KEYBOARD) + nvec_process_keyboard_msg(msg); +} + + +static inline int is_read(unsigned long status) +{ + return (status & RNW) == 0; +} + + +static inline int is_ready(unsigned long status) +{ + return status & I2C_SL_IRQ; +} + + +/** + * Perform complete io operation (read or write). + * NOTE: function will wait NVEC_TIMEOUT_MIN (20ms) + * before status check to avoid nvec hang. + * + * @param nvec nvec state struct + * @param wait_for_ec if 1(NVEC_WAIT_FOR_EC) operation + * timeout is NVEC_TIMEOUT_MAX (600ms), + * otherwise function will return if io + * is not ready. + * + * @return nvec_io_* code + */ +int nvec_do_io(struct nvec_t *nvec, int wait_for_ec) +{ + unsigned int poll_start_ms = 0; + unsigned long status; + unsigned int received = 0; + unsigned int to_send = 0; + unsigned int timeout_ms = NVEC_TIMEOUT_MAX; + int is_first_iteration = 1; + + poll_start_ms = get_timer(0); + mdelay(NVEC_TIMEOUT_MIN); + + while (1) { + status = readl(nvec->base + I2C_SL_STATUS); + if (!is_ready(status)) { + if (is_first_iteration && !wait_for_ec) + return nvec_io_not_ready; + + if (get_timer(poll_start_ms) > timeout_ms) + return nvec_io_timeout; + + is_first_iteration = 0; + udelay(100); + continue; + } + is_first_iteration = 0; + + if (is_read(status)) + received = readl(nvec->base + I2C_SL_RCVD); + + if (status == (I2C_SL_IRQ | RCVD)) { + nvec->state = NVST_BEGIN; + nvec->rx_pos = 0; + nvec->tx_pos = 0; + } + + switch (nvec->state) { + case NVST_BEGIN: + nvec->rx_pos = 0; + nvec->tx_pos = 0; + if (received != nvec->i2c_addr) { + error("NVEC io: unknown addr 0x%x\n", received); + return nvec_io_error; + } + nvec->state = NVST_CMD; + break; + + case NVST_CMD: + nvec->rx_buf[nvec->rx_pos++] = (char)received; + nvec->state = NVST_SUBCMD; + break; + + case NVST_SUBCMD: + if (status == (I2C_SL_IRQ | RNW | RCVD)) { + if (nvec->rx_buf[0] != 0x01) { + error("NVEC io: wrong read\n"); + nvec->state = NVST_BEGIN; + return nvec_io_error; + } + nvec->state = NVST_WRITE; + if (nvec->tx_buf == 0) { + debug("NVEC io: error, tx buffer is 0\n"); + nvec->tx_buf = noop; + nvec->tx_size = 2; + nvec->tx_pos = 0; + } + to_send = nvec->tx_size; + writel(to_send, nvec->base + I2C_SL_RCVD); + gpio_set_value(nvec_data.gpio, 1); + nvec->state = NVST_WRITE; + } else { + nvec->state = NVST_READ; + nvec->rx_buf[nvec->rx_pos] = (char)received; + ++nvec->rx_pos; + } + break; + + case NVST_READ: + if (nvec->rx_pos >= 34) { + error("NVEC io: read buffer is full\n"); + break; + } + nvec->rx_buf[nvec->rx_pos++] = (char)received; + if (status & END_TRANS) { + nvec_process_msg(nvec); + nvec->rx_pos = 0; + return nvec_io_read_ok; + } + break; + + case NVST_WRITE_SIZE: + to_send = nvec->tx_size; + writel(to_send, nvec->base + I2C_SL_RCVD); + nvec->state = NVST_WRITE; + break; + + case NVST_WRITE: + if (nvec->tx_pos >= nvec->tx_size) { + if (status & END_TRANS) + return nvec_io_write_ok; + + error("NVEC io: no data to write\n"); + return nvec_io_error; + } + to_send = nvec->tx_buf[nvec->tx_pos++]; + writel(to_send, nvec->base + I2C_SL_RCVD); + if (status & END_TRANS) { + nvec->tx_pos = 0; + nvec->tx_buf = 0; + return nvec_io_write_ok; + } + + break; + + default: + error("NVEC io: unknown state\n"); + break; + } + if (status & END_TRANS) + return nvec_io_retry; + } +} + + +/** + * Send request and read response. If write or read failed + * operation will be repeated NVEC_ATTEMPTS_MAX times. + * + * @param buf request data + * @param size request data size + * @return 0 if ok, -1 on error + */ +int nvec_do_request(char *buf, int size) +{ + int res = 0; + int i = 0; + + nvec_data.tx_buf = buf; + nvec_data.tx_size = size; + + while (i++ < NVEC_ATTEMPTS_MAX) { + nvec_data.tx_pos = 0; + + /* request */ + gpio_set_value(nvec_data.gpio, 0); + res = nvec_do_io(&nvec_data, NVEC_WAIT_FOR_EC); + if (res != nvec_io_write_ok) { + debug("warning: nvec failed to send request\n"); + continue; + } + + /* response */ + res = nvec_do_io(&nvec_data, NVEC_WAIT_FOR_EC); + if (res != nvec_io_read_ok) { + debug("warning: nvec failed to read response\n"); + continue; + } + + nvec_data.tx_buf = 0; + nvec_data.tx_size = 0; + nvec_data.tx_pos = 0; + + return 0; + } + + error("nvec failed to perform request\n"); + return -1; +} + + +/** + * Init i2c controller to operate in slave mode. + * + * @param nvec nvec state struct + */ +static void nvec_init_i2c_slave(struct nvec_t *nvec) +{ + unsigned long val; + + val = I2C_CNFG_NEW_MASTER_SFM | I2C_CNFG_PACKET_MODE_EN | + (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); + writel(val, nvec->base + I2C_CNFG); + + /* i2c3 -> 67 */ + clock_start_periph_pll(67, CLOCK_ID_PERIPH, + nvec->i2c_clk * 8); + + reset_periph(67, 1); + + writel(I2C_SL_NEWSL, nvec->base + I2C_SL_CNFG); + writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT); + + writel(nvec->i2c_addr>>1, nvec->base + I2C_SL_ADDR1); + writel(0, nvec->base + I2C_SL_ADDR2); + + funcmux_select(67, FUNCMUX_DEFAULT); +} + + +/** + * Decode the nvec information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int nvec_decode_config(const void *blob, + struct fdt_nvec_config *config) +{ + int node; + + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_NVEC); + if (node < 0) { + error("Cannot find NVEC node in fdt\n"); + return node; + } + + config->base_addr = fdtdec_get_addr(blob, node, "reg"); + if (config->base_addr == FDT_ADDR_T_NONE) { + error("No NVEC controller address\n"); + return -1; + } + + if (fdtdec_decode_gpio(blob, node, "request-gpios", + &config->request_gpio)) { + error("No NVEC request gpio\n"); + return -1; + } + + config->i2c_addr = fdtdec_get_int(blob, node, "slave-addr", -1); + config->i2c_clk = fdtdec_get_int(blob, node, "clock-frequency", -1); + + return 0; +} + + +int board_nvec_init(void) +{ + int res = 0; + + struct fdt_nvec_config cfg; + if (nvec_decode_config(gd->fdt_blob, &cfg)) { + debug("Can't parse NVEC node in device tree\n"); + return -1; + } + + nvec_data.rx_pos = 0; + nvec_data.tx_buf = 0; + nvec_data.tx_pos = 0; + nvec_data.tx_size = 0; + nvec_data.state = NVST_BEGIN; + + nvec_data.gpio = cfg.request_gpio.gpio; + nvec_data.i2c_addr = cfg.i2c_addr; + nvec_data.i2c_clk = cfg.i2c_clk; + nvec_data.base = (void __iomem *)cfg.base_addr; + + debug("NVEC initialization...\n"); + + res = gpio_request(nvec_data.gpio, NULL); + if (res != 0) + error("NVEC: err, gpio_request\n"); + res = gpio_direction_output(nvec_data.gpio, 1); + if (res != 0) + error("NVEC: err, gpio_direction\n"); + res = gpio_set_value(nvec_data.gpio, 1); + if (res != 0) + error("NVEC: err, gpio_set_value\n"); + udelay(100); + + nvec_init_i2c_slave(&nvec_data); + + nvec_enable_kbd_events(); + + return 1; +} + + +int nvec_read_events(void) +{ + int res; + int cnt = 0; + + while (++cnt <= 8) { + res = nvec_do_io(&nvec_data, NVEC_DONT_WAIT_FOR_EC); + switch (res) { + case nvec_io_not_ready: + return 0; + + case nvec_io_read_ok: + case nvec_io_retry: + break; + + case nvec_io_error: + case nvec_io_timeout: + debug("NVEC events: io failed %d\n", res); + return 0; + + case nvec_io_write_ok: + default: + debug("NVEC events: unexpected io result %d\n", res); + return 0; + } + } + + return 0; +} diff --git a/drivers/i2c/tegra_nvec_keyboard.c b/drivers/i2c/tegra_nvec_keyboard.c new file mode 100644 index 0000000..0837f08 --- /dev/null +++ b/drivers/i2c/tegra_nvec_keyboard.c @@ -0,0 +1,108 @@ +/* + * (C) Copyright 2013 + * Andrey Danin + * + * 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 + */ + +#include +#include +#include +#include +#include + + +circbuf_t key_buf = { 0, 0, NULL, NULL, NULL, NULL }; + +/* nvec commands */ +static char enable_kbd[] = { NVEC_KBD, ENABLE_KBD }; +static char reset_kbd[] = { NVEC_PS2, MOUSE_SEND_CMD, MOUSE_RESET, 3 }; +static char clear_leds[] = { NVEC_KBD, SET_LEDS, 0 }; + + +void nvec_push_key(unsigned short code, unsigned short state) +{ + int code_state; + + assert(key_buf.totalsize > 0); + + if (key_buf.size == key_buf.totalsize) + return; + + code_state = ((state << 16) | code); + buf_push(&key_buf, (const char *)&code_state, sizeof(code_state)); +} + + +int nvec_have_keys(void) +{ + return key_buf.size > 0; +} + + +int nvec_pop_key(void) +{ + int code_state; + int len = buf_pop(&key_buf, (char *)&code_state, sizeof(code_state)); + + if (len < sizeof(code_state)) + return -1; + + return code_state; +} + + +void nvec_process_keyboard_msg(const unsigned char *msg) +{ + int code, state; + int event_type; + int _size; + + event_type = nvec_msg_event_type(msg); + if (event_type != NVEC_KEYBOARD) + return; + + _size = (msg[0] & (3 << 5)) >> 5; + + if (_size == NVEC_VAR_SIZE) + return; + + if (_size == NVEC_3BYTES) + msg++; + + code = msg[1] & 0x7f; + state = msg[1] & 0x80; + + nvec_push_key(code_tabs[_size][code], state); +} + + +void nvec_enable_kbd_events(void) +{ + buf_init(&key_buf, NVEC_KEYS_QUEUE_SIZE * sizeof(int)); + + if (nvec_do_request(reset_kbd, 4)) + error("NVEC: failed to reset keyboard\n"); + if (nvec_do_request(clear_leds, 3)) + error("NVEC: failed to clear leds\n"); + if (nvec_do_request(enable_kbd, 2)) + error("NVEC: failed to enable keyboard\n"); + + debug("NVEC: keyboard initialization finished\n"); +} diff --git a/include/fdtdec.h b/include/fdtdec.h index cd336fa..8917e34 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -79,6 +79,7 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra20 SDMMC controller */ COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ + COMPAT_NVIDIA_TEGRA20_NVEC, /* Tegra 2 EC controller */ COMPAT_NVIDIA_TEGRA114_SPI, /* Tegra 114 SPI controller */ COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ COMPAT_SAMSUNG_EXYNOS5_SROMC, /* Exynos5 SROMC */ diff --git a/lib/Makefile b/lib/Makefile index 5d58609..b338211 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o COBJS-$(CONFIG_BZIP2) += bzlib_randtable.o COBJS-$(CONFIG_BZIP2) += bzlib_huffman.o COBJS-$(CONFIG_USB_TTY) += circbuf.o +COBJS-$(CONFIG_TEGRA_NVEC) += circbuf.o COBJS-y += crc7.o COBJS-y += crc16.o COBJS-y += display_options.o diff --git a/lib/fdtdec.c b/lib/fdtdec.c index e322b82..4c70c79 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -52,6 +52,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"), COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"), + COMPAT(NVIDIA_TEGRA20_NVEC, "nvidia,tegra20-nvec"), COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"), COMPAT(SMSC_LAN9215, "smsc,lan9215"), COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"),