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

Tom Warren TWarren at nvidia.com
Fri Jul 19 18:28:25 CEST 2013


Andrey,

> -----Original Message-----
> From: Andrey Danin [mailto:danindrey at mail.ru]
> Sent: Friday, July 19, 2013 1:48 AM
> To: Tom Warren; u-boot at lists.denx.de
> Cc: Stephen Warren; Marc Dietrich; Julian Andres Klode;
> ac100 at 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 <danindrey at 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 <andreydanin at 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 at 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 <andreydanin at 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 at 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 at 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



More information about the U-Boot mailing list