Signed-off-by: Andrey Danin <danind...@mail.ru>
CC: Stephen Warren <swar...@nvidia.com>
CC: Marc Dietrich <marvi...@gmx.de>
CC: Julian Andres Klode <j...@jak-linux.org>
CC: ac...@lists.launchpad.net
---
 Changes for v2:
        - NVEC driver was reworked to use tegra-i2c

 arch/arm/include/asm/arch-tegra/tegra_nvec.h |  130 ++++++++++++
 board/nvidia/common/board.c                  |   12 ++
 drivers/i2c/Makefile                         |    1 +
 drivers/i2c/tegra_nvec.c                     |  294 ++++++++++++++++++++++++++
 include/fdtdec.h                             |    1 +
 lib/fdtdec.c                                 |    1 +
 6 files changed, 439 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec.h
 create mode 100644 drivers/i2c/tegra_nvec.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..f1cec0d
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra/tegra_nvec.h
@@ -0,0 +1,130 @@
+/*
+ * (C) Copyright 2014
+ * Andrey Danin <danind...@mail.ru>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#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,
+       NVEC_LAST_MSG,
+};
+
+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,
+};
+
+enum ps2_subcmds {
+       MOUSE_SEND_CMD = 0x01
+};
+
+#define MOUSE_RESET 0xff
+
+typedef int (*periph_start)(void);
+typedef int (*periph_process_msg)(const unsigned char *);
+
+struct nvec_periph {
+       periph_start            start;
+       periph_process_msg      process_msg;
+};
+
+
+int board_nvec_init(void);
+
+/**
+ * Read all available events.
+ *
+ * @return count of available events if ok, -1 on error
+ */
+int nvec_read_events(void);
+
+int nvec_msg_is_event(const unsigned char *msg);
+int nvec_msg_event_type(const unsigned char *msg);
+
+/**
+ * Register perepherial device driver.
+ *
+ * @param msg_type     type of messages that divece processes.
+ * @param periph       pointer to device description.
+ *
+ * @return 0 if ok, -1 on error
+ */
+int nvec_register_periph(int msg_type, struct nvec_periph *periph);
+
+/**
+ * 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/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 3b18e28..6d3055c 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -38,6 +38,9 @@
 #include <asm/arch-tegra/tegra_mmc.h>
 #include <asm/arch-tegra/mmc.h>
 #endif
+#ifdef CONFIG_SYS_I2C_TEGRA_NVEC
+#include <asm/arch-tegra/tegra_nvec.h>
+#endif
 #include <i2c.h>
 #include <spi.h>
 #include "emc.h"
@@ -194,10 +197,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_SYS_I2C_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 fa3a875..3041191 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -27,4 +27,5 @@ obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o
 obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
 obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
 obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
+obj-$(CONFIG_SYS_I2C_TEGRA_NVEC) += tegra_nvec.o
 obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o
diff --git a/drivers/i2c/tegra_nvec.c b/drivers/i2c/tegra_nvec.c
new file mode 100644
index 0000000..b568988
--- /dev/null
+++ b/drivers/i2c/tegra_nvec.c
@@ -0,0 +1,294 @@
+/*
+ * (C) Copyright 2014
+ * Andrey Danin <danind...@mail.ru>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <i2c.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>
+
+#ifndef CONFIG_SYS_I2C_TEGRA_NVEC
+#error "You should enable CONFIG_SYS_I2C_TEGRA_NVEC"
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct nvec_periph devices[NVEC_LAST_MSG];
+
+
+struct nvec_t {
+       int                     i2c_bus;
+       struct fdt_gpio_state   gpio;
+} nvec_data;
+
+
+/* nvec commands */
+char noop[] = { NVEC_CNTL, CNTL_NOOP };
+
+void nvec_signal_request(void)
+{
+       gpio_set_value(nvec_data.gpio.gpio, 0);
+}
+
+
+void nvec_clear_request(void)
+{
+       gpio_set_value(nvec_data.gpio.gpio, 1);
+}
+
+
+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.
+ *
+ * @param nvec nvec state struct
+ */
+void nvec_process_msg(struct nvec_t *nvec, struct i2c_transaction *trans)
+{
+       (void) nvec;
+
+       const unsigned char *msg = (const unsigned char *)&trans->rx_buf[1];
+       int event_type;
+
+       if (!nvec_msg_is_event(msg))
+               return;
+
+       event_type = nvec_msg_event_type(msg);
+
+       if (event_type < NVEC_KEYBOARD || event_type >= NVEC_LAST_MSG)
+               return;
+
+       if (devices[event_type].process_msg)
+               devices[event_type].process_msg(msg);
+}
+
+
+/**
+ * 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)
+{
+       static unsigned int prev_timer;
+       unsigned int poll_start_ms = get_timer(prev_timer);
+       struct i2c_transaction trans;
+       int res;
+
+       if (poll_start_ms > 30000) {
+               if (prev_timer != 0)
+                       nvec_do_request(noop, sizeof(noop));
+               prev_timer = get_timer(0);
+       }
+
+       memset(&trans, 0, sizeof(trans));
+       trans.start_timeout = 1;
+       trans.timeout = 6000;
+
+       res = i2c_slave_io(&trans);
+       if (res == 0) {
+               nvec_process_msg(nvec, &trans);
+               return 0;
+       }
+
+       if (res != -1)
+               debug("Error: i2c slave io failed with code %d\n", res);
+
+       return -1;
+}
+
+/**
+ * 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;
+       struct i2c_transaction trans;
+       int i;
+
+       nvec_signal_request();
+
+       /* Request */
+       for (i = 0; i < 10; ++i) {
+               memset(&trans, 0, sizeof(trans));
+               trans.start_timeout = 600;
+               trans.timeout = 6000;
+
+               trans.tx_buf[0] = (char)size;
+               memcpy(&trans.tx_buf[1], buf, size);
+               trans.tx_size = size + 1;
+
+               res = i2c_slave_io(&trans);
+               if (res == 0) {
+                       if (trans.tx_pos == trans.tx_size)
+                               break;
+
+                       debug("Request was not sent completely");
+               } else if (res != -1) {
+                       debug("Unknown error while slave io");
+               }
+       }
+       nvec_clear_request();
+       if (res != 0) {
+               error("nvec failed to perform request\n");
+               return -1;
+       }
+
+       /* Response */
+       for (i = 0; i < 10; ++i) {
+               memset(&trans, 0, sizeof(trans));
+               trans.start_timeout = 600;
+               trans.timeout = 6000;
+
+               res = i2c_slave_io(&trans);
+               if (res == 0)
+                       break;
+       }
+       if (res != 0) {
+               error("nvec failed to read response\n");
+               return -1;
+       }
+
+       /* TODO Parse response */
+
+       return 0;
+}
+
+
+/**
+ * Decode the nvec information from the fdt.
+ *
+ * @param blob         fdt blob
+ * @param nvec         nvec device sturct
+ * @return 0 if ok, -ve on error
+ */
+static int nvec_decode_config(const void *blob,
+                             struct nvec_t *nvec)
+{
+       int node, parent;
+       int i2c_bus;
+
+       node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_NVEC);
+       if (node < 0) {
+               error("Cannot find NVEC node in fdt\n");
+               return node;
+       }
+
+       parent = fdt_parent_offset(blob, node);
+       if (parent < 0) {
+               debug("%s: Cannot find node parent\n", __func__);
+               return -1;
+       }
+
+       i2c_bus = i2c_get_bus_num_fdt(parent);
+       if (i2c_bus < 0)
+               return -1;
+       nvec->i2c_bus = i2c_bus;
+
+       if (fdtdec_decode_gpio(blob, node, "request-gpios",
+                              &nvec->gpio)) {
+               error("No NVEC request gpio\n");
+               return -1;
+       }
+
+       debug("NVEC: i2c:%d, gpio:%s(%u)\n",
+             nvec->i2c_bus, nvec->gpio.name, nvec->gpio.gpio);
+       return 0;
+}
+
+
+int board_nvec_init(void)
+{
+       int res = 0;
+       int i;
+
+       if (nvec_decode_config(gd->fdt_blob, &nvec_data)) {
+               error("Can't parse NVEC node in device tree\n");
+               return -1;
+       }
+
+       debug("NVEC initialization...\n");
+
+       res = gpio_request(nvec_data.gpio.gpio, NULL);
+       if (res != 0)
+               error("NVEC: err, gpio_request\n");
+       res = gpio_direction_output(nvec_data.gpio.gpio, 1);
+       if (res != 0)
+               error("NVEC: err, gpio_direction\n");
+       res = gpio_set_value(nvec_data.gpio.gpio, 1);
+       if (res != 0)
+               error("NVEC: err, gpio_set_value\n");
+       udelay(100);
+
+       i2c_set_bus_num(nvec_data.i2c_bus);
+
+       for (i = NVEC_KEYBOARD; i < NVEC_LAST_MSG; ++i)
+               if (devices[i].start) {
+                       debug("Starting device %d(0x%x)\n", i, i);
+                       devices[i].start();
+               }
+
+       return 1;
+}
+
+
+int nvec_read_events(void)
+{
+       int res;
+       int cnt = 0;
+
+       while (++cnt <= 8) {
+               res = nvec_do_io(&nvec_data);
+               if (res)
+                       break;
+
+               /* TODO Process nvec communication errors */
+       }
+
+       return cnt;
+}
+
+
+int nvec_register_periph(int msg_type, struct nvec_periph *periph)
+{
+       if (devices[msg_type].start || devices[msg_type].process_msg) {
+               error("Device for msg %d already registered\n", msg_type);
+               return -1;
+       }
+
+       devices[msg_type] = *periph;
+       return 0;
+}
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 19bab79..7b84335 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -64,6 +64,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/fdtdec.c b/lib/fdtdec.c
index 1fecab3..82df701 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -37,6 +37,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.9.5

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to