Andrey, > -----Original Message----- > From: Andrey Danin [mailto:danind...@mail.ru] > Sent: Friday, July 19, 2013 1:48 AM > To: Tom Warren; u-boot@lists.denx.de > Cc: Stephen Warren; Marc Dietrich; Julian Andres Klode; > ac...@lists.launchpad.net; Andrey Danin > Subject: [PATCH 1/3] ARM: tegra: add nvec driver > > Adopted version of nvec driver from linux kernel with keyboard support > only. > > Signed-off-by: Andrey Danin <danind...@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 +++++++++++++
I think some of these header files could be reduced / combined - do we really need this many header files in common arch-tegra for NVEC kbd support? > board/nvidia/common/board.c | 12 + > drivers/i2c/Makefile | 1 + > drivers/i2c/tegra_nvec.c | 462 > ++++++++++++++++++++ > drivers/i2c/tegra_nvec_keyboard.c | 108 +++++ Shouldn't all input drivers (keyboard, etc.) go in driver/input/? > 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 <andreyda...@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 <andreyda...@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); For instance, combine this file with tegra_nvec.h and tegra_nvec_keyboard.h into one file, tegra_nvec.h. And all protos should have their arguments spelled out in a header, as you did for nvec_do_request above. > + > + > +#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 <andreyda...@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 <andreyda...@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 <andreyda...@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"), > -- > 1.7.1 Tom -- nvpublic _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot