Patchwork [U-Boot,1/3] ARM: tegra: add nvec driver

login
register
mail settings
Submitter Andrey Danin
Date July 19, 2013, 8:47 a.m.
Message ID <1374223663-8576-2-git-send-email-danindrey@mail.ru>
Download mbox | patch
Permalink /patch/260240/
State Changes Requested
Delegated to: Tom Warren
Headers show

Comments

Andrey Danin - July 19, 2013, 8:47 a.m.
Adopted version of nvec driver from linux kernel with
keyboard support only.

Signed-off-by: Andrey Danin <danindrey@mail.ru>
---
 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

Patch

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 <andreydanin@mail.ru>
+ *
+ * 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 <andreydanin@mail.ru>
+ *
+ * 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 <andreydanin@mail.ru>
+ *
+ * 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 <linux/input.h>
+
+
+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 <asm/arch-tegra/tegra_mmc.h>
 #include <asm/arch-tegra/mmc.h>
 #endif
+#ifdef CONFIG_TEGRA_NVEC
+#include <asm/arch-tegra/tegra_nvec.h>
+#endif
 #include <i2c.h>
 #include <spi.h>
 #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 <andreydanin@mail.ru>
+ *
+ * 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 <common.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/funcmux.h>
+#include <asm/arch-tegra/tegra_nvec.h>
+#include <asm/arch-tegra/tegra_nvec_keyboard.h>
+
+#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 <andreydanin@mail.ru>
+ *
+ * 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 <common.h>
+#include <circbuf.h>
+#include <asm/arch-tegra/tegra_nvec_keyboard.h>
+#include <asm/arch-tegra/tegra_nvec_keytable.h>
+#include <asm/arch-tegra/tegra_nvec.h>
+
+
+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"),